Revive ptest and add result to buildhistory
v2 cover Revive ptest and add result to buildhistory
* V2:
- Fix Paul's comments
- Fix RP's comments
* V1:
- Initial version
// Robert
The following changes since commit 5c9ef0734d23909b5694ed43cdbb205c2ba9ca95:
devtool/copy_buildsystem: adds meta-skeleton layer in the eSDK installation. (2017-08-19 22:15:25 +0100)
are available in the git repository at:
git://git.openembedded.org/openembedded-core-contrib rbt/ptest
http://cgit.openembedded.org/openembedded-core-contrib/log/?h=rbt/ptest
Robert Yang (9):
runtime/cases/_ptest.py: revive it oeqa/utils/logparser.py: add skip status runtime/cases/_ptest.py: add skip status runtime/cases/_ptest.py: rename it to ptest.py utils/logparser.py: fix section check core/target/ssh.py: replace decode errors buildhistory.bbclass: print message when no commit testimage.bbclass: update comments buildhistory.bbclass: add ptest
meta/classes/buildhistory.bbclass | 32 +++++++++++ meta/classes/testimage.bbclass | 4 +- meta/lib/oeqa/core/target/ssh.py | 4 +- meta/lib/oeqa/runtime/cases/_ptest.py | 103 ---------------------------------- meta/lib/oeqa/runtime/cases/ptest.py | 82 +++++++++++++++++++++++++++ meta/lib/oeqa/utils/logparser.py | 7 ++- 6 files changed, 122 insertions(+), 110 deletions(-) delete mode 100644 meta/lib/oeqa/runtime/cases/_ptest.py create mode 100644 meta/lib/oeqa/runtime/cases/ptest.py
Signature · 1 line
2.10.2
PATCH v2 1/9 runtime/cases/_ptest.py: revive it
* Make it work with current oeqa
* Skip the test if ptest is not in DISTRO_FEATURES
* Skip the test if ptest-pkgs is not in IMAGE_FEATURES
* The logs are saved to:
testimage/ptest_log -> testimage/ptest_log.<datetime>
* This provides data that could be used to detect regressions in ptest results
[YOCTO #11547]
--- meta/lib/oeqa/runtime/cases/_ptest.py | 96 +++++++++++++---------------------- 1 file changed, 35 insertions(+), 61 deletions(-)
meta/lib/oeqa/runtime/cases/_ptest.py +35 -61
@@ -1,28 +1,10 @@ -import os -import shutil -import subprocess - from oeqa.runtime.case import OERuntimeTestCase from oeqa.core.decorator.depends import OETestDepends from oeqa.core.decorator.oeid import OETestID -from oeqa.core.decorator.data import skipIfNotDataVar, skipIfNotFeature -from oeqa.runtime.decorator.package import OEHasPackage - -from oeqa.runtime.cases.dnf import DnfTest -from oeqa.utils.logparser import * -from oeqa.utils.httpserver import HTTPService - -class PtestRunnerTest(DnfTest): +from oeqa.core.decorator.data import skipIfNotFeature +from oeqa.utils.logparser import Lparser, Result - @classmethod - def setUpClass(cls): - rpm_deploy = os.path.join(cls.tc.td['DEPLOY_DIR'], 'rpm') - cls.repo_server = HTTPService(rpm_deploy, cls.tc.target.server_ip) - cls.repo_server.start() - - @classmethod - def tearDownClass(cls): - cls.repo_server.stop() +class PtestRunnerTest(OERuntimeTestCase): # a ptest log parser def parse_ptest(self, logfile):
@@ -59,45 +41,37 @@ class PtestRunnerTest(DnfTest): result.sort_tests() return result - def _install_ptest_packages(self): - # Get ptest packages that can be installed in the image. - packages_dir = os.path.join(self.tc.td['DEPLOY_DIR'], 'rpm') - ptest_pkgs = [pkg[:pkg.find('-ptest')+6] - for _, _, filenames in os.walk(packages_dir) - for pkg in filenames - if 'ptest' in pkg - and pkg[:pkg.find('-ptest')] in self.tc.image_packages] - - repo_url = 'http://%s:%s' % (self.target.server_ip, - self.repo_server.port) - dnf_options = ('--repofrompath=oe-ptest-repo,%s ' - '--nogpgcheck ' - 'install -y' % repo_url) - self.dnf('%s %s ptest-runner' % (dnf_options, ' '.join(ptest_pkgs))) - - @skipIfNotFeature('package-management', - 'Test requires package-management to be in DISTRO_FEATURES') - @skipIfNotFeature('ptest', - 'Test requires package-management to be in DISTRO_FEATURES') - @skipIfNotDataVar('IMAGE_PKGTYPE', 'rpm', - 'RPM is not the primary package manager') - @OEHasPackage(['dnf']) + @OETestID(1600) + @skipIfNotFeature('ptest', 'Test requires ptest to be in DISTRO_FEATURES') + @skipIfNotFeature('ptest-pkgs', 'Test requires ptest-pkgs to be in IMAGE_FEATURES') @OETestDepends(['ssh.SSHTest.test_ssh']) def test_ptestrunner(self): - self.ptest_log = os.path.join(self.tc.td['TEST_LOG_DIR'], - 'ptest-%s.log' % self.tc.td['DATETIME']) - self._install_ptest_packages() - - (runnerstatus, result) = self.target.run('/usr/bin/ptest-runner > /tmp/ptest.log 2>&1', 0) - #exit code is !=0 even if ptest-runner executes because some ptest tests fail. - self.assertTrue(runnerstatus != 127, msg="Cannot execute ptest-runner!") - self.target.copyFrom('/tmp/ptest.log', self.ptest_log) - shutil.copyfile(self.ptest_log, "ptest.log") - - result = self.parse_ptest("ptest.log") - log_results_to_location = "./results" - if os.path.exists(log_results_to_location): - shutil.rmtree(log_results_to_location) - os.makedirs(log_results_to_location) - - result.log_as_files(log_results_to_location, test_status = ['pass','fail']) + import datetime + + test_log_dir = self.td.get('TEST_LOG_DIR', '') + # The TEST_LOG_DIR maybe NULL when testimage is added after + # testdata.json is generated. + if not test_log_dir: + test_log_dir = os.path.join(self.td.get('WORKDIR', ''), 'testimage') + # Don't use self.td.get('DATETIME'), it's from testdata.json, not + # up-to-date, and may cause "File exists" when re-reun. + datetime = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + ptest_log_dir_link = os.path.join(test_log_dir, 'ptest_log') + ptest_log_dir = '%s.%s' % (ptest_log_dir_link, datetime) + ptest_runner_log = os.path.join(ptest_log_dir, 'ptest-runner.log') + + status, output = self.target.run('ptest-runner', 0) + os.makedirs(ptest_log_dir) + with open(ptest_runner_log, 'w') as f: + f.write(output) + + # status != 0 is OK since some ptest tests may fail + self.assertTrue(status != 127, msg="Cannot execute ptest-runner!") + + # Parse and save results + parse_result = self.parse_ptest(ptest_runner_log) + parse_result.log_as_files(ptest_log_dir, test_status = ['pass','fail']) + if os.path.exists(ptest_log_dir_link): + # Remove the old link to create a new one + os.remove(ptest_log_dir_link) + os.symlink(os.path.basename(ptest_log_dir), ptest_log_dir_link)
Signature · 1 line
2.10.2
PATCH v2 2/9 oeqa/utils/logparser.py: add skip status
In reply to Robert YangSome test cases maybe skipped, let's parse it.
[YOCTO #11547]
--- meta/lib/oeqa/utils/logparser.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
meta/lib/oeqa/utils/logparser.py +3 -2
@@ -9,7 +9,7 @@ from . import ftools # A parser that can be used to identify weather a line is a test result or a section statement. class Lparser(object): - def __init__(self, test_0_pass_regex, test_0_fail_regex, section_0_begin_regex=None, section_0_end_regex=None, **kwargs): + def __init__(self, test_0_pass_regex, test_0_fail_regex, test_0_skip_regex, section_0_begin_regex=None, section_0_end_regex=None, **kwargs): # Initialize the arguments dictionary if kwargs: self.args = kwargs
@@ -19,12 +19,13 @@ class Lparser(object): # Add the default args to the dictionary self.args['test_0_pass_regex'] = test_0_pass_regex self.args['test_0_fail_regex'] = test_0_fail_regex + self.args['test_0_skip_regex'] = test_0_skip_regex if section_0_begin_regex: self.args['section_0_begin_regex'] = section_0_begin_regex if section_0_end_regex: self.args['section_0_end_regex'] = section_0_end_regex - self.test_possible_status = ['pass', 'fail', 'error'] + self.test_possible_status = ['pass', 'fail', 'error', 'skip'] self.section_possible_status = ['begin', 'end'] self.initialized = False
Signature · 1 line
2.10.2
PATCH v2 3/9 runtime/cases/_ptest.py: add skip status
In reply to Robert YangThe packages' test cases maybe skipped, check and save them.
[YOCTO #11547]
--- meta/lib/oeqa/runtime/cases/_ptest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
meta/lib/oeqa/runtime/cases/_ptest.py +6 -1
@@ -10,6 +10,7 @@ class PtestRunnerTest(OERuntimeTestCase): def parse_ptest(self, logfile): parser = Lparser(test_0_pass_regex="^PASS:(.+)", test_0_fail_regex="^FAIL:(.+)", + test_0_skip_regex="^SKIP:(.+)", section_0_begin_regex="^BEGIN: .*/(.+)/ptest", section_0_end_regex="^END: .*/(.+)/ptest") parser.init()
@@ -38,6 +39,10 @@ class PtestRunnerTest(OERuntimeTestCase): result.store(current_section, name, status) continue + if line_type == 'test' and status == 'skip': + result.store(current_section, name, status) + continue + result.sort_tests() return result
@@ -70,7 +75,7 @@ class PtestRunnerTest(OERuntimeTestCase): # Parse and save results parse_result = self.parse_ptest(ptest_runner_log) - parse_result.log_as_files(ptest_log_dir, test_status = ['pass','fail']) + parse_result.log_as_files(ptest_log_dir, test_status = ['pass','fail', 'skip']) if os.path.exists(ptest_log_dir_link): # Remove the old link to create a new one os.remove(ptest_log_dir_link)
Signature · 1 line
2.10.2
PATCH v2 4/9 runtime/cases/_ptest.py: rename it to ptest.py
In reply to Robert YangIt works now.
[YOCTO #11547]
---
meta/lib/oeqa/runtime/cases/{_ptest.py => ptest.py} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename meta/lib/oeqa/runtime/cases/{_ptest.py => ptest.py} (100%)meta/lib/oeqa/runtime/cases/ptest.py renamed from meta/lib/oeqa/runtime/cases/_ptest.py +0 -0
Signature · 1 line
2.10.2
PATCH v2 5/9 utils/logparser.py: fix section check
In reply to Robert YangThe section might be None or '', so use "if section" for it.
Fixed:
File "/buildarea/lyang1/poky/meta/lib/oeqa/utils/logparser.py", line 113, in log_as_files
prefix += section
TypeError: Can't convert 'NoneType' object to str implicitly
[YOCTO #11547]
--- meta/lib/oeqa/utils/logparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
meta/lib/oeqa/utils/logparser.py +1 -1
@@ -109,7 +109,7 @@ class Result(object): prefix = '' for x in test_status: prefix +=x+'.' - if (section != ''): + if section: prefix += section section_file = os.path.join(target_dir, prefix) # purge the file contents if it exists
Signature · 1 line
2.10.2
PATCH v2 6/9 core/target/ssh.py: replace decode errors
In reply to Robert YangThere might be wild strings when read from target (especially when reading ptest results), replace the errors to avoid breaking the test.
Fixed: (Not always happen)
$ bitbake core-image-sato -ctestimage
[snip]
status, output = self.target.run('ptest-runner', 0)
File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 84, in run
status, output = self._run(sshCmd, processTimeout, True)
File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 55, in _run
status, output = SSHCall(command, self.logger, timeout)
File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 258, in SSHCall
run()
File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 236, in run
output = process.communicate()[0].decode("utf-8")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 4906: invalid continuation byte[YOCTO #11547]
--- meta/lib/oeqa/core/target/ssh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
meta/lib/oeqa/core/target/ssh.py +2 -2
@@ -211,7 +211,7 @@ def SSHCall(command, logger, timeout=None, **opts): process.stdout.close() eof = True else: - data = data.decode("utf-8") + data = data.decode("utf-8", errors='replace') output += data logger.debug('Partial data from SSH call: %s' % data) endtime = time.time() + timeout
@@ -233,7 +233,7 @@ def SSHCall(command, logger, timeout=None, **opts): output += lastline else: - output = process.communicate()[0].decode("utf-8") + output = process.communicate()[0].decode("utf-8", errors='replace') logger.debug('Data from SSH call: %s' % output.rstrip()) options = {
Signature · 1 line
2.10.2
Re: [PATCH V2 6/9] core/target/ssh.py: replace decode errors
Quoted 29 lines · NZST Robert Yang
There might be wild strings when read from target (especially when reading ptest results), replace the errors to avoid breaking the test.
Fixed: (Not always happen)
$ bitbake core-image-sato -ctestimage [snip] status, output = self.target.run('ptest-runner', 0) File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 84, in run status, output = self._run(sshCmd, processTimeout, True) File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 55, in _run status, output = SSHCall(command, self.logger, timeout) File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 258, in SSHCall run() File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 236, in run output = process.communicate()[0].decode("utf-8") UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 4906: invalid continuation byte[YOCTO #11547]
Signed-off-by Robert Yang <[email protected]>--- meta/lib/oeqa/core/target/ssh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)meta/lib/oeqa/core/target/ssh.py +0
-0
meta/lib/oeqa/core/target/ssh.py def SSHCall(command, logger, timeout=None, **opts): · line 216
Quoted 7 lines
meta/lib/oeqa/core/target/ssh.py +1
-1@@ -211,7 +211,7 @@ def SSHCall(command, logger, timeout=None, **opts): process.stdout.close() eof = True else: - data = data.decode("utf-8") + data = data.decode("utf-8", errors='replace') output += data logger.debug('Partial data from SSH call: %s' % data)
Since we're dealing with partial data here, shouldn't we be using a reader object? e.g.:
http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?id=bfd8c35c3f917e3806c8dfe36c98c70fbccbb3c9
Cheers,
Paul
Signature · 2 lines
Paul Eggleton Intel Open Source Technology Centre
Re: [PATCH V2 6/9] core/target/ssh.py: replace decode errors
Quoted 51 lines · Paul Eggleton
Quoted 39 lines · NZST Robert Yang
There might be wild strings when read from target (especially when reading ptest results), replace the errors to avoid breaking the test.
Fixed: (Not always happen)
$ bitbake core-image-sato -ctestimage [snip] status, output = self.target.run('ptest-runner', 0) File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 84, in run status, output = self._run(sshCmd, processTimeout, True) File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 55, in _run status, output = SSHCall(command, self.logger, timeout) File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 258, in SSHCall run() File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 236, in run output = process.communicate()[0].decode("utf-8") UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 4906: invalid continuation byte[YOCTO #11547]
Signed-off-by Robert Yang <[email protected]>---
meta/lib/oeqa/core/target/ssh.py | 4 ++--1 file changed, 2 insertions(+), 2 deletions(-)meta/lib/oeqa/core/target/ssh.py +0
-0@@ -211,7 +211,7 @@ def SSHCall(command, logger, timeout=None, **opts): process.stdout.close()Quoted 8 lines
eof = True
else:
- data =data.decode("utf-8")+ data =data.decode("utf-8", errors='replace')
output += data logger.debug('Partial data from SSH
call: %s' % data)Since we're dealing with partial data here, shouldn't we be using a reader object? e.g.:
Quoted 2 lines
http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?id=bfd8c35c3
f917e3806c8dfe36c98c70fbccbb3c9
I've decided to merge this series as it improves the ptest support substantially however I am expecting a follow up with the tweaks Paul suggests here please.
Cheers,
Richard
Re: [PATCH V2 6/9] core/target/ssh.py: replace decode errors
Quoted 52 lines · Richard Purdie
Quoted 51 lines · Paul Eggleton
Quoted 45 lines · NZST Robert Yang
There might be wild strings when read from target (especially when reading ptest results), replace the errors to avoid breaking the test.
Fixed: (Not always happen)
$ bitbake core-image-sato -ctestimage [snip] status, output = self.target.run('ptest-runner', 0) File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 84, in run status, output = self._run(sshCmd, processTimeout, True) File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 55, in _run status, output = SSHCall(command, self.logger, timeout) File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 258, in SSHCall run() File "/buildarea/lyang1/poky/meta/lib/oeqa/core/target/ssh.py", line 236, in run output = process.communicate()[0].decode("utf-8") UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 4906: invalid continuation byte[YOCTO #11547]
Signed-off-by Robert Yang <[email protected]>--- meta/lib/oeqa/core/target/ssh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)meta/lib/oeqa/core/target/ssh.py +1
-1@@ -211,7 +211,7 @@ def SSHCall(command, logger, timeout=None, **opts): process.stdout.close() eof = True else: - data = data.decode("utf-8") + data = data.decode("utf-8", errors='replace') output += dataQuoted 2 lines
logger.debug('Partial data from SSH
call: %s' % data)Since we're dealing with partial data here, shouldn't we be using a reader object? e.g.:
Quoted 7 lines
Quoted 2 lines
http://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?id=bfd8c35c3
f917e3806c8dfe36c98c70fbccbb3c9I've decided to merge this series as it improves the ptest support substantially however I am expecting a follow up with the tweaks Paul suggests here please.
Thanks, sent:
[OE-core] [PATCH 0/1] core/target/ssh.py: use reader to handle partial data
// Robert
Quoted 6 lines
Cheers,
Richard
PATCH v2 7/9 buildhistory.bbclass: print message when no commit
In reply to Robert YangThis makes the user easier to know how to make commit in buildhistory.
[YOCTO #11547]
--- meta/classes/buildhistory.bbclass | 2 ++ 1 file changed, 2 insertions(+)
meta/classes/buildhistory.bbclass +2 -0
@@ -824,6 +824,8 @@ python buildhistory_eventhandler() { interrupted = getattr(e, '_interrupted', 0) localdata.setVar('BUILDHISTORY_BUILD_INTERRUPTED', str(interrupted)) bb.build.exec_func("buildhistory_commit", localdata) + else: + bb.note("No commit since BUILDHISTORY_COMMIT != '1'") } addhandler buildhistory_eventhandler
Signature · 1 line
2.10.2
PATCH v2 8/9 testimage.bbclass: update comments
In reply to Robert YangIt's very important to add IMAGE_CLASSES += "testimage" in local.conf firstly, otherwise the var like TEST_LOG_DIR (defined in testimage.bbclass) will not be in testdata.json.
[YOCTO #11547]
--- meta/classes/testimage.bbclass | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
meta/classes/testimage.bbclass +2 -2
@@ -7,8 +7,8 @@ # Most of the tests are commands run on target image over ssh. # To use it add testimage to global inherit and call your target image with -c testimage # You can try it out like this: -# - first build a qemu core-image-sato -# - add IMAGE_CLASSES += "testimage" in local.conf +# - first add IMAGE_CLASSES += "testimage" in local.conf +# - build a qemu core-image-sato # - then bitbake core-image-sato -c testimage. That will run a standard suite of tests. # You can set (or append to) TEST_SUITES in local.conf to select the tests
Signature · 1 line
2.10.2
PATCH v2 9/9 buildhistory.bbclass: add ptest
In reply to Robert YangThe ptest log will be saved to buildhistory/ptest, we can easily get the regression result between builds by:
$ git show HEAD ptest/pass.fail.skip.*
[YOCTO #11547]
--- meta/classes/buildhistory.bbclass | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
meta/classes/buildhistory.bbclass +30 -0
@@ -912,3 +912,33 @@ def write_latest_srcrev(d, pkghistdir): else: if os.path.exists(srcrevfile): os.remove(srcrevfile) + +do_testimage[postfuncs] += "write_ptest_result" +do_testimage[vardepsexclude] += "write_ptest_result" + +python write_ptest_result() { + write_latest_ptest_result(d, d.getVar('BUILDHISTORY_DIR')) +} + +def write_latest_ptest_result(d, histdir): + import glob + import subprocess + test_log_dir = d.getVar('TEST_LOG_DIR') + input_ptest = os.path.join(test_log_dir, 'ptest_log') + output_ptest = os.path.join(histdir, 'ptest') + if os.path.exists(input_ptest): + try: + # Lock it avoid race issue + lock = bb.utils.lockfile(output_ptest + "/ptest.lock") + bb.utils.mkdirhier(output_ptest) + oe.path.copytree(input_ptest, output_ptest) + # Sort test result + for result in glob.glob('%s/pass.fail.*' % output_ptest): + bb.debug(1, 'Processing %s' % result) + cmd = ['sort', result, '-o', result] + bb.debug(1, 'Running %s' % cmd) + ret = subprocess.call(cmd) + if ret != 0: + bb.error('Failed to run %s!' % cmd) + finally: + bb.utils.unlockfile(lock)
Signature · 1 line
2.10.2