Sumo-next patches for review
cover Sumo-next patches for review
The Bulk of the changres are for QA automation related support and some misc backports.
Passed "A-Full" build
The following changes since commit a4c7d286883db186f17d4044a6531eac88473403:
bitbake: bitbake-user-manual: Added "usehead" parameter. (2018-12-10 20:41:28 +0000)
are available in the git repository at:
git://git.yoctoproject.org/poky-contrib stable/sumo-next
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=stable/sumo-next
Michael Halstead (1):
scripts/runqemu: Replace subprocess.run() for compatibilty
Richard Purdie (34):
scripts/runqemu: Tidy up lock handling code scripts/runqemu: Improve lockfile handling for python with
close_fd=True
oeqa/selftest/signing: Skip tests if gpg isn't found oeqa/selftest/signing: Allow tests not to need gpg on the host oeqa/selftest/signing: Use do_populate_lic target instead of
do_package
oeqa/selftest/case: Use bb.utils.remove() instead of shutil.remove() oeqa/selftest/buildoptions: Improve ccache test failure output oeqa/utils/commands: Add extra qemu failure logging oeqa/utils/qemurunner: Fix python ResourceWarning for unclosed file oeqa/utils/commands: Avoid log message duplication oeqa/qemurunner: Remove resource python warnings oeqa/selftest/buildoptions: Improve ccache test oeqa/selftest/buildoptions: Ensure diskmon tests run consistently oeqa/selftest/runqemu: Improve testcase failure handling oeqa/utils/qemurunner: Avoid tracebacks on closed files oeqa/loader: Fix deprecation warning
oeqa/utils/commands: Avoid unclosed file warnings oeqa/selftest/context: Replace deprecated imp module usage oeqa/utils/qemurunner.py: Fix python regex warnings oeqa/selftest/context: Improve log file handling oeqa/core/runner: Improve test case comparision oeqa/runner: Ensure we don't print misleading results output oeqa/core/threaded: Remove in favour of using concurrenttests oeqa/runner: Simplify code
oeqa: Remove xmlrunner
oeqa/runtime/ptest: Inject results+logs into stored json results file oeqa/runner: Always show a summary of success/fail/error/skip counts oeqa/runner: Sort the test result output by result class oeqa/selftest: Improvements to the json logging oeqa/utils/metadata: Allow to function without the git module image-buildinfo,oeqa/selftest/containerimage: Ensure image-buildinfo
doesn't break tests
oeqa/selftest/esdk: Ensure parent directory exists oeqa/selftest/esdk: Fix typo causing test failure
busybox: Put klogd/syslogd alternative links in syslog package
Ross Burton (3):
oeqa/oelib/path: don't leak temporary directories
oeqa: don't litter /tmp with temporary directories oeqa/selftest/esdk: run selftest inside workdir not /tmp
Stefan Lendl (1):
default-versions.inc: Make PREFERRED_VERSION_openssl* overwritable
Yeoh Ee Peng (6):
oeqa/core/runner: refactor for OEQA to write json testresult oeqa/core/runner: write testresult to json files oeqa/selftest/context: write testresult to json files testimage.bbclass: write testresult to json files testsdk.bbclass: write testresult to json files oeqa/selftest: Standardize json logging output directory
meta-selftest/recipes-test/delay/delay.bb | 12 + meta/classes/image-buildinfo.bbclass | 2 + meta/classes/testimage.bbclass | 31 ++- meta/classes/testsdk.bbclass | 40 ++- meta/conf/distro/include/default-versions.inc | 6 +- meta/lib/oeqa/core/context.py | 1 - meta/lib/oeqa/core/decorator/depends.py | 9 +- meta/lib/oeqa/core/decorator/oetimeout.py | 40 +-- meta/lib/oeqa/core/loader.py | 2 +- meta/lib/oeqa/core/runner.py | 186 +++++++------- .../core/tests/cases/loader/threaded/threaded.py | 12 - .../tests/cases/loader/threaded/threaded_alone.py | 8 - .../cases/loader/threaded/threaded_depends.py | 10 - .../tests/cases/loader/threaded/threaded_module.py | 12 - meta/lib/oeqa/core/tests/common.py | 10 - meta/lib/oeqa/core/tests/test_data.py | 4 +- meta/lib/oeqa/core/tests/test_decorators.py | 12 - meta/lib/oeqa/core/tests/test_loader.py | 30 +-- meta/lib/oeqa/core/threaded.py | 275 --------------------- meta/lib/oeqa/runtime/cases/ptest.py | 21 +- meta/lib/oeqa/sdk/context.py | 5 +- meta/lib/oeqa/selftest/case.py | 4 +- meta/lib/oeqa/selftest/cases/buildoptions.py | 12 +- meta/lib/oeqa/selftest/cases/containerimage.py | 1 + meta/lib/oeqa/selftest/cases/eSDK.py | 14 +- meta/lib/oeqa/selftest/cases/oelib/path.py | 10 +- meta/lib/oeqa/selftest/cases/runqemu.py | 32 ++- meta/lib/oeqa/selftest/cases/signing.py | 80 ++++-- meta/lib/oeqa/selftest/context.py | 45 +++- meta/lib/oeqa/utils/buildproject.py | 3 +- meta/lib/oeqa/utils/commands.py | 11 +- meta/lib/oeqa/utils/metadata.py | 17 +- meta/lib/oeqa/utils/qemurunner.py | 26 +- meta/lib/oeqa/utils/targetbuild.py | 5 +- meta/recipes-core/busybox/busybox.inc | 5 +- scripts/runqemu | 25 +- 36 files changed, 409 insertions(+), 609 deletions(-) create mode 100644 meta-selftest/recipes-test/delay/delay.bb delete mode 100644 meta/lib/oeqa/core/tests/cases/loader/threaded/threaded.py delete mode 100644 meta/lib/oeqa/core/tests/cases/loader/threaded/threaded_alone.py delete mode 100644 meta/lib/oeqa/core/tests/cases/loader/threaded/threaded_depends.py delete mode 100644 meta/lib/oeqa/core/tests/cases/loader/threaded/threaded_module.py delete mode 100644 meta/lib/oeqa/core/threaded.py
Signature · 1 line
2.7.4
PATCH 1/45 scripts/runqemu: Replace subprocess.run() for compatibilty
From: Michael Halstead <[email protected]>
subprocess.run() was introduced in Python 3.5. We currently support down to Python 3.4 so I've replaced it with subprocess.check_call() which is available in that version.
(From OE-Core rev: 1c61f3017a6a2d95747883c722c376763ec77c9d)
--- scripts/runqemu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
scripts/runqemu +2 -2
@@ -1313,7 +1313,7 @@ def main(): logger.info("SIGTERM received") os.kill(config.qemupid, signal.SIGTERM) config.cleanup() - subprocess.run(["tput", "smam"]) + subprocess.check_call(["tput", "smam"]) signal.signal(signal.SIGTERM, sigterm_handler) config.check_args()
@@ -1335,7 +1335,7 @@ def main(): return 1 finally: config.cleanup() - subprocess.run(["tput", "smam"]) + subprocess.check_call(["tput", "smam"]) if __name__ == "__main__": sys.exit(main())
Signature · 1 line
2.7.4
PATCH 2/45 scripts/runqemu: Tidy up lock handling code
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Various tweaks:
- Balance up the aquire/release functions
- Use debug messge for both acquiring and release message for consistency in logs
- Use None instead of an empty string
- Reset the value of the field if we don't have the lock any more
(From OE-Core rev: d3c052e6ccd81d544b23a3bee80ba00cafaedbbd)
--- scripts/runqemu | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
scripts/runqemu +9 -7
@@ -218,7 +218,7 @@ class BaseConfig(object): self.tcpserial_portnum = '' self.custombiosdir = '' self.lock = '' - self.lock_descriptor = '' + self.lock_descriptor = None self.bitbake_e = '' self.snapshot = False self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
@@ -252,13 +252,17 @@ class BaseConfig(object): logger.info(msg) if self.lock_descriptor: self.lock_descriptor.close() + self.lock_descriptor = None return False return True def release_lock(self): - fcntl.flock(self.lock_descriptor, fcntl.LOCK_UN) - self.lock_descriptor.close() - os.remove(self.lock) + if self.lock_descriptor: + logger.debug("Releasing lockfile for tap device '%s'" % self.tap) + fcntl.flock(self.lock_descriptor, fcntl.LOCK_UN) + self.lock_descriptor.close() + os.remove(self.lock) + self.lock_descriptor = None def get(self, key): if key in self.d:
@@ -1232,9 +1236,7 @@ class BaseConfig(object): cmd = 'sudo %s %s %s' % (self.qemuifdown, self.tap, self.bindir_native) logger.debug('Running %s' % cmd) subprocess.check_call(cmd, shell=True) - if self.lock_descriptor: - logger.info("Releasing lockfile for tap device '%s'" % self.tap) - self.release_lock() + self.release_lock() if self.nfs_running: logger.info("Shutting down the userspace NFS server...")
Signature · 1 line
2.7.4
PATCH 3/45 scripts/runqemu: Improve lockfile handling for python with close_fd=True
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
On python versions with close_fds=True (python 3.2 onwards), the tap device lockfile isn't passed to the child process.
Since this guards against use of an active interface, we really want this here, so pass it in pass_fds. This means if the parent exits early, the child still holds the lock, avoiding messages like:
runqemu - ERROR - Failed to run qemu: qemu-system-x86_64: could not configure /dev/net/tun (tap0): Device or resource busy
(From OE-Core rev: 17a0a067d597c445c5892ff9914e91a2187f7e09)
(From OE-Core rev: d3b3c55ddc312039380ce8e23e68dd8bb2439388)
--- scripts/runqemu | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
scripts/runqemu +4 -1
@@ -1215,7 +1215,10 @@ class BaseConfig(object): cmd = "%s %s" % (self.qemu_opt, kernel_opts) cmds = shlex.split(cmd) logger.info('Running %s\n' % cmd) - process = subprocess.Popen(cmds, stderr=subprocess.PIPE) + pass_fds = [] + if self.lock_descriptor: + pass_fds = [self.lock_descriptor.fileno()] + process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds) self.qemupid = process.pid retcode = process.wait() if retcode:
Signature · 1 line
2.7.4
PATCH 4/45 oeqa/selftest/signing: Skip tests if gpg isn't found
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Raising an assertionError in the class setup isn't a particuarly good way to indicate gpg isn't installed. Instead skip the tests if the required binary isn't present. For the signing tests we do require it to be present and can't use a prebuilt one.
(From OE-Core rev: 2d486af97e51b9daa9c40482c31d637c9ab4ae79)
(From OE-Core rev: 65403bb556f7e3132722288a62ef36631af0b557)
--- meta/lib/oeqa/selftest/cases/signing.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-)
meta/lib/oeqa/selftest/cases/signing.py +11 -12
@@ -15,23 +15,18 @@ class Signing(OESelftestTestCase): pub_key_path = "" secret_key_path = "" - @classmethod - def setUpClass(cls): - super(Signing, cls).setUpClass() + def setup_gpg(self): # Check that we can find the gpg binary and fail early if we can't if not shutil.which("gpg"): - raise AssertionError("This test needs GnuPG") + self.skipTest('gpg binary not found') - cls.gpg_dir = tempfile.mkdtemp(prefix="oeqa-signing-") + self.gpg_dir = tempfile.mkdtemp(prefix="oeqa-signing-") + self.track_for_cleanup(self.gpg_dir) - cls.pub_key_path = os.path.join(cls.testlayer_path, 'files', 'signing', "key.pub") - cls.secret_key_path = os.path.join(cls.testlayer_path, 'files', 'signing', "key.secret") + self.pub_key_path = os.path.join(self.testlayer_path, 'files', 'signing', "key.pub") + self.secret_key_path = os.path.join(self.testlayer_path, 'files', 'signing', "key.secret") - runCmd('gpg --batch --homedir %s --import %s %s' % (cls.gpg_dir, cls.pub_key_path, cls.secret_key_path)) - - @classmethod - def tearDownClass(cls): - shutil.rmtree(cls.gpg_dir, ignore_errors=True) + runCmd('gpg --batch --homedir %s --import %s %s' % (self.gpg_dir, self.pub_key_path, self.secret_key_path)) @OETestID(1362) def test_signing_packages(self):
@@ -46,6 +41,8 @@ class Signing(OESelftestTestCase): """ import oe.packagedata + self.setup_gpg() + package_classes = get_bb_var('PACKAGE_CLASSES') if 'package_rpm' not in package_classes: self.skipTest('This test requires RPM Packaging.')
@@ -111,6 +108,8 @@ class Signing(OESelftestTestCase): builddir = os.environ.get('BUILDDIR') sstatedir = os.path.join(builddir, 'test-sstate') + self.setup_gpg() + self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe) self.add_command_to_tearDown('rm -rf %s' % sstatedir)
Signature · 1 line
2.7.4
PATCH 5/45 oeqa/selftest/signing: Allow tests not to need gpg on the host
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
We ideally don't want to use gpg from the host. This is straightforward for package management but not for sstate.
For sstate, create a second build directory to run the test in using gnupg-native from the original build directory.
(From OE-Core rev: 10afa94c3f0d7eb7524a26deda86949073d55fde)
(From OE-Core rev: c5d68a24b0c6df0a16f50075a690b3aab0e273ee)
--- meta/lib/oeqa/selftest/cases/signing.py | 67 +++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 19 deletions(-)
meta/lib/oeqa/selftest/cases/signing.py +48 -19
@@ -1,10 +1,12 @@ from oeqa.selftest.case import OESelftestTestCase from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars import os +import oe import glob import re import shutil import tempfile +from contextlib import contextmanager from oeqa.core.decorator.oeid import OETestID from oeqa.utils.ftools import write_file
@@ -16,9 +18,7 @@ class Signing(OESelftestTestCase): secret_key_path = "" def setup_gpg(self): - # Check that we can find the gpg binary and fail early if we can't - if not shutil.which("gpg"): - self.skipTest('gpg binary not found') + bitbake('gnupg-native -c addto_recipe_sysroot') self.gpg_dir = tempfile.mkdtemp(prefix="oeqa-signing-") self.track_for_cleanup(self.gpg_dir)
@@ -26,7 +26,30 @@ class Signing(OESelftestTestCase): self.pub_key_path = os.path.join(self.testlayer_path, 'files', 'signing', "key.pub") self.secret_key_path = os.path.join(self.testlayer_path, 'files', 'signing', "key.secret") - runCmd('gpg --batch --homedir %s --import %s %s' % (self.gpg_dir, self.pub_key_path, self.secret_key_path)) + nsysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "gnupg-native") + runCmd('gpg --batch --homedir %s --import %s %s' % (self.gpg_dir, self.pub_key_path, self.secret_key_path), native_sysroot=nsysroot) + return nsysroot + get_bb_var("bindir_native") + + + @contextmanager + def create_new_builddir(self, builddir, newbuilddir): + bb.utils.mkdirhier(newbuilddir) + oe.path.copytree(builddir + "/conf", newbuilddir + "/conf") + oe.path.copytree(builddir + "/cache", newbuilddir + "/cache") + + origenv = os.environ.copy() + + for e in os.environ: + if builddir in os.environ[e]: + os.environ[e] = os.environ[e].replace(builddir, newbuilddir) + + os.chdir(newbuilddir) + try: + yield + finally: + for e in origenv: + os.environ[e] = origenv[e] + os.chdir(builddir) @OETestID(1362) def test_signing_packages(self):
@@ -105,13 +128,12 @@ class Signing(OESelftestTestCase): test_recipe = 'ed' - builddir = os.environ.get('BUILDDIR') + # Since we need gpg but we can't use gpg-native for sstate signatures, we + # build gpg-native in our original builddir then run the tests in a second one. + builddir = os.environ.get('BUILDDIR') + "-testsign" sstatedir = os.path.join(builddir, 'test-sstate') - self.setup_gpg() - - self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe) - self.add_command_to_tearDown('rm -rf %s' % sstatedir) + nsysroot = self.setup_gpg() feature = 'SSTATE_SIG_KEY ?= "testuser"\n' feature += 'SSTATE_SIG_PASSPHRASE ?= "test123"\n'
@@ -123,19 +145,26 @@ class Signing(OESelftestTestCase): self.write_config(feature) - bitbake('-c clean %s' % test_recipe) - bitbake(test_recipe) + with self.create_new_builddir(os.environ['BUILDDIR'], builddir): + + os.environ["PATH"] = nsysroot + ":" + os.environ["PATH"] + self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe) + self.add_command_to_tearDown('rm -rf %s' % sstatedir) + self.add_command_to_tearDown('rm -rf %s' % builddir) + + bitbake('-c clean %s' % test_recipe) + bitbake(test_recipe) - recipe_sig = glob.glob(sstatedir + '/*/*:ed:*_package.tgz.sig') - recipe_tgz = glob.glob(sstatedir + '/*/*:ed:*_package.tgz') + recipe_sig = glob.glob(sstatedir + '/*/*:ed:*_package.tgz.sig') + recipe_tgz = glob.glob(sstatedir + '/*/*:ed:*_package.tgz') - self.assertEqual(len(recipe_sig), 1, 'Failed to find .sig file.') - self.assertEqual(len(recipe_tgz), 1, 'Failed to find .tgz file.') + self.assertEqual(len(recipe_sig), 1, 'Failed to find .sig file.') + self.assertEqual(len(recipe_tgz), 1, 'Failed to find .tgz file.') - ret = runCmd('gpg --homedir %s --verify %s %s' % (self.gpg_dir, recipe_sig[0], recipe_tgz[0])) - # gpg: Signature made Thu 22 Oct 2015 01:45:09 PM EEST using RSA key ID 61EEFB30 - # gpg: Good signature from "testuser (nocomment) <[email protected]>" - self.assertIn('gpg: Good signature from', ret.output, 'Package signed incorrectly.') + ret = runCmd('gpg --homedir %s --verify %s %s' % (self.gpg_dir, recipe_sig[0], recipe_tgz[0])) + # gpg: Signature made Thu 22 Oct 2015 01:45:09 PM EEST using RSA key ID 61EEFB30 + # gpg: Good signature from "testuser (nocomment) <[email protected]>" + self.assertIn('gpg: Good signature from', ret.output, 'Package signed incorrectly.') class LockedSignatures(OESelftestTestCase):
Signature · 1 line
2.7.4
PATCH 6/45 oeqa/selftest/signing: Use do_populate_lic target instead of do_package
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
This should speed the test up signficiantly without any loss of functionality for the purposes of the test.
(From OE-Core rev: 3dde0b749643575878bfbca2f8d2d9ec30bad166)
(From OE-Core rev: 51989db2d9eebd9f190994109c2932dac3f3034b)
--- meta/lib/oeqa/selftest/cases/signing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
meta/lib/oeqa/selftest/cases/signing.py +3 -3
@@ -153,10 +153,10 @@ class Signing(OESelftestTestCase): self.add_command_to_tearDown('rm -rf %s' % builddir) bitbake('-c clean %s' % test_recipe) - bitbake(test_recipe) + bitbake('-c populate_lic %s' % test_recipe) - recipe_sig = glob.glob(sstatedir + '/*/*:ed:*_package.tgz.sig') - recipe_tgz = glob.glob(sstatedir + '/*/*:ed:*_package.tgz') + recipe_sig = glob.glob(sstatedir + '/*/*:ed:*_populate_lic.tgz.sig') + recipe_tgz = glob.glob(sstatedir + '/*/*:ed:*_populate_lic.tgz') self.assertEqual(len(recipe_sig), 1, 'Failed to find .sig file.') self.assertEqual(len(recipe_tgz), 1, 'Failed to find .tgz file.')
Signature · 1 line
2.7.4
PATCH 7/45 oeqa/selftest/case: Use bb.utils.remove() instead of shutil.remove()
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
This avoids problems where shutil.remove will error with:
File "/usr/lib/python3.5/shutil.py", line 436, in _rmtree_safe_fd
os.unlink(name, dir_fd=topfd)
FileNotFoundError: [Errno 2] No such file or directory: 'S.gpg-agent.extra'
when there are races over file deletion (gpg agent may be slow to exit).
We already worked around speed and race issues in bb.utils.
(From OE-Core rev: 00a8fd5b93a5c19ce0b7498e2bc653ce8ad58aaf)
(From OE-Core rev: e7c0d29e063d1a47004acdc07a63996be98c74ef)
--- meta/lib/oeqa/selftest/case.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
meta/lib/oeqa/selftest/case.py +3 -1
@@ -12,6 +12,8 @@ import oeqa.utils.ftools as ftools from oeqa.utils.commands import runCmd, bitbake, get_bb_var from oeqa.core.case import OETestCase +import bb.utils + class OESelftestTestCase(OETestCase): def __init__(self, methodName="runTest"): self._extra_tear_down_commands = []
@@ -167,7 +169,7 @@ to ensure accurate results.") if self._track_for_cleanup: for path in self._track_for_cleanup: if os.path.isdir(path): - shutil.rmtree(path) + bb.utils.remove(path, recurse=True) if os.path.isfile(path): os.remove(path) self._track_for_cleanup = []
Signature · 1 line
2.7.4
PATCH 8/45 oeqa/selftest/buildoptions: Improve ccache test failure output
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
The current failure mode doesn't show us what the logs actually looked like and later cleans can lose them. Show the whole log in case of failure to aid debugging intermittent problems on the autobuilder.
(From OE-Core rev: 7c3a0dc5978cea898b1ca51decf4d6e7cf9d519f)
(From OE-Core rev: 60b58e976dcc2a45a036e18cfcc87933a8859c9f)
--- meta/lib/oeqa/selftest/cases/buildoptions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
meta/lib/oeqa/selftest/cases/buildoptions.py +3 -2
@@ -40,8 +40,9 @@ class ImageOptionsTests(OESelftestTestCase): self.add_command_to_tearDown('bitbake -c clean m4') bitbake("m4 -f -c compile") log_compile = os.path.join(get_bb_var("WORKDIR","m4"), "temp/log.do_compile") - res = runCmd("grep ccache %s" % log_compile, ignore_status=True) - self.assertEqual(0, res.status, msg="No match for ccache in m4 log.do_compile. For further details: %s" % log_compile) + with open(log_compile, "r") as f: + loglines = "".join(f.readlines()) + self.assertIn("ccache", loglines, msg="No match for ccache in m4 log.do_compile. For further details: %s" % log_compile) @OETestID(1435) def test_read_only_image(self):
Signature · 1 line
2.7.4
PATCH 9/45 oeqa/utils/commands: Add extra qemu failure logging
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Rather than just referring the user to the logs containing the failure, print them on the console. This aids debugging with oe-selftest with parallelisation as the logs may otherwise be lost.
(From OE-Core rev: 36a018e245a232f520ff946f152cc875927a6fb4)
(From OE-Core rev: 0ea441ab6a6fe752cc8820fb371e67eee92353e4)
--- meta/lib/oeqa/utils/commands.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
meta/lib/oeqa/utils/commands.py +5 -1
@@ -333,7 +333,11 @@ def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None, launch_cmd=None, try: qemu.start(params=qemuparams, ssh=ssh, runqemuparams=runqemuparams, launch_cmd=launch_cmd, discard_writes=discard_writes) except bb.build.FuncFailed: - raise Exception('Failed to start QEMU - see the logs in %s' % logdir) + msg = 'Failed to start QEMU - see the logs in %s' % logdir + if os.path.exists(qemu.qemurunnerlog): + with open(qemu.qemurunnerlog, 'r') as f: + msg = msg + "Qemurunner log output from %s:\n%s" % (qemu.qemurunnerlog, f.read()) + raise Exception(msg) yield qemu
Signature · 1 line
2.7.4
PATCH 10/45 oeqa/utils/qemurunner: Fix python ResourceWarning for unclosed file
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Fixes:
Stderr:
/media/build1/poky/meta/lib/oeqa/utils/qemurunner.py:381: ResourceWarning: unclosed file <_io.BufferedWriter name=16>
self.runqemu = None
(From OE-Core rev: b9e0bf919e6fc1a58e02145a363ebe7066e5bf4f)
(From OE-Core rev: b8a9eec2c9e56181350269acd6b42a14cc4abb3e)
--- meta/lib/oeqa/utils/qemurunner.py | 2 ++ 1 file changed, 2 insertions(+)
meta/lib/oeqa/utils/qemurunner.py +2 -0
@@ -369,6 +369,8 @@ class QemuRunner: if self.runqemu.poll() is None: self.logger.debug("Sending SIGKILL to runqemu") os.killpg(os.getpgid(self.runqemu.pid), signal.SIGKILL) + self.runqemu.stdin.close() + self.runqemu.stdout.close() self.runqemu = None if hasattr(self, 'server_socket') and self.server_socket: self.server_socket.close()
Signature · 1 line
2.7.4
PATCH 11/45 oeqa/utils/commands: Avoid log message duplication
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Each time a runqemu() fails, the log handler would be left behind meaning messages from any subsequent run would be duplicated (or worse/more).
This ensures we remove the handler regardless and means we no longer have the duplication.
(From OE-Core rev: 532984708436bdfa3a8cac2c684a425eb249bad0)
(From OE-Core rev: 600e71ede1977c45fed1958dd5356911e4fcf272)
--- meta/lib/oeqa/utils/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
meta/lib/oeqa/utils/commands.py +1 -1
@@ -342,11 +342,11 @@ def runqemu(pn, ssh=True, runqemuparams='', image_fstype=None, launch_cmd=None, yield qemu finally: + targetlogger.removeHandler(handler) try: qemu.stop() except: pass - targetlogger.removeHandler(handler) def updateEnv(env_file): """
Signature · 1 line
2.7.4
PATCH 12/45 oeqa/qemurunner: Remove resource python warnings
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
If runqemu fails it would leak an unclosed socket and file. Ensure we close these in all cases to remove the resource warning.
(From OE-Core rev: ed80e46ccbc8fe8e9148d80723152066fa00ba28)
(From OE-Core rev: baa8313e492cff8c31633a558792500b10493e9e)
--- meta/lib/oeqa/utils/qemurunner.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
meta/lib/oeqa/utils/qemurunner.py +8 -2
@@ -135,7 +135,7 @@ class QemuRunner: def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None, env = None): try: - threadsock, threadport = self.create_socket() + self.threadsock, threadport = self.create_socket() self.server_socket, self.serverport = self.create_socket() except socket.error as msg: self.logger.error("Failed to create listening socket: %s" % msg[1])
@@ -263,7 +263,7 @@ class QemuRunner: self.logger.debug("Target IP: %s" % self.ip) self.logger.debug("Server IP: %s" % self.server_ip) - self.thread = LoggingThread(self.log, threadsock, self.logger) + self.thread = LoggingThread(self.log, self.threadsock, self.logger) self.thread.start() if not self.thread.connection_established.wait(self.boottime): self.logger.error("Didn't receive a console connection from qemu. "
@@ -372,13 +372,19 @@ class QemuRunner: self.runqemu.stdin.close() self.runqemu.stdout.close() self.runqemu = None + if hasattr(self, 'server_socket') and self.server_socket: self.server_socket.close() self.server_socket = None + if hasattr(self, 'threadsock') and self.threadsock: + self.threadsock.close() + self.threadsock = None self.qemupid = None self.ip = None if os.path.exists(self.qemu_pidfile): os.remove(self.qemu_pidfile) + if self.monitorpipe: + self.monitorpipe.close() def stop_qemu_system(self): if self.qemupid:
Signature · 1 line
2.7.4
PATCH 13/45 oeqa/selftest/buildoptions: Improve ccache test
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
This test occisionally fails as m4 doesn't recompile, meaning the logfile test then doesn't find mention of ccache.
To ensure m4 does recompile, clean m4 before force compiling it.
(Reading the test is confusing due to the test cleanup also involving a clean)
(From OE-Core rev: 6e0b9214a0d57ed45a5df0ba5c9887a9045b89b1)
(From OE-Core rev: b729a46820cce38b3d500271df19bdaf31973140)
--- meta/lib/oeqa/selftest/cases/buildoptions.py | 1 + 1 file changed, 1 insertion(+)
meta/lib/oeqa/selftest/cases/buildoptions.py +1 -0
@@ -38,6 +38,7 @@ class ImageOptionsTests(OESelftestTestCase): self.assertTrue(os.path.isfile(p), msg = "No ccache found (%s)" % p) self.write_config('INHERIT += "ccache"') self.add_command_to_tearDown('bitbake -c clean m4') + bitbake("m4 -c clean") bitbake("m4 -f -c compile") log_compile = os.path.join(get_bb_var("WORKDIR","m4"), "temp/log.do_compile") with open(log_compile, "r") as f:
Signature · 1 line
2.7.4
PATCH 14/45 oeqa/selftest/buildoptions: Ensure diskmon tests run consistently
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Heartbeat events default to once a second and we need to ensure we have enough time in the task to see them.
Add a nostamp delay task 5s long so we can have a consistently timed task which doesn't need cleanup or have unneeded dependencies. This ensures we should deterministically see the disk moinitor events regardless of the state of the build. This is done in a way which doesn't corrupt build state or need cleanup and is efficient.
(From OE-Core rev: ecc49ee8986929e2429d948000a0ca588fe63959)
(From OE-Core rev: d5ce38168238181423c9dcd1d258253e3515d0a5)
--- meta-selftest/recipes-test/delay/delay.bb | 12 ++++++++++++ meta/lib/oeqa/selftest/cases/buildoptions.py | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 meta-selftest/recipes-test/delay/delay.bb
meta-selftest/recipes-test/delay/delay.bb new file +12 -0
@@ -0,0 +1,12 @@ +SUMMARY = "Recipe with a fixed delay task" +DESCRIPTION = "Contains a delay task to be used to for testing." +LICENSE = "MIT" + +INHIBIT_DEFAULT_DEPS = "1" +EXCLUDE_FROM_WORLD = "1" + +do_delay() { + sleep 5 +} +do_delay[nostamp] = "1" +addtask delay
meta/lib/oeqa/selftest/cases/buildoptions.py +3 -3
@@ -59,15 +59,15 @@ class DiskMonTest(OESelftestTestCase): @OETestID(277) def test_stoptask_behavior(self): self.write_config('BB_DISKMON_DIRS = "STOPTASKS,${TMPDIR},100000G,100K"') - res = bitbake("m4", ignore_status = True) + res = bitbake("delay -c delay", ignore_status = True) self.assertTrue('ERROR: No new tasks can be executed since the disk space monitor action is "STOPTASKS"!' in res.output, msg = "Tasks should have stopped. Disk monitor is set to STOPTASK: %s" % res.output) self.assertEqual(res.status, 1, msg = "bitbake reported exit code %s. It should have been 1. Bitbake output: %s" % (str(res.status), res.output)) self.write_config('BB_DISKMON_DIRS = "ABORT,${TMPDIR},100000G,100K"') - res = bitbake("m4", ignore_status = True) + res = bitbake("delay -c delay", ignore_status = True) self.assertTrue('ERROR: Immediately abort since the disk space monitor action is "ABORT"!' in res.output, "Tasks should have been aborted immediatelly. Disk monitor is set to ABORT: %s" % res.output) self.assertEqual(res.status, 1, msg = "bitbake reported exit code %s. It should have been 1. Bitbake output: %s" % (str(res.status), res.output)) self.write_config('BB_DISKMON_DIRS = "WARN,${TMPDIR},100000G,100K"') - res = bitbake("m4") + res = bitbake("delay -c delay") self.assertTrue('WARNING: The free space' in res.output, msg = "A warning should have been displayed for disk monitor is set to WARN: %s" %res.output) class SanityOptionsTest(OESelftestTestCase):
Signature · 1 line
2.7.4
PATCH 15/45 oeqa/oelib/path: don't leak temporary directories
In reply to Armin KusterFrom: Ross Burton <[email protected]>
setUp() is used to populate a directory of temporary files, and deleted in __del__. However setUp() is called once *per test* so __del__ would only be able to remove the last directory created.
Fix the code by using the natural counterpart to setUp, tearDown(), to clean up.
(From OE-Core rev: 68b4723e6fb11d171869185bccf28f32f6284c18)
(From OE-Core rev: edd2ecbc86d8b02a0467491451306f67e81d9ead)
--- meta/lib/oeqa/selftest/cases/oelib/path.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
meta/lib/oeqa/selftest/cases/oelib/path.py +3 -7
@@ -38,13 +38,6 @@ class TestRealPath(TestCase): ( "b/test", errno.ENOENT ), ] - def __del__(self): - try: - #os.system("tree -F %s" % self.tmpdir) - shutil.rmtree(self.tmpdir) - except: - pass - def setUp(self): self.tmpdir = tempfile.mkdtemp(prefix = "oe-test_path") self.root = os.path.join(self.tmpdir, "R")
@@ -59,6 +52,9 @@ class TestRealPath(TestCase): for l in self.LINKS: os.symlink(l[1], os.path.join(self.root, l[0])) + def tearDown(self): + shutil.rmtree(self.tmpdir) + def __realpath(self, file, use_physdir, assume_dir = True): return oe.path.realpath(os.path.join(self.root, file), self.root, use_physdir, assume_dir = assume_dir)
Signature · 1 line
2.7.4
PATCH 16/45 oeqa/selftest/runqemu: Improve testcase failure handling
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
assertTrue doesn't give good debug information when things fail. Update several to use assertIn which gives information upon failure, for the others print the log information upon failure.
(From OE-Core rev: c29cb75d5ce6b0873a934f4709b0c8824f7164d3)
(From OE-Core rev: 6f2bb2c12289422396deb793ae4b4e99cfa88c7b)
--- meta/lib/oeqa/selftest/cases/runqemu.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-)
meta/lib/oeqa/selftest/cases/runqemu.py +20 -12
@@ -44,7 +44,8 @@ SYSLINUX_TIMEOUT = "10" """Test runqemu machine""" cmd = "%s %s" % (self.cmd_common, self.machine) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: - self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) + with open(qemu.qemurunnerlog) as f: + self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read())) @OETestID(2002) def test_boot_machine_ext4(self):
@@ -52,7 +53,7 @@ SYSLINUX_TIMEOUT = "10" cmd = "%s %s ext4" % (self.cmd_common, self.machine) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: with open(qemu.qemurunnerlog) as f: - self.assertTrue('rootfs.ext4' in f.read(), "Failed: %s" % cmd) + self.assertIn('rootfs.ext4', f.read(), "Failed: %s" % cmd) @OETestID(2003) def test_boot_machine_iso(self):
@@ -60,14 +61,16 @@ SYSLINUX_TIMEOUT = "10" cmd = "%s %s iso" % (self.cmd_common, self.machine) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: with open(qemu.qemurunnerlog) as f: - self.assertTrue('media=cdrom' in f.read(), "Failed: %s" % cmd) + self.assertIn('media=cdrom', f.read(), "Failed: %s" % cmd) @OETestID(2004) def test_boot_recipe_image(self): """Test runqemu recipe-image""" cmd = "%s %s" % (self.cmd_common, self.recipe) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: - self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) + with open(qemu.qemurunnerlog) as f: + self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read())) + @OETestID(2005) def test_boot_recipe_image_vmdk(self):
@@ -75,7 +78,7 @@ SYSLINUX_TIMEOUT = "10" cmd = "%s %s wic.vmdk" % (self.cmd_common, self.recipe) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: with open(qemu.qemurunnerlog) as f: - self.assertTrue('format=vmdk' in f.read(), "Failed: %s" % cmd) + self.assertIn('format=vmdk', f.read(), "Failed: %s" % cmd) @OETestID(2006) def test_boot_recipe_image_vdi(self):
@@ -83,14 +86,16 @@ SYSLINUX_TIMEOUT = "10" cmd = "%s %s wic.vdi" % (self.cmd_common, self.recipe) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: with open(qemu.qemurunnerlog) as f: - self.assertTrue('format=vdi' in f.read(), "Failed: %s" % cmd) + self.assertIn('format=vdi', f.read(), "Failed: %s" % cmd) @OETestID(2007) def test_boot_deploy(self): """Test runqemu deploy_dir_image""" cmd = "%s %s" % (self.cmd_common, self.deploy_dir_image) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: - self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) + with open(qemu.qemurunnerlog) as f: + self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read())) + @OETestID(2008) def test_boot_deploy_hddimg(self):
@@ -98,7 +103,7 @@ SYSLINUX_TIMEOUT = "10" cmd = "%s %s hddimg" % (self.cmd_common, self.deploy_dir_image) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: with open(qemu.qemurunnerlog) as f: - self.assertTrue(re.search('file=.*.hddimg', f.read()), "Failed: %s" % cmd) + self.assertTrue(re.search('file=.*.hddimg', f.read()), "Failed: %s, %s" % (cmd, f.read())) @OETestID(2009) def test_boot_machine_slirp(self):
@@ -106,7 +111,7 @@ SYSLINUX_TIMEOUT = "10" cmd = "%s slirp %s" % (self.cmd_common, self.machine) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: with open(qemu.qemurunnerlog) as f: - self.assertTrue(' -netdev user' in f.read(), "Failed: %s" % cmd) + self.assertIn(' -netdev user', f.read(), "Failed: %s" % cmd) @OETestID(2009) def test_boot_machine_slirp_qcow2(self):
@@ -114,7 +119,7 @@ SYSLINUX_TIMEOUT = "10" cmd = "%s slirp wic.qcow2 %s" % (self.cmd_common, self.machine) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: with open(qemu.qemurunnerlog) as f: - self.assertTrue('format=qcow2' in f.read(), "Failed: %s" % cmd) + self.assertIn('format=qcow2', f.read(), "Failed: %s" % cmd) @OETestID(2010) def test_boot_qemu_boot(self):
@@ -125,7 +130,8 @@ SYSLINUX_TIMEOUT = "10" self.skipTest("%s not found" % qemuboot_conf) cmd = "%s %s" % (self.cmd_common, qemuboot_conf) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: - self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) + with open(qemu.qemurunnerlog) as f: + self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read())) @OETestID(2011) def test_boot_rootfs(self):
@@ -136,7 +142,9 @@ SYSLINUX_TIMEOUT = "10" self.skipTest("%s not found" % rootfs) cmd = "%s %s" % (self.cmd_common, rootfs) with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu: - self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) + with open(qemu.qemurunnerlog) as f: + self.assertTrue(qemu.runner.logged, "Failed: %s, %s" % (cmd, f.read())) + # This test was designed as a separate class to test that shutdown # command will shutdown qemu as expected on each qemu architecture
Signature · 1 line
2.7.4
PATCH 17/45 oeqa/utils/qemurunner: Avoid tracebacks on closed files
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Reorder the shutdown/teardown to avoid:
File "/home/pokybuild/yocto-worker/oe-selftest-ubuntu/build/meta/lib/oeqa/utils/qemurunner.py", line 224, in launch
op = self.getOutput(output)
File "/home/pokybuild/yocto-worker/oe-selftest-ubuntu/build/meta/lib/oeqa/utils/qemurunner.py", line 90, in getOutput
fl = fcntl.fcntl(o, fcntl.F_GETFL)
ValueError: I/O operation on closed file
(From OE-Core rev: 8e7d756862d2a8d62f3c87497d6d65ddb3c1b962)
(From OE-Core rev: 48979ffbe25351f92179021a973207a71bbe7a4e)
--- meta/lib/oeqa/utils/qemurunner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
meta/lib/oeqa/utils/qemurunner.py +2 -2
@@ -203,8 +203,8 @@ class QemuRunner: # No point waiting any longer self.logger.debug('runqemu exited with code %d' % self.runqemu.returncode) self._dump_host() - self.stop() self.logger.debug("Output from runqemu:\n%s" % self.getOutput(output)) + self.stop() return False time.sleep(0.5)
@@ -216,8 +216,8 @@ class QemuRunner: processes = ps.decode("utf-8") self.logger.debug("Running processes:\n%s" % processes) self._dump_host() - self.stop() op = self.getOutput(output) + self.stop() if op: self.logger.error("Output from runqemu:\n%s" % op) else:
Signature · 1 line
2.7.4
PATCH 18/45 oeqa: don't litter /tmp with temporary directories
In reply to Armin KusterFrom: Ross Burton <[email protected]>
If we need to create a temporary directory in targetbuild or buildproject use tempfile.TemporaryDirectory so that when the test case is finished, the directory is deleted.
Also synchronise the logic and don't possibly store the temporary directory in self.tmpdir as nothing uses that.
(From OE-Core rev: db0e658097130d146752785d0d45f46a3e0bad71)
(From OE-Core rev: d39252324a13580cc96f0694b88bc10515e030a0)
--- meta/lib/oeqa/utils/buildproject.py | 3 ++- meta/lib/oeqa/utils/targetbuild.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-)
meta/lib/oeqa/utils/buildproject.py +2 -1
@@ -17,7 +17,8 @@ class BuildProject(metaclass=ABCMeta): self.uri = uri self.archive = os.path.basename(uri) if not tmpdir: - tmpdir = tempfile.mkdtemp(prefix='buildproject') + self.tempdirobj = tempfile.TemporaryDirectory(prefix='buildproject-') + tmpdir = self.tempdirobj.name self.localarchive = os.path.join(tmpdir, self.archive) self.dl_dir = dl_dir if foldername:
meta/lib/oeqa/utils/targetbuild.py +3 -2
@@ -20,8 +20,9 @@ class BuildProject(metaclass=ABCMeta): if not tmpdir: tmpdir = self.d.getVar('WORKDIR') if not tmpdir: - tmpdir = tempfile.mkdtemp(prefix='buildproject') - self.localarchive = os.path.join(tmpdir,self.archive) + self.tempdirobj = tempfile.TemporaryDirectory(prefix='buildproject-') + tmpdir = self.tempdirobj.name + self.localarchive = os.path.join(tmpdir, self.archive) if foldername: self.fname = foldername else:
Signature · 1 line
2.7.4
PATCH 19/45 oeqa/selftest/esdk: run selftest inside workdir not /tmp
In reply to Armin KusterFrom: Ross Burton <[email protected]>
We've seen issues with rootfs size calculations and we've seen systems like opensuse which have btrfs mounted on /tmp causing selftest failures.
(From OE-Core rev: 61be3cd748d1b7321a1fc4cfe84efa9b26a6aee0)
(From OE-Core rev: d936faabfb29ea377d74e77332a2a91603747ac7)
--- meta/lib/oeqa/selftest/cases/eSDK.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
meta/lib/oeqa/selftest/cases/eSDK.py +7 -6
@@ -70,11 +70,12 @@ CORE_IMAGE_EXTRA_INSTALL = "perl" @classmethod def setUpClass(cls): super(oeSDKExtSelfTest, cls).setUpClass() - cls.tmpdir_eSDKQA = tempfile.mkdtemp(prefix='eSDKQA') + cls.image = 'core-image-minimal' - sstate_dir = get_bb_var('SSTATE_DIR') + bb_vars = get_bb_vars(['SSTATE_DIR', 'WORKDIR'], cls.image) + cls.tmpdirobj = tempfile.TemporaryDirectory(prefix="selftest-esdk-", dir=bb_vars["WORKDIR"]) + cls.tmpdir_eSDKQA = cls.tempdirobj.name - cls.image = 'core-image-minimal' oeSDKExtSelfTest.generate_eSDK(cls.image) # Install eSDK
@@ -87,14 +88,14 @@ CORE_IMAGE_EXTRA_INSTALL = "perl" sstate_config=""" SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS" SSTATE_MIRRORS = "file://.* file://%s/PATH" - """ % sstate_dir + """ % bb_vars["SSTATE_DIR"] with open(os.path.join(cls.tmpdir_eSDKQA, 'conf', 'local.conf'), 'a+') as f: f.write(sstate_config) @classmethod def tearDownClass(cls): - shutil.rmtree(cls.tmpdir_eSDKQA, ignore_errors=True) - super(oeSDKExtSelfTest, cls).tearDownClass() + cls.tmpdirobj.cleanup() + super().tearDownClass() @OETestID(1602) def test_install_libraries_headers(self):
Signature · 1 line
2.7.4
PATCH 20/45 oeqa/loader: Fix deprecation warning
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Clean up the warning:meta/lib/oeqa/core/loader.py:27: DeprecationWarning: inspect.getargspec() is deprecated, use inspect.signature() or inspect.getfullargspec()
_failed_test_args = inspect.getargspec(unittest.loader._make_failed_test).args
(From OE-Core rev: d2deb66830be2d44532fea3d5db763b57778252a)
(From OE-Core rev: f9ab10bb08446052fd6af2a21f38d8454e466d51)
--- meta/lib/oeqa/core/loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
meta/lib/oeqa/core/loader.py +1 -1
@@ -24,7 +24,7 @@ from oeqa.core.decorator import decoratorClasses, OETestDecorator, \ # Generate the function definition because this differ across python versions # Python >= 3.4.4 uses tree parameters instead four but for example Python 3.5.3 # ueses four parameters so isn't incremental. -_failed_test_args = inspect.getargspec(unittest.loader._make_failed_test).args +_failed_test_args = inspect.getfullargspec(unittest.loader._make_failed_test).args exec("""def _make_failed_test(%s): raise exception""" % ', '.join(_failed_test_args)) unittest.loader._make_failed_test = _make_failed_test
Signature · 1 line
2.7.4
PATCH 21/45 oeqa/utils/commands: Avoid unclosed file warnings
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Avoid warnings such as:
meta/lib/oeqa/utils/commands.py:213: ResourceWarning: unclosed file <_io.BufferedReader name=4>
return runCmd(cmd, ignore_status, timeout, output_log=output_log, **options)
(From OE-Core rev: 6a68c42de08cffbadb59ebda63fa5e19f6e5acef)
(From OE-Core rev: 682d7b2810b235e86a28a8afe034e3853dbe8c45)
--- meta/lib/oeqa/utils/commands.py | 3 +++ 1 file changed, 3 insertions(+)
meta/lib/oeqa/utils/commands.py +3 -0
@@ -146,6 +146,9 @@ class Command(object): # At this point we know that the process has closed stdout/stderr, so # it is safe and necessary to wait for the actual process completion. self.status = self.process.wait() + self.process.stdout.close() + if self.process.stderr: + self.process.stderr.close() self.log.debug("Command '%s' returned %d as exit code." % (self.cmd, self.status)) # logging the complete output is insane
Signature · 1 line
2.7.4
PATCH 22/45 oeqa/selftest/context: Replace deprecated imp module usage
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Avoid the warning:
meta/lib/oeqa/selftest/context.py:8: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
import imp
In this case importlib is a direct replacement.
(From OE-Core rev: db7a60c36a2d3eefc61ae6e1ede01680dc932035)
(From OE-Core rev: 351a7b75959593922909d7e1929a6429a2bf94a7)
--- meta/lib/oeqa/selftest/context.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
meta/lib/oeqa/selftest/context.py +2 -2
@@ -5,7 +5,7 @@ import os import time import glob import sys -import imp +import importlib import signal from shutil import copyfile from random import choice
@@ -174,7 +174,7 @@ class OESelftestTestContextExecutor(OETestContextExecutor): self.tc.logger.info("\t%s" % l) sys.path.extend(layer_libdirs) - imp.reload(oeqa.selftest) + importlib.reload(oeqa.selftest) _check_required_env_variables(["BUILDDIR"]) _check_presence_meta_selftest()
Signature · 1 line
2.7.4
PATCH 23/45 oeqa/utils/qemurunner.py: Fix python regex warnings
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Fix the warnings:
meta/lib/oeqa/utils/qemurunner.py:250: DeprecationWarning: invalid escape sequence \.
ips = re.findall("((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1])meta/lib/oeqa/utils/qemurunner.py:343: DeprecationWarning: invalid escape sequence \-
if re.search("root@[a-zA-Z0-9\-]+:~#", output):poky/meta/lib/oeqa/utils/qemurunner.py:350: DeprecationWarning: invalid escape sequence \-
if re.search("root@[a-zA-Z0-9\-]+:~#", output):meta/lib/oeqa/utils/qemurunner.py:448: DeprecationWarning: invalid escape sequence \-
if re.search("[a-zA-Z0-9]+@[a-zA-Z0-9\-]+:~#", data):
by correctly marking the regexs.
(From OE-Core rev: 8e6987735002560fca714f77ea8ece9d4b28f7fa)
(From OE-Core rev: 7eb1f0be82d7ee12f893cdd40384da306fa597a7)
--- meta/lib/oeqa/utils/qemurunner.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
meta/lib/oeqa/utils/qemurunner.py +5 -5
@@ -238,13 +238,13 @@ class QemuRunner: # because is possible to have control characters cmdline = re_control_char.sub(' ', cmdline) try: - ips = re.findall("((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1]) + ips = re.findall(r"((?:[0-9]{1,3}\.){3}[0-9]{1,3})", cmdline.split("ip=")[1]) self.ip = ips[0] self.server_ip = ips[1] self.logger.debug("qemu cmdline used:\n{}".format(cmdline)) except (IndexError, ValueError): # Try to get network configuration from runqemu output - match = re.match('.*Network configuration: ([0-9.]+)::([0-9.]+):([0-9.]+)$.*', + match = re.match(r'.*Network configuration: ([0-9.]+)::([0-9.]+):([0-9.]+)$.*', out, re.MULTILINE|re.DOTALL) if match: self.ip, self.server_ip, self.netmask = match.groups()
@@ -331,14 +331,14 @@ class QemuRunner: # If we are not able to login the tests can continue try: (status, output) = self.run_serial("root\n", raw=True) - if re.search("root@[a-zA-Z0-9\-]+:~#", output): + if re.search(r"root@[a-zA-Z0-9\-]+:~#", output): self.logged = True self.logger.debug("Logged as root in serial console") if netconf: # configure guest networking cmd = "ifconfig eth0 %s netmask %s up\n" % (self.ip, self.netmask) output = self.run_serial(cmd, raw=True)[1] - if re.search("root@[a-zA-Z0-9\-]+:~#", output): + if re.search(r"root@[a-zA-Z0-9\-]+:~#", output): self.logger.debug("configured ip address %s", self.ip) else: self.logger.debug("Couldn't configure guest networking")
@@ -444,7 +444,7 @@ class QemuRunner: if answer: data += answer.decode('utf-8') # Search the prompt to stop - if re.search("[a-zA-Z0-9]+@[a-zA-Z0-9\-]+:~#", data): + if re.search(r"[a-zA-Z0-9]+@[a-zA-Z0-9\-]+:~#", data): break else: raise Exception("No data on serial console socket")
Signature · 1 line
2.7.4
PATCH 24/45 oeqa/selftest/context: Improve log file handling
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
The existing logfile is simply placed in the current directory. Since the test changes cwd to BUILDDIR, the symlink to the log can be placed in an invalid directory. We also see trackbacks if the symlink is invalid.
Improve things by:
* Placing logs in LOG_DIR (or BUILDDIR if unset).
* Using a full path to the log meaning the log and link are placed in the same directory.
* Using lexists instead of exists so invalid symlinks are handled correctly.
(From OE-Core rev: 750ece11bed0e62a11e0003d1d16a81f7c219761)
(From OE-Core rev: 9c4c3c876dd5d224133571fcad1095af1098ae1d)
--- meta/lib/oeqa/selftest/context.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
meta/lib/oeqa/selftest/context.py +10 -4
@@ -96,11 +96,17 @@ class OESelftestTestContextExecutor(OETestContextExecutor): return cases_paths def _process_args(self, logger, args): - args.output_log = '%s-results-%s.log' % (self.name, - time.strftime("%Y%m%d%H%M%S")) + + args.test_start_time = time.strftime("%Y%m%d%H%M%S") args.test_data_file = None args.CASES_PATHS = None + bbvars = get_bb_vars() + logdir = os.environ.get("BUILDDIR") + if 'LOG_DIR' in bbvars: + logdir = bbvars['LOG_DIR'] + args.output_log = logdir + '/%s-results-%s.log' % (self.name, args.test_start_time) + super(OESelftestTestContextExecutor, self)._process_args(logger, args) if args.list_modules:
@@ -110,7 +116,7 @@ class OESelftestTestContextExecutor(OETestContextExecutor): elif args.list_tests: args.list_tests = 'name' - self.tc_kwargs['init']['td'] = get_bb_vars() + self.tc_kwargs['init']['td'] = bbvars self.tc_kwargs['init']['machines'] = self._get_available_machines() builddir = os.environ.get("BUILDDIR")
@@ -270,7 +276,7 @@ class OESelftestTestContextExecutor(OETestContextExecutor): output_link = os.path.join(os.path.dirname(args.output_log), "%s-results.log" % self.name) - if os.path.exists(output_link): + if os.path.lexists(output_link): os.remove(output_link) os.symlink(args.output_log, output_link)
Signature · 1 line
2.7.4
PATCH 25/45 oeqa/core/runner: Improve test case comparision
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
We can directly compare the test case IDs rather than representations, then if we're using subunit to split the tests, the comparisions still work as intended.
(From OE-Core rev: 72e5f46f75454ba4c445c65c1cbc616a9e72fc6e)
(From OE-Core rev: 4c53aac5315f9d0a0ed95cbeb48b7704e274e3a9)
--- meta/lib/oeqa/core/runner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
meta/lib/oeqa/core/runner.py +2 -2
@@ -88,10 +88,10 @@ class OETestResult(_TestResult): break scase_str = scase.test_id else: - if case == scase: + if case.id() == scase.id(): found = True break - scase_str = str(scase) + scase_str = str(scase.id()) # When fails at module or class level the class name is passed as string # so figure out to see if match
Signature · 1 line
2.7.4
PATCH 26/45 oeqa/runner: Ensure we don't print misleading results output
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
The current code assumes if something isn't a failure of some kind, it was a pass. When test case IDs weren't matching, this lead to very confusing output where things would fail, then be listed as passing.
This adds code to track successes, ensuring we don't end up in this position again with unmatched entries being listed as UNKNOWN.
(From OE-Core rev: 4374c296d8963e4f6a1aa7bef7983ad0a1c2fcff)
(From OE-Core rev: bcb2948773d76befef2be787be6d25cf544e49a9)
--- meta/lib/oeqa/core/runner.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
meta/lib/oeqa/core/runner.py +11 -3
@@ -42,6 +42,8 @@ class OETestResult(_TestResult): def __init__(self, tc, *args, **kwargs): super(OETestResult, self).__init__(*args, **kwargs) + self.successes = [] + self.tc = tc self._tc_map_results()
@@ -58,6 +60,7 @@ class OETestResult(_TestResult): self.tc._results['errors'] = self.errors self.tc._results['skipped'] = self.skipped self.tc._results['expectedFailures'] = self.expectedFailures + self.tc._results['successes'] = self.successes def logSummary(self, component, context_msg=''): elapsed_time = self.tc._run_end_time - self.tc._run_start_time
@@ -115,13 +118,18 @@ class OETestResult(_TestResult): return (found, None) + def addSuccess(self, test): + #Added so we can keep track of successes too + self.successes.append((test, None)) + super(OETestResult, self).addSuccess(test) + def logDetails(self): self.tc.logger.info("RESULTS:") for case_name in self.tc._registry['cases']: case = self.tc._registry['cases'][case_name] - result_types = ['failures', 'errors', 'skipped', 'expectedFailures'] - result_desc = ['FAILED', 'ERROR', 'SKIPPED', 'EXPECTEDFAIL'] + result_types = ['failures', 'errors', 'skipped', 'expectedFailures', 'successes'] + result_desc = ['FAILED', 'ERROR', 'SKIPPED', 'EXPECTEDFAIL', 'PASSED'] fail = False desc = None
@@ -143,7 +151,7 @@ class OETestResult(_TestResult): oeid, desc)) else: self.tc.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(), - oeid, 'PASSED')) + oeid, 'UNKNOWN')) class OEListTestsResult(object): def wasSuccessful(self):
Signature · 1 line
2.7.4
PATCH 27/45 oeqa/core/threaded: Remove in favour of using concurrenttests
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
We have several options for parallel processing in oeqa, parallel execution of modules, threading and mulitple processes for the runners.
After much experimentation is appears the most scalable and least invasive approach is multiple processes using concurrenttestsuite from testtools. This means we can drop the current threading code which is only used by the sdk test execution.
oeqa/decorator/depends: Remove threading code
Revert "oeqa/sdk: Enable usage of OEQA thread mode" This reverts commit adc434c0636b7dea2ef70c8d2c8e61cdb5c703b1.
Revert "oeqa/core/tests: Add tests of OEQA Threaded mode" This reverts commit a4eef558c9933eb32413b61ff80a11b999951b40.
Revert "oeqa/core/decorator/oetimeout: Add support for OEQA threaded mode" This reverts commit d3d4ba902dee8b19fa1054330cffdf73f9b81fe7.
(From OE-Core rev: a98ab5e560e73b6988512fbae5cefe9e42ceed53)
(From OE-Core rev: bb9a85e157e669d7a91c3bbefc8d5138e7b8b6ae)
--- meta/classes/testsdk.bbclass | 4 - meta/lib/oeqa/core/decorator/depends.py | 7 +- meta/lib/oeqa/core/decorator/oetimeout.py | 40 +-- .../core/tests/cases/loader/threaded/threaded.py | 12 - .../tests/cases/loader/threaded/threaded_alone.py | 8 - .../cases/loader/threaded/threaded_depends.py | 10 - .../tests/cases/loader/threaded/threaded_module.py | 12 - meta/lib/oeqa/core/tests/common.py | 10 - meta/lib/oeqa/core/tests/test_decorators.py | 12 - meta/lib/oeqa/core/tests/test_loader.py | 30 +-- meta/lib/oeqa/core/threaded.py | 275 --------------------- meta/lib/oeqa/sdk/context.py | 5 +- 12 files changed, 14 insertions(+), 411 deletions(-) delete mode 100644 meta/lib/oeqa/core/tests/cases/loader/threaded/threaded.py delete mode 100644 meta/lib/oeqa/core/tests/cases/loader/threaded/threaded_alone.py delete mode 100644 meta/lib/oeqa/core/tests/cases/loader/threaded/threaded_depends.py delete mode 100644 meta/lib/oeqa/core/tests/cases/loader/threaded/threaded_module.py delete mode 100644 meta/lib/oeqa/core/threaded.py
meta/classes/testsdk.bbclass +0 -4
@@ -24,8 +24,6 @@ def testsdk_main(d): from oeqa.sdk.context import OESDKTestContext, OESDKTestContextExecutor from oeqa.utils import make_logger_bitbake_compatible - bb.event.enable_threadlock() - pn = d.getVar("PN") logger = make_logger_bitbake_compatible(logging.getLogger("BitBake"))
@@ -99,8 +97,6 @@ def testsdkext_main(d): from oeqa.utils import avoid_paths_in_environ, make_logger_bitbake_compatible, subprocesstweak from oeqa.sdkext.context import OESDKExtTestContext, OESDKExtTestContextExecutor - bb.event.enable_threadlock() - pn = d.getVar("PN") logger = make_logger_bitbake_compatible(logging.getLogger("BitBake"))
meta/lib/oeqa/core/decorator/depends.py +1 -6
@@ -3,7 +3,6 @@ from unittest import SkipTest -from oeqa.core.threaded import OETestRunnerThreaded from oeqa.core.exception import OEQADependency from . import OETestDiscover, registerDecorator
@@ -64,11 +63,7 @@ def _order_test_case_by_depends(cases, depends): return [cases[case_id] for case_id in cases_ordered] def _skipTestDependency(case, depends): - if isinstance(case.tc.runner, OETestRunnerThreaded): - import threading - results = case.tc._results[threading.get_ident()] - else: - results = case.tc._results + results = case.tc._results skipReasons = ['errors', 'failures', 'skipped']
meta/lib/oeqa/core/decorator/oetimeout.py +10 -30
@@ -1,12 +1,8 @@ # Copyright (C) 2016 Intel Corporation # Released under the MIT license (see COPYING.MIT) -from . import OETestDecorator, registerDecorator - import signal -from threading import Timer - -from oeqa.core.threaded import OETestRunnerThreaded +from . import OETestDecorator, registerDecorator from oeqa.core.exception import OEQATimeoutError @registerDecorator
@@ -14,32 +10,16 @@ class OETimeout(OETestDecorator): attrs = ('oetimeout',) def setUpDecorator(self): - self.logger.debug("Setting up a %d second(s) timeout" % self.oetimeout) - - if isinstance(self.case.tc.runner, OETestRunnerThreaded): - self.timeouted = False - def _timeoutHandler(): - self.timeouted = True - - self.timer = Timer(self.oetimeout, _timeoutHandler) - self.timer.start() - else: - timeout = self.oetimeout - def _timeoutHandler(signum, frame): - raise OEQATimeoutError("Timed out after %s " + timeout = self.oetimeout + def _timeoutHandler(signum, frame): + raise OEQATimeoutError("Timed out after %s " "seconds of execution" % timeout) - self.alarmSignal = signal.signal(signal.SIGALRM, _timeoutHandler) - signal.alarm(self.oetimeout) + self.logger.debug("Setting up a %d second(s) timeout" % self.oetimeout) + self.alarmSignal = signal.signal(signal.SIGALRM, _timeoutHandler) + signal.alarm(self.oetimeout) def tearDownDecorator(self): - if isinstance(self.case.tc.runner, OETestRunnerThreaded): - self.timer.cancel() - self.logger.debug("Removed Timer handler") - if self.timeouted: - raise OEQATimeoutError("Timed out after %s " - "seconds of execution" % self.oetimeout) - else: - signal.alarm(0) - signal.signal(signal.SIGALRM, self.alarmSignal) - self.logger.debug("Removed SIGALRM handler") + signal.alarm(0) + signal.signal(signal.SIGALRM, self.alarmSignal) + self.logger.debug("Removed SIGALRM handler")
meta/lib/oeqa/core/tests/cases/loader/threaded/threaded.py deleted +0 -12
@@ -1,12 +0,0 @@ -# Copyright (C) 2017 Intel Corporation -# Released under the MIT license (see COPYING.MIT) - -from oeqa.core.case import OETestCase - -class ThreadedTest(OETestCase): - def test_threaded_no_depends(self): - self.assertTrue(True, msg='How is this possible?') - -class ThreadedTest2(OETestCase): - def test_threaded_same_module(self): - self.assertTrue(True, msg='How is this possible?')
meta/lib/oeqa/core/tests/cases/loader/threaded/threaded_alone.py deleted +0 -8
@@ -1,8 +0,0 @@ -# Copyright (C) 2017 Intel Corporation -# Released under the MIT license (see COPYING.MIT) - -from oeqa.core.case import OETestCase - -class ThreadedTestAlone(OETestCase): - def test_threaded_alone(self): - self.assertTrue(True, msg='How is this possible?')
meta/lib/oeqa/core/tests/cases/loader/threaded/threaded_depends.py deleted +0 -10
@@ -1,10 +0,0 @@ -# Copyright (C) 2017 Intel Corporation -# Released under the MIT license (see COPYING.MIT) - -from oeqa.core.case import OETestCase -from oeqa.core.decorator.depends import OETestDepends - -class ThreadedTest3(OETestCase): - @OETestDepends(['threaded.ThreadedTest.test_threaded_no_depends']) - def test_threaded_depends(self): - self.assertTrue(True, msg='How is this possible?')
meta/lib/oeqa/core/tests/cases/loader/threaded/threaded_module.py deleted +0 -12
@@ -1,12 +0,0 @@ -# Copyright (C) 2017 Intel Corporation -# Released under the MIT license (see COPYING.MIT) - -from oeqa.core.case import OETestCase - -class ThreadedTestModule(OETestCase): - def test_threaded_module(self): - self.assertTrue(True, msg='How is this possible?') - -class ThreadedTestModule2(OETestCase): - def test_threaded_module2(self): - self.assertTrue(True, msg='How is this possible?')
meta/lib/oeqa/core/tests/common.py +0 -10
@@ -33,13 +33,3 @@ class TestBase(unittest.TestCase): tc.loadTests(self.cases_path, modules=modules, tests=tests, filters=filters) return tc - - def _testLoaderThreaded(self, d={}, modules=[], - tests=[], filters={}): - from oeqa.core.threaded import OETestContextThreaded - - tc = OETestContextThreaded(d, self.logger) - tc.loadTests(self.cases_path, modules=modules, tests=tests, - filters=filters) - - return tc
meta/lib/oeqa/core/tests/test_decorators.py +0 -12
@@ -131,17 +131,5 @@ class TestTimeoutDecorator(TestBase): msg = "OETestTimeout didn't restore SIGALRM" self.assertIs(alarm_signal, signal.getsignal(signal.SIGALRM), msg=msg) - def test_timeout_thread(self): - tests = ['timeout.TimeoutTest.testTimeoutPass'] - msg = 'Failed to run test using OETestTimeout' - tc = self._testLoaderThreaded(modules=self.modules, tests=tests) - self.assertTrue(tc.runTests().wasSuccessful(), msg=msg) - - def test_timeout_threaded_fail(self): - tests = ['timeout.TimeoutTest.testTimeoutFail'] - msg = "OETestTimeout test didn't timeout as expected" - tc = self._testLoaderThreaded(modules=self.modules, tests=tests) - self.assertFalse(tc.runTests().wasSuccessful(), msg=msg) - if __name__ == '__main__': unittest.main()
meta/lib/oeqa/core/tests/test_loader.py +1 -29
@@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (C) 2016-2017 Intel Corporation +# Copyright (C) 2016 Intel Corporation # Released under the MIT license (see COPYING.MIT) import os
@@ -82,33 +82,5 @@ class TestLoader(TestBase): msg = 'Expected modules from two different paths' self.assertEqual(modules, expected_modules, msg=msg) - def test_loader_threaded(self): - cases_path = self.cases_path - - self.cases_path = [os.path.join(self.cases_path, 'loader', 'threaded')] - - tc = self._testLoaderThreaded() - self.assertEqual(len(tc.suites), 3, "Expected to be 3 suites") - - case_ids = ['threaded.ThreadedTest.test_threaded_no_depends', - 'threaded.ThreadedTest2.test_threaded_same_module', - 'threaded_depends.ThreadedTest3.test_threaded_depends'] - for case in tc.suites[0]._tests: - self.assertEqual(case.id(), - case_ids[tc.suites[0]._tests.index(case)]) - - case_ids = ['threaded_alone.ThreadedTestAlone.test_threaded_alone'] - for case in tc.suites[1]._tests: - self.assertEqual(case.id(), - case_ids[tc.suites[1]._tests.index(case)]) - - case_ids = ['threaded_module.ThreadedTestModule.test_threaded_module', - 'threaded_module.ThreadedTestModule2.test_threaded_module2'] - for case in tc.suites[2]._tests: - self.assertEqual(case.id(), - case_ids[tc.suites[2]._tests.index(case)]) - - self.cases_path = cases_path - if __name__ == '__main__': unittest.main()
meta/lib/oeqa/core/threaded.py deleted +0 -275
275 lines not on this page · raw message
meta/lib/oeqa/sdk/context.py +2 -3
@@ -6,10 +6,9 @@ import sys import glob import re -from oeqa.core.context import OETestContextExecutor -from oeqa.core.threaded import OETestContextThreaded +from oeqa.core.context import OETestContext, OETestContextExecutor -class OESDKTestContext(OETestContextThreaded): +class OESDKTestContext(OETestContext): sdk_files_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "files") def __init__(self, td=None, logger=None, sdk_dir=None, sdk_env=None,
Signature · 1 line
2.7.4
PATCH 28/45 oeqa/runner: Simplify code
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
There doesn't appear to be any reason we need this _results indirection any more so remove it.
(From OE-Core rev: b618261811c48ff3b98eab1b340a8cd09ef183c6)
(From OE-Core rev: ab271b49d9b55ea271d519c3a4da0b639a07f0bb)
--- meta/lib/oeqa/core/context.py | 1 - meta/lib/oeqa/core/decorator/depends.py | 4 +--- meta/lib/oeqa/core/runner.py | 15 +++++---------- meta/lib/oeqa/core/tests/test_data.py | 4 ++-- 4 files changed, 8 insertions(+), 16 deletions(-)
meta/lib/oeqa/core/context.py +0 -1
@@ -27,7 +27,6 @@ class OETestContext(object): self.logger = logger self._registry = {} self._registry['cases'] = collections.OrderedDict() - self._results = {} def _read_modules_from_manifest(self, manifest): if not os.path.exists(manifest):
meta/lib/oeqa/core/decorator/depends.py +1 -3
@@ -63,12 +63,10 @@ def _order_test_case_by_depends(cases, depends): return [cases[case_id] for case_id in cases_ordered] def _skipTestDependency(case, depends): - results = case.tc._results - skipReasons = ['errors', 'failures', 'skipped'] for reason in skipReasons: - for test, _ in results[reason]: + for test, _ in getattr(case.tc.results, reason): if test.id() in depends: raise SkipTest("Test case %s depends on %s and was in %s." \ % (case.id(), test.id(), reason))
meta/lib/oeqa/core/runner.py +5 -10
@@ -44,8 +44,10 @@ class OETestResult(_TestResult): self.successes = [] + # Inject into tc so that TestDepends decorator can see results + tc.results = self + self.tc = tc - self._tc_map_results() def startTest(self, test): # Allow us to trigger the testcase buffer mode on a per test basis
@@ -55,13 +57,6 @@ class OETestResult(_TestResult): self.buffer = test.buffer super(OETestResult, self).startTest(test) - def _tc_map_results(self): - self.tc._results['failures'] = self.failures - self.tc._results['errors'] = self.errors - self.tc._results['skipped'] = self.skipped - self.tc._results['expectedFailures'] = self.expectedFailures - self.tc._results['successes'] = self.successes - def logSummary(self, component, context_msg=''): elapsed_time = self.tc._run_end_time - self.tc._run_start_time self.tc.logger.info("SUMMARY:")
@@ -73,7 +68,7 @@ class OETestResult(_TestResult): msg = "%s - OK - All required tests passed" % component else: msg = "%s - FAIL - Required tests failed" % component - skipped = len(self.tc._results['skipped']) + skipped = len(self.skipped) if skipped: msg += " (skipped=%d)" % skipped self.tc.logger.info(msg)
@@ -81,7 +76,7 @@ class OETestResult(_TestResult): def _getDetailsNotPassed(self, case, type, desc): found = False - for (scase, msg) in self.tc._results[type]: + for (scase, msg) in getattr(self, type): # XXX: When XML reporting is enabled scase is # xmlrunner.result._TestInfo instance instead of # string.
meta/lib/oeqa/core/tests/test_data.py +2 -2
@@ -21,7 +21,7 @@ class TestData(TestBase): tc = self._testLoader(modules=self.modules) self.assertEqual(False, tc.runTests().wasSuccessful()) - for test, data in tc._results['errors']: + for test, data in tc.errors: expect = False if expectedException in data: expect = True
@@ -34,7 +34,7 @@ class TestData(TestBase): tc = self._testLoader(d=d, modules=self.modules) self.assertEqual(False, tc.runTests().wasSuccessful()) - for test, data in tc._results['failures']: + for test, data in tc.failures: expect = False if expectedError in data: expect = True
Signature · 1 line
2.7.4
PATCH 29/45 oeqa: Remove xmlrunner
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
This isn't present on modern distros by default and doesn't work with testtools, needing multiple code paths in the code. Remove it in favour of finding a better replacement for results collection/analysis.
(From OE-Core rev: 8001d933a8dc86004db014777f094d718086687d)
(From OE-Core rev: 02449e89d62a7714d30ab6e2e58c476dc9441f7a)
--- meta/lib/oeqa/core/runner.py | 54 ++++++++------------------------------------ 1 file changed, 9 insertions(+), 45 deletions(-)
meta/lib/oeqa/core/runner.py +9 -45
@@ -7,16 +7,8 @@ import unittest import logging import re -xmlEnabled = False -try: - import xmlrunner - from xmlrunner.result import _XMLTestResult as _TestResult - from xmlrunner.runner import XMLTestRunner as _TestRunner - xmlEnabled = True -except ImportError: - # use the base runner instead - from unittest import TextTestResult as _TestResult - from unittest import TextTestRunner as _TestRunner +from unittest import TextTestResult as _TestResult +from unittest import TextTestRunner as _TestRunner class OEStreamLogger(object): def __init__(self, logger):
@@ -77,19 +69,10 @@ class OETestResult(_TestResult): found = False for (scase, msg) in getattr(self, type): - # XXX: When XML reporting is enabled scase is - # xmlrunner.result._TestInfo instance instead of - # string. - if xmlEnabled: - if case.id() == scase.test_id: - found = True - break - scase_str = scase.test_id - else: - if case.id() == scase.id(): - found = True - break - scase_str = str(scase.id()) + if case.id() == scase.id(): + found = True + break + scase_str = str(scase.id()) # When fails at module or class level the class name is passed as string # so figure out to see if match
@@ -156,33 +139,14 @@ class OETestRunner(_TestRunner): streamLoggerClass = OEStreamLogger def __init__(self, tc, *args, **kwargs): - if xmlEnabled: - if not kwargs.get('output'): - kwargs['output'] = os.path.join(os.getcwd(), - 'TestResults_%s_%s' % (time.strftime("%Y%m%d%H%M%S"), os.getpid())) - kwargs['stream'] = self.streamLoggerClass(tc.logger) super(OETestRunner, self).__init__(*args, **kwargs) self.tc = tc self.resultclass = OETestResult - # XXX: The unittest-xml-reporting package defines _make_result method instead - # of _makeResult standard on unittest. - if xmlEnabled: - def _make_result(self): - """ - Creates a TestResult object which will be used to store - information about the executed tests. - """ - # override in subclasses if necessary. - return self.resultclass(self.tc, - self.stream, self.descriptions, self.verbosity, self.elapsed_times - ) - else: - def _makeResult(self): - return self.resultclass(self.tc, self.stream, self.descriptions, - self.verbosity) - + def _makeResult(self): + return self.resultclass(self.tc, self.stream, self.descriptions, + self.verbosity) def _walk_suite(self, suite, func): for obj in suite:
Signature · 1 line
2.7.4
PATCH 30/45 oeqa/core/runner: refactor for OEQA to write json testresult
In reply to Armin KusterFrom: Yeoh Ee Peng <[email protected]>
Refactor the original _getDetailsNotPassed method to return testresult details (test status and log), which will be reused by future OEQA code to write json testresult.
Take the opportunity to consolidate and simplify the logic used to gather test status and log within the TestResult instance.
(From OE-Core rev: 79ee7d1c371a86edeb61c99679985118da657e5d)
(From OE-Core rev: bc444181f9658423856621b2f2c60364642ae5b1)
--- meta/lib/oeqa/core/runner.py | 70 ++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 41 deletions(-)
meta/lib/oeqa/core/runner.py +29 -41
@@ -61,40 +61,43 @@ class OETestResult(_TestResult): else: msg = "%s - FAIL - Required tests failed" % component skipped = len(self.skipped) - if skipped: + if skipped: msg += " (skipped=%d)" % skipped self.tc.logger.info(msg) - def _getDetailsNotPassed(self, case, type, desc): - found = False + def _getTestResultDetails(self, case): + result_types = {'failures': 'FAILED', 'errors': 'ERROR', 'skipped': 'SKIPPED', + 'expectedFailures': 'EXPECTEDFAIL', 'successes': 'PASSED'} - for (scase, msg) in getattr(self, type): - if case.id() == scase.id(): - found = True - break - scase_str = str(scase.id()) - - # When fails at module or class level the class name is passed as string - # so figure out to see if match - m = re.search("^setUpModule \((?P<module_name>.*)\)$", scase_str) - if m: - if case.__class__.__module__ == m.group('module_name'): + for rtype in result_types: + found = False + for (scase, msg) in getattr(self, rtype): + if case.id() == scase.id(): found = True break + scase_str = str(scase.id()) - m = re.search("^setUpClass \((?P<class_name>.*)\)$", scase_str) - if m: - class_name = "%s.%s" % (case.__class__.__module__, - case.__class__.__name__) + # When fails at module or class level the class name is passed as string + # so figure out to see if match + m = re.search("^setUpModule \((?P<module_name>.*)\)$", scase_str) + if m: + if case.__class__.__module__ == m.group('module_name'): + found = True + break - if class_name == m.group('class_name'): - found = True - break + m = re.search("^setUpClass \((?P<class_name>.*)\)$", scase_str) + if m: + class_name = "%s.%s" % (case.__class__.__module__, + case.__class__.__name__) + + if class_name == m.group('class_name'): + found = True + break - if found: - return (found, msg) + if found: + return result_types[rtype], msg - return (found, None) + return 'UNKNOWN', None def addSuccess(self, test): #Added so we can keep track of successes too
@@ -106,17 +109,7 @@ class OETestResult(_TestResult): for case_name in self.tc._registry['cases']: case = self.tc._registry['cases'][case_name] - result_types = ['failures', 'errors', 'skipped', 'expectedFailures', 'successes'] - result_desc = ['FAILED', 'ERROR', 'SKIPPED', 'EXPECTEDFAIL', 'PASSED'] - - fail = False - desc = None - for idx, name in enumerate(result_types): - (fail, msg) = self._getDetailsNotPassed(case, result_types[idx], - result_desc[idx]) - if fail: - desc = result_desc[idx] - break + (status, log) = self._getTestResultDetails(case) oeid = -1 if hasattr(case, 'decorators'):
@@ -124,12 +117,7 @@ class OETestResult(_TestResult): if hasattr(d, 'oeid'): oeid = d.oeid - if fail: - self.tc.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(), - oeid, desc)) - else: - self.tc.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(), - oeid, 'UNKNOWN')) + self.tc.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(), oeid, status)) class OEListTestsResult(object): def wasSuccessful(self):
Signature · 1 line
2.7.4
PATCH 31/45 oeqa/core/runner: write testresult to json files
In reply to Armin KusterFrom: Yeoh Ee Peng <[email protected]>
As part of the solution to replace Testopia to store testresult, OEQA need to output testresult into single json file, where json testresult file will be stored in git repository by the future test-case-management tools.
The json testresult file will store more than one set of results, where each set of results was uniquely identified by the result_id. The result_id would be like "runtime-qemux86-core-image-sato", where it was a runtime test with target machine equal to qemux86 and running on core-image-sato image. The json testresult file will only store the latest test content for a given result_id. The json testresult file contains the configuration (eg. COMMIT, BRANCH, MACHINE, IMAGE), result (eg. PASSED, FAILED, ERROR), test log, and result_id.
Based on the destination json testresult file directory provided, it could have multiple instances of bitbake trying to write json testresult to a single testresult file, using locking a lockfile alongside the results file directory to prevent races.
Also the library class inside this patch will be reused by the future test-case-management tools to write json testresult for manual test case executed.
(From OE-Core rev: 00e03b5004f1eb6d59295544b3a8620504278f51)
(From OE-Core rev: 045511425577ccbe89d8eb91e2a87e385390cabf)
--- meta/lib/oeqa/core/runner.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-)
meta/lib/oeqa/core/runner.py +34 -1
@@ -6,6 +6,7 @@ import time import unittest import logging import re +import json from unittest import TextTestResult as _TestResult from unittest import TextTestRunner as _TestRunner
@@ -104,8 +105,9 @@ class OETestResult(_TestResult): self.successes.append((test, None)) super(OETestResult, self).addSuccess(test) - def logDetails(self): + def logDetails(self, json_file_dir=None, configuration=None, result_id=None): self.tc.logger.info("RESULTS:") + result = {} for case_name in self.tc._registry['cases']: case = self.tc._registry['cases'][case_name]
@@ -118,6 +120,11 @@ class OETestResult(_TestResult): oeid = d.oeid self.tc.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(), oeid, status)) + result[case.id()] = {'status': status, 'log': log} + + if json_file_dir: + tresultjsonhelper = OETestResultJSONHelper() + tresultjsonhelper.dump_testresult_file(json_file_dir, configuration, result_id, result) class OEListTestsResult(object): def wasSuccessful(self):
@@ -230,3 +237,29 @@ class OETestRunner(_TestRunner): self._list_tests_module(suite) return OEListTestsResult() + +class OETestResultJSONHelper(object): + + testresult_filename = 'testresults.json' + + def _get_existing_testresults_if_available(self, write_dir): + testresults = {} + file = os.path.join(write_dir, self.testresult_filename) + if os.path.exists(file): + with open(file, "r") as f: + testresults = json.load(f) + return testresults + + def _write_file(self, write_dir, file_name, file_content): + file_path = os.path.join(write_dir, file_name) + with open(file_path, 'w') as the_file: + the_file.write(file_content) + + def dump_testresult_file(self, write_dir, configuration, result_id, test_result): + bb.utils.mkdirhier(write_dir) + lf = bb.utils.lockfile(os.path.join(write_dir, 'jsontestresult.lock')) + test_results = self._get_existing_testresults_if_available(write_dir) + test_results[result_id] = {'configuration': configuration, 'result': test_result} + json_testresults = json.dumps(test_results, sort_keys=True, indent=4) + self._write_file(write_dir, self.testresult_filename, json_testresults) + bb.utils.unlockfile(lf)
Signature · 1 line
2.7.4
PATCH 32/45 oeqa/runtime/ptest: Inject results+logs into stored json results file
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
This allows the ptest results from ptest-runner, run in an image to be transferred over to the resulting json results output.
Each test is given a pass/skip/fail so individual results can be monitored and the raw log output from the ptest-runner is also dumped into the results json file as this means after the fact debugging becomes much easier.
Currently the log output is not split up per test but that would make a good future enhancement.
I attempted to implement this as python subTests however it failed as the output was too confusing, subTests don't support any kind of log output handling, subTest successes aren't logged and it was making things far more complex than they needed to be.
We mark ptest-runner as "EXPECTEDFAILURE" since its unlikely every ptest will pass currently and we don't want that to fail the whole image test run. Its assumed there would be later analysis of the json output to determine regressions. We do have to change the test runner code so that 'unexpectedsuccess' is not a failure.
Also, the test names are manipuated to remove spaces and brackets with "_" used as a replacement and any duplicate occurrences truncated.
(From OE-Core rev: a13e088942e2a3c3521e98954a394e61a15234e8)
(From OE-Core rev: 526ceab9d0e43f73635bb92e8dd7763ef75ad33b)
--- meta/lib/oeqa/core/runner.py | 8 ++++++++ meta/lib/oeqa/runtime/cases/ptest.py | 21 +++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-)
meta/lib/oeqa/core/runner.py +8 -0
@@ -107,7 +107,11 @@ class OETestResult(_TestResult): def logDetails(self, json_file_dir=None, configuration=None, result_id=None): self.tc.logger.info("RESULTS:") + result = {} + if hasattr(self.tc, "extraresults"): + result = self.tc.extraresults + for case_name in self.tc._registry['cases']: case = self.tc._registry['cases'][case_name]
@@ -126,6 +130,10 @@ class OETestResult(_TestResult): tresultjsonhelper = OETestResultJSONHelper() tresultjsonhelper.dump_testresult_file(json_file_dir, configuration, result_id, result) + def wasSuccessful(self): + # Override as we unexpected successes aren't failures for us + return (len(self.failures) == len(self.errors) == 0) + class OEListTestsResult(object): def wasSuccessful(self): return True
meta/lib/oeqa/runtime/cases/ptest.py +19 -2
@@ -1,3 +1,6 @@ +import unittest +import pprint + from oeqa.runtime.case import OERuntimeTestCase from oeqa.core.decorator.depends import OETestDepends from oeqa.core.decorator.oeid import OETestID
@@ -49,6 +52,7 @@ class PtestRunnerTest(OERuntimeTestCase): @OETestID(1600) @skipIfNotFeature('ptest', 'Test requires ptest to be in DISTRO_FEATURES') @OETestDepends(['ssh.SSHTest.test_ssh']) + @unittest.expectedFailure def test_ptestrunner(self): status, output = self.target.run('which ptest-runner', 0) if status != 0:
@@ -76,6 +80,11 @@ class PtestRunnerTest(OERuntimeTestCase): # status != 0 is OK since some ptest tests may fail self.assertTrue(status != 127, msg="Cannot execute ptest-runner!") + if not hasattr(self.tc, "extraresults"): + self.tc.extraresults = {} + extras = self.tc.extraresults + extras['ptestresult.rawlogs'] = {'log': output} + # Parse and save results parse_result = self.parse_ptest(ptest_runner_log) parse_result.log_as_files(ptest_log_dir, test_status = ['pass','fail', 'skip'])
@@ -84,10 +93,18 @@ class PtestRunnerTest(OERuntimeTestCase): os.remove(ptest_log_dir_link) os.symlink(os.path.basename(ptest_log_dir), ptest_log_dir_link) + trans = str.maketrans("()", "__") + resmap = {'pass': 'PASSED', 'skip': 'SKIPPED', 'fail': 'FAILED'} + for section in parse_result.result_dict: + for test, result in parse_result.result_dict[section]: + testname = "ptestresult." + section + "." + "_".join(test.translate(trans).split()) + extras[testname] = {'status': resmap[result]} + failed_tests = {} for section in parse_result.result_dict: - failed_testcases = [ test for test, result in parse_result.result_dict[section] if result == 'fail' ] + failed_testcases = [ "_".join(test.translate(trans).split()) for test, result in parse_result.result_dict[section] if result == 'fail' ] if failed_testcases: failed_tests[section] = failed_testcases - self.assertFalse(failed_tests, msg = "Failed ptests: %s" %(str(failed_tests))) + if failed_tests: + self.fail("Failed ptests:\n%s" % pprint.pformat(failed_tests))
Signature · 1 line
2.7.4
PATCH 33/45 oeqa/runner: Always show a summary of success/fail/error/skip counts
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Its useful to have the counts of success/failure/error/skipped at the end of the results to allow for easier human reading of what happened.
(From OE-Core rev: 5942318a261ce7a885f351e214669068ff9d8931)
--- meta/lib/oeqa/core/runner.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
meta/lib/oeqa/core/runner.py +1 -3
@@ -61,9 +61,7 @@ class OETestResult(_TestResult): msg = "%s - OK - All required tests passed" % component else: msg = "%s - FAIL - Required tests failed" % component - skipped = len(self.skipped) - if skipped: - msg += " (skipped=%d)" % skipped + msg += " (successes=%d, skipped=%d, failures=%d, errors=%d)" % (len(self.successes), len(self.skipped), len(self.failures), len(self.errors)) self.tc.logger.info(msg) def _getTestResultDetails(self, case):
Signature · 1 line
2.7.4
PATCH 34/45 oeqa/runner: Sort the test result output by result class
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
We want to see failures/errors listed last since this is the most easily visible part of the log on consoles or autobuilder output and makes human processing easier rather than having to scroll up and scan for a single failure.
(From OE-Core rev: 2cc07ab253f1ba6a1f07a66051c9ba6d98cd2357)
--- meta/lib/oeqa/core/runner.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
meta/lib/oeqa/core/runner.py +14 -2
@@ -107,6 +107,7 @@ class OETestResult(_TestResult): self.tc.logger.info("RESULTS:") result = {} + logs = {} if hasattr(self.tc, "extraresults"): result = self.tc.extraresults
@@ -121,8 +122,19 @@ class OETestResult(_TestResult): if hasattr(d, 'oeid'): oeid = d.oeid - self.tc.logger.info("RESULTS - %s - Testcase %s: %s" % (case.id(), oeid, status)) - result[case.id()] = {'status': status, 'log': log} + if status not in logs: + logs[status] = [] + logs[status].append("RESULTS - %s - Testcase %s: %s" % (case.id(), oeid, status)) + if log: + result[case.id()] = {'status': status, 'log': log} + else: + result[case.id()] = {'status': status} + + for i in ['PASSED', 'SKIPPED', 'EXPECTEDFAIL', 'ERROR', 'FAILED', 'UNKNOWN']: + if i not in logs: + continue + for l in logs[i]: + self.tc.logger.info(l) if json_file_dir: tresultjsonhelper = OETestResultJSONHelper()
Signature · 1 line
2.7.4
PATCH 35/45 oeqa/selftest/context: write testresult to json files
In reply to Armin KusterFrom: Yeoh Ee Peng <[email protected]>
As part of the solution to replace Testopia to store testresult, OEQA selftest need to output testresult into json files, where these json testresult files will be stored into git repository by the future test-case-management tools.
By default, json testresult file will be written to "oeqa" directory under the oe-selftest log directory.
To configure multiple instances of bitbake to write json testresult to a single testresult file at custom directory, user will define the variable "OEQA_JSON_RESULT_DIR" with the custom directory for json testresult.
(From OE-Core rev: a95218525a4c8228fff9908ffbda85c6b85e101c)
--- meta/lib/oeqa/selftest/context.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-)
meta/lib/oeqa/selftest/context.py +29 -1
@@ -202,6 +202,31 @@ class OESelftestTestContextExecutor(OETestContextExecutor): self.tc.logger.info("Running bitbake -p") runCmd("bitbake -p") + def _get_json_result_dir(self, args): + json_result_dir = os.path.join(os.path.dirname(os.path.abspath(args.output_log)), 'oeqa') + if "OEQA_JSON_RESULT_DIR" in self.tc.td: + json_result_dir = self.tc.td["OEQA_JSON_RESULT_DIR"] + + return json_result_dir + + def _get_configuration(self, args): + import platform + from oeqa.utils.metadata import metadata_from_bb + metadata = metadata_from_bb() + configuration = {'TEST_TYPE': 'oeselftest', + 'START_TIME': args.test_start_time, + 'MACHINE': self.tc.td["MACHINE"], + 'HOST_DISTRO': ('-'.join(platform.linux_distribution())).replace(' ', '-'), + 'HOST_NAME': metadata['hostname']} + layers = metadata['layers'] + for l in layers: + configuration['%s_BRANCH_REV' % os.path.basename(l)] = '%s:%s' % (metadata['layers'][l]['branch'], + metadata['layers'][l]['commit']) + return configuration + + def _get_result_id(self, configuration): + return '%s_%s_%s' % (configuration['TEST_TYPE'], configuration['HOST_DISTRO'], configuration['MACHINE']) + def _internal_run(self, logger, args): self.module_paths = self._get_cases_paths( self.tc_kwargs['init']['td']['BBPATH'].split(':'))
@@ -218,7 +243,10 @@ class OESelftestTestContextExecutor(OETestContextExecutor): else: self._pre_run() rc = self.tc.runTests(**self.tc_kwargs['run']) - rc.logDetails() + configuration = self._get_configuration(args) + rc.logDetails(self._get_json_result_dir(args), + configuration, + self._get_result_id(configuration)) rc.logSummary(self.name) return rc
Signature · 1 line
2.7.4
PATCH 36/45 testimage.bbclass: write testresult to json files
In reply to Armin KusterFrom: Yeoh Ee Peng <[email protected]>
As part of the solution to replace Testopia to store testresult, OEQA testimage need to output testresult into json files, where these json testresult files will be stored into git repository by the future test-case-management tools.
By default, json testresult file will be written to "oeqa" directory under the "WORKDIR" directory.
To configure multiple instances of bitbake to write json testresult to a single testresult file at custom directory, user will define the variable "OEQA_JSON_RESULT_DIR" with the custom directory for json testresult.
(From OE-Core rev: 2b8b47ec8ee835d2e70cc4ff3ec484f9e4e4d02d)
--- meta/classes/testimage.bbclass | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-)
meta/classes/testimage.bbclass +29 -2
@@ -2,7 +2,7 @@ # # Released under the MIT license (see COPYING.MIT) - +inherit metadata_scm # testimage.bbclass enables testing of qemu images using python unittests. # 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
@@ -132,6 +132,30 @@ def testimage_sanity(d): bb.fatal('When TEST_TARGET is set to "simpleremote" ' 'TEST_TARGET_IP and TEST_SERVER_IP are needed too.') +def _get_testimage_configuration(d, test_type, pid, machine): + import platform + configuration = {'TEST_TYPE': test_type, + 'PROCESS_ID': pid, + 'MACHINE': machine, + 'IMAGE_BASENAME': d.getVar("IMAGE_BASENAME"), + 'IMAGE_PKGTYPE': d.getVar("IMAGE_PKGTYPE"), + 'HOST_DISTRO': ('-'.join(platform.linux_distribution())).replace(' ', '-')} + layers = (d.getVar("BBLAYERS") or "").split() + for l in layers: + configuration['%s_BRANCH_REV' % os.path.basename(l)] = '%s:%s' % (base_get_metadata_git_branch(l, None).strip(), + base_get_metadata_git_revision(l, None)) + return configuration + +def _get_testimage_json_result_dir(d): + json_result_dir = os.path.join(d.getVar("WORKDIR"), 'oeqa') + custom_json_result_dir = d.getVar("OEQA_JSON_RESULT_DIR") + if custom_json_result_dir: + json_result_dir = custom_json_result_dir + return json_result_dir + +def _get_testimage_result_id(configuration): + return '%s_%s_%s' % (configuration['TEST_TYPE'], configuration['IMAGE_BASENAME'], configuration['MACHINE']) + def testimage_main(d): import os import json
@@ -299,7 +323,10 @@ def testimage_main(d): # Show results (if we have them) if not results: bb.fatal('%s - FAILED - tests were interrupted during execution' % pn, forcelog=True) - results.logDetails() + configuration = _get_testimage_configuration(d, 'runtime', os.getpid(), machine) + results.logDetails(_get_testimage_json_result_dir(d), + configuration, + _get_testimage_result_id(configuration)) results.logSummary(pn) if not results.wasSuccessful(): bb.fatal('%s - FAILED - check the task log and the ssh log' % pn, forcelog=True)
Signature · 1 line
2.7.4
PATCH 37/45 testsdk.bbclass: write testresult to json files
In reply to Armin KusterFrom: Yeoh Ee Peng <[email protected]>
As part of the solution to replace Testopia to store testresult, OEQA sdk and sdkext need to output testresult into json files, where these json testresult files will be stored into git repository by the future test-case-management tools.
By default, json testresult file will be written to "oeqa" directory under the "WORKDIR" directory.
To configure multiple instances of bitbake to write json testresult to a single testresult file at custom directory, user will define the variable "OEQA_JSON_RESULT_DIR" with the custom directory for json testresult.
(From OE-Core rev: eefb07907873d20f2e66d3784106f6f72030b5b2)
--- meta/classes/testsdk.bbclass | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-)
meta/classes/testsdk.bbclass +32 -4
@@ -14,6 +14,30 @@ # # where "<image-name>" is an image like core-image-sato. +def _get_sdk_configuration(d, test_type, pid): + import platform + configuration = {'TEST_TYPE': test_type, + 'PROCESS_ID': pid, + 'SDK_MACHINE': d.getVar("SDKMACHINE"), + 'IMAGE_BASENAME': d.getVar("IMAGE_BASENAME"), + 'IMAGE_PKGTYPE': d.getVar("IMAGE_PKGTYPE"), + 'HOST_DISTRO': ('-'.join(platform.linux_distribution())).replace(' ', '-')} + layers = (d.getVar("BBLAYERS") or "").split() + for l in layers: + configuration['%s_BRANCH_REV' % os.path.basename(l)] = '%s:%s' % (base_get_metadata_git_branch(l, None).strip(), + base_get_metadata_git_revision(l, None)) + return configuration + +def _get_sdk_json_result_dir(d): + json_result_dir = os.path.join(d.getVar("WORKDIR"), 'oeqa') + custom_json_result_dir = d.getVar("OEQA_JSON_RESULT_DIR") + if custom_json_result_dir: + json_result_dir = custom_json_result_dir + return json_result_dir + +def _get_sdk_result_id(configuration): + return '%s_%s_%s' % (configuration['TEST_TYPE'], configuration['IMAGE_BASENAME'], configuration['SDK_MACHINE']) + def testsdk_main(d): import os import subprocess
@@ -69,8 +93,10 @@ def testsdk_main(d): component = "%s %s" % (pn, OESDKTestContextExecutor.name) context_msg = "%s:%s" % (os.path.basename(tcname), os.path.basename(sdk_env)) - - result.logDetails() + configuration = _get_sdk_configuration(d, 'sdk', os.getpid()) + result.logDetails(_get_sdk_json_result_dir(d), + configuration, + _get_sdk_result_id(configuration)) result.logSummary(component, context_msg) if not result.wasSuccessful():
@@ -172,8 +198,10 @@ def testsdkext_main(d): component = "%s %s" % (pn, OESDKExtTestContextExecutor.name) context_msg = "%s:%s" % (os.path.basename(tcname), os.path.basename(sdk_env)) - - result.logDetails() + configuration = _get_sdk_configuration(d, 'sdkext', os.getpid()) + result.logDetails(_get_sdk_json_result_dir(d), + configuration, + _get_sdk_result_id(configuration)) result.logSummary(component, context_msg) if not result.wasSuccessful():
Signature · 1 line
2.7.4
PATCH 38/45 oeqa/selftest: Improvements to the json logging
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Tweak the preceeding commit to:
* Add STARTTIME to the identifier to make it unique
* Use LOG_DIR
* Store the layer config in a more natural json format
* Drop '_' function prefixes
(From OE-Core rev: 173f59acf9722e2ef27fdd49c20f7d3d664917eb)
(From OE-Core rev: 3b69099edc7db99c11bfb41eab2af50bd0e3d4f2)
--- meta/lib/oeqa/selftest/context.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-)
meta/lib/oeqa/selftest/context.py +11 -14
@@ -202,30 +202,27 @@ class OESelftestTestContextExecutor(OETestContextExecutor): self.tc.logger.info("Running bitbake -p") runCmd("bitbake -p") - def _get_json_result_dir(self, args): - json_result_dir = os.path.join(os.path.dirname(os.path.abspath(args.output_log)), 'oeqa') + def get_json_result_dir(self, args): + json_result_dir = os.path.join(os.path.dirname(os.path.abspath(args.output_log)), 'log', 'oeqa') if "OEQA_JSON_RESULT_DIR" in self.tc.td: json_result_dir = self.tc.td["OEQA_JSON_RESULT_DIR"] return json_result_dir - def _get_configuration(self, args): + def get_configuration(self, args): import platform from oeqa.utils.metadata import metadata_from_bb metadata = metadata_from_bb() configuration = {'TEST_TYPE': 'oeselftest', - 'START_TIME': args.test_start_time, + 'STARTTIME': args.test_start_time, 'MACHINE': self.tc.td["MACHINE"], 'HOST_DISTRO': ('-'.join(platform.linux_distribution())).replace(' ', '-'), - 'HOST_NAME': metadata['hostname']} - layers = metadata['layers'] - for l in layers: - configuration['%s_BRANCH_REV' % os.path.basename(l)] = '%s:%s' % (metadata['layers'][l]['branch'], - metadata['layers'][l]['commit']) + 'HOST_NAME': metadata['hostname'], + 'LAYERS': metadata['layers']} return configuration - def _get_result_id(self, configuration): - return '%s_%s_%s' % (configuration['TEST_TYPE'], configuration['HOST_DISTRO'], configuration['MACHINE']) + def get_result_id(self, configuration): + return '%s_%s_%s_%s' % (configuration['TEST_TYPE'], configuration['HOST_DISTRO'], configuration['MACHINE'], configuration['STARTTIME']) def _internal_run(self, logger, args): self.module_paths = self._get_cases_paths(
@@ -243,10 +240,10 @@ class OESelftestTestContextExecutor(OETestContextExecutor): else: self._pre_run() rc = self.tc.runTests(**self.tc_kwargs['run']) - configuration = self._get_configuration(args) - rc.logDetails(self._get_json_result_dir(args), + configuration = self.get_configuration(args) + rc.logDetails(self.get_json_result_dir(args), configuration, - self._get_result_id(configuration)) + self.get_result_id(configuration)) rc.logSummary(self.name) return rc
Signature · 1 line
2.7.4
PATCH 39/45 oeqa/selftest: Standardize json logging output directory
In reply to Armin KusterFrom: Yeoh Ee Peng <[email protected]>
Currently sdk & sdkext will output json file to LOG_DIR, while selftest will output json file to TOPDIR/log.
Standardize selftest json output file to LOG_DIR.
(From OE-Core rev: 9f2e39684cbbe9f87eeef6a81961e6db783439e3)
--- meta/lib/oeqa/selftest/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
meta/lib/oeqa/selftest/context.py +1 -1
@@ -203,7 +203,7 @@ class OESelftestTestContextExecutor(OETestContextExecutor): runCmd("bitbake -p") def get_json_result_dir(self, args): - json_result_dir = os.path.join(os.path.dirname(os.path.abspath(args.output_log)), 'log', 'oeqa') + json_result_dir = os.path.join(self.tc.td["LOG_DIR"], 'oeqa') if "OEQA_JSON_RESULT_DIR" in self.tc.td: json_result_dir = self.tc.td["OEQA_JSON_RESULT_DIR"]
Signature · 1 line
2.7.4
PATCH 40/45 oeqa/utils/metadata: Allow to function without the git module
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
The python git module may or may not be enabled, allow this code to function without it, falling back to the same method as metadata_scm.bbclass uses. This will be cleaned up in the next round of feature development.
(From OE-Core rev: 6350586ba9f4a4107a2d457590824cd4d662d5b9)
(From OE-Core rev: 32c9169b76e13e53b6a9ab4a59932cea7863d992)
--- meta/lib/oeqa/utils/metadata.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
meta/lib/oeqa/utils/metadata.py +15 -2
@@ -58,9 +58,22 @@ def metadata_from_data_store(d): def git_rev_info(path): """Get git revision information as a dict""" - from git import Repo, InvalidGitRepositoryError, NoSuchPathError - info = OrderedDict() + + try: + from git import Repo, InvalidGitRepositoryError, NoSuchPathError + except ImportError: + import subprocess + try: + info['branch'] = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=path).decode('utf-8').strip() + except subprocess.CalledProcessError: + pass + try: + info['commit'] = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=path).decode('utf-8').strip() + except subprocess.CalledProcessError: + pass + return info + try: repo = Repo(path, search_parent_directories=True) except (InvalidGitRepositoryError, NoSuchPathError):
Signature · 1 line
2.7.4
PATCH 41/45 image-buildinfo, oeqa/selftest/containerimage: Ensure image-buildinfo doesn't break tests
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Having image-buildinfo enabled causes containerimage.ContainerImageTests.test_expected_files to fail due to the presence of an unexpected file:
['./',
'./etc/',
- './etc/build',
'./etc/default/',
'./etc/default/postinst',
Tweak the class to allow it to be disabled and disable it from the test just in case it was enabled.
(From OE-Core rev: af67bf422a4df5b7e07894512ff73a5f493682ab)
(From OE-Core rev: f49ab8b1610c045acaed7b964d12f07f969df856)
--- meta/classes/image-buildinfo.bbclass | 2 ++ meta/lib/oeqa/selftest/cases/containerimage.py | 1 + 2 files changed, 3 insertions(+)
meta/classes/image-buildinfo.bbclass +2 -0
@@ -64,6 +64,8 @@ def buildinfo_target(d): # Write build information to target filesystem python buildinfo () { + if not d.getVar('IMAGE_BUILDINFO_FILE'): + return with open(d.expand('${IMAGE_ROOTFS}${IMAGE_BUILDINFO_FILE}'), 'w') as build: build.writelines(( '''-----------------------
meta/lib/oeqa/selftest/cases/containerimage.py +1 -0
@@ -39,6 +39,7 @@ class ContainerImageTests(OESelftestTestCase): IMAGE_FSTYPES = "container" PACKAGE_CLASSES = "package_ipk" IMAGE_FEATURES = "" +IMAGE_BUILDINFO_FILE = "" """) bbvars = get_bb_vars(['bindir', 'sysconfdir', 'localstatedir',
Signature · 1 line
2.7.4
PATCH 42/45 oeqa/selftest/esdk: Ensure parent directory exists
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
INFO - ======================================================================
INFO - ERROR: setUpClass (eSDK.oeSDKExtSelfTest)
INFO - ----------------------------------------------------------------------
INFO - Traceback (most recent call last):
File "/home/pokybuild/yocto-worker/oe-selftest-debian/build/meta/lib/oeqa/core/case.py", line 32, in _oeSetUpClass
clss.setUpClassMethod()
File "/home/pokybuild/yocto-worker/oe-selftest-debian/build/meta/lib/oeqa/selftest/cases/eSDK.py", line 76, in setUpClass
cls.tmpdirobj = tempfile.TemporaryDirectory(prefix="selftest-esdk-", dir=bb_vars["WORKDIR"])
File "/usr/lib/python3.5/tempfile.py", line 929, in __init__
self.name = mkdtemp(suffix, prefix, dir)
File "/usr/lib/python3.5/tempfile.py", line 507, in mkdtemp
_os.mkdir(file, 0o700)
FileNotFoundError: [Errno 2] No such file or directory: '/home/pokybuild/yocto-worker/oe-selftest-debian/build/build/tmp/work/qemux86_64-poky-linux/core-image-minimal/1.0-r0/selftest-esdk-q7ln84gc'
(From OE-Core rev: 170a601a99836d13b69e5287bee0d3e71983dd46)
--- meta/lib/oeqa/selftest/cases/eSDK.py | 1 + 1 file changed, 1 insertion(+)
meta/lib/oeqa/selftest/cases/eSDK.py +1 -0
@@ -73,6 +73,7 @@ CORE_IMAGE_EXTRA_INSTALL = "perl" cls.image = 'core-image-minimal' bb_vars = get_bb_vars(['SSTATE_DIR', 'WORKDIR'], cls.image) + bb.utils.mkdirhier(bb_vars["WORKDIR"]) cls.tmpdirobj = tempfile.TemporaryDirectory(prefix="selftest-esdk-", dir=bb_vars["WORKDIR"]) cls.tmpdir_eSDKQA = cls.tempdirobj.name
Signature · 1 line
2.7.4
PATCH 43/45 default-versions.inc: Make PREFERRED_VERSION_openssl* overwritable
In reply to Armin KusterFrom: Stefan Lendl <[email protected]>
defaultsetup.conf and therefore default-versions.inc is sourced pretty late in bitbake.conf. default-versions.inc overwrites previous assignments of PREFERRED_VERSION_openssl. Assigning it with ?= allows other .conf files to assign correctly.
in particular assignment in conf/local.conf and machine config is used instead of default.
(From OE-Core rev: c9786adf81434e2d58247f55cf80fdd3131121e7)
--- meta/conf/distro/include/default-versions.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
meta/conf/distro/include/default-versions.inc +3 -3
@@ -2,6 +2,6 @@ # Default preferred versions # -PREFERRED_VERSION_openssl = "1.0.%" -PREFERRED_VERSION_openssl-native = "1.0.%" -PREFERRED_VERSION_nativesdk-openssl = "1.0.%" +PREFERRED_VERSION_openssl ?= "1.0.%" +PREFERRED_VERSION_openssl-native ?= "1.0.%" +PREFERRED_VERSION_nativesdk-openssl ?= "1.0.%"
Signature · 1 line
2.7.4
PATCH 44/45 oeqa/selftest/esdk: Fix typo causing test failure
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
2018-12-06 23:19:24,564 - oe-selftest - INFO - Traceback (most recent call last):
File "/media/build1/poky-sumo/meta/lib/oeqa/core/case.py", line 32, in _oeSetUpClass
clss.setUpClassMethod()
File "/media/build1/poky-sumo/meta/lib/oeqa/selftest/cases/eSDK.py", line 78, in setUpClass
cls.tmpdir_eSDKQA = cls.tempdirobj.nameAttributeError: type object 'oeSDKExtSelfTest' has no attribute 'tempdirobj'
(From OE-Core rev: 2c60908a2039d333a9fe2651622750ff6ed4cce1)
--- meta/lib/oeqa/selftest/cases/eSDK.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
meta/lib/oeqa/selftest/cases/eSDK.py +1 -1
@@ -75,7 +75,7 @@ CORE_IMAGE_EXTRA_INSTALL = "perl" bb_vars = get_bb_vars(['SSTATE_DIR', 'WORKDIR'], cls.image) bb.utils.mkdirhier(bb_vars["WORKDIR"]) cls.tmpdirobj = tempfile.TemporaryDirectory(prefix="selftest-esdk-", dir=bb_vars["WORKDIR"]) - cls.tmpdir_eSDKQA = cls.tempdirobj.name + cls.tmpdir_eSDKQA = cls.tmpdirobj.name oeSDKExtSelfTest.generate_eSDK(cls.image)
Signature · 1 line
2.7.4
PATCH 45/45 busybox: Put klogd/syslogd alternative links in syslog package
In reply to Armin KusterFrom: Richard Purdie <[email protected]>
Currently these are in ${PN} and ${PN}-syslog may get replaced by other packages but update-alternatives would error in the postinst if other files were installed first. Avoid the problems by putting the links in the correct package.
(From OE-Core rev: ef11c54ba99af261a70ec31091216cdd1556da24)
--- meta/recipes-core/busybox/busybox.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
meta/recipes-core/busybox/busybox.inc +4 -1
@@ -368,7 +368,10 @@ python do_package_prepend () { # Match coreutils if alt_name == '[': alt_name = 'lbracket' - d.appendVar('ALTERNATIVE_%s' % (pn), ' ' + alt_name) + if alt_name == 'klogd' or alt_name == 'syslogd': + d.appendVar('ALTERNATIVE_%s-syslog' % (pn), ' ' + alt_name) + else: + d.appendVar('ALTERNATIVE_%s' % (pn), ' ' + alt_name) d.setVarFlag('ALTERNATIVE_LINK_NAME', alt_name, alt_link_name) if os.path.exists('%s%s' % (dvar, target)): d.setVarFlag('ALTERNATIVE_TARGET', alt_name, target)
Signature · 1 line
2.7.4
✗ patchtest: failure for Sumo-next patches for review
In reply to Armin Kuster== Series Details ==
Series: Sumo-next patches for review
Revision: 1
URL : https://patchwork.openembedded.org/series/15331/
State : failure
== Summary ==
Thank you for submitting this patch series to OpenEmbedded Core. This is an automated response. Several tests have been executed on the proposed series by patchtest resulting in the following failures:
* Issue Series does not apply on top of target branch [test_series_merge_on_head]
Suggested fix Rebase your series on top of targeted branch
Targeted branch master (currently at 6d666b0413)
If you believe any of these test results are incorrect, please reply to the mailing list ([email protected]) raising your concerns. Otherwise we would appreciate you correcting the issues and submitting a new version of the patchset if applicable. Please ensure you add/increment the version number when sending the new version (i.e. [PATCH] -> [PATCH v2] -> [PATCH v3] -> ...).
---
Guidelines: https://www.openembedded.org/wiki/Commit_Patch_Message_Guidelines Test framework: http://git.yoctoproject.org/cgit/cgit.cgi/patchtest Test suite: http://git.yoctoproject.org/cgit/cgit.cgi/patchtest-oe