aboutsummaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/selftest/cases
diff options
context:
space:
mode:
authorLeonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com>2017-05-12 14:40:21 -0700
committerRichard Purdie <richard.purdie@linuxfoundation.org>2017-06-05 17:59:40 +0100
commitddbbefdd124604d10bd47dd0266b55a764fcc0ab (patch)
treebf787bf3a23a035b472a42c0b899758ded848c28 /meta/lib/oeqa/selftest/cases
parent3b2a20eee4a39f40287bf67545839eaa09fc892d (diff)
downloadopenembedded-core-contrib-ddbbefdd124604d10bd47dd0266b55a764fcc0ab.tar.gz
oeqa/selftest/cases: Migrate test cases into the new oe-qa framework
New framework has different classes/decorators so adapt current test cases to support these. Changes include changes on base classes and decorators. Also include paths in selftest/__init__.py isn't needed because the loader is the standard unittest one. Signed-off-by: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com> Signed-off-by: Aníbal Limón <anibal.limon@linux.intel.com>
Diffstat (limited to 'meta/lib/oeqa/selftest/cases')
-rw-r--r--meta/lib/oeqa/selftest/cases/_sstatetests_noauto.py92
-rw-r--r--meta/lib/oeqa/selftest/cases/archiver.py41
-rw-r--r--meta/lib/oeqa/selftest/cases/bblayers.py97
-rw-r--r--meta/lib/oeqa/selftest/cases/bbtests.py279
-rw-r--r--meta/lib/oeqa/selftest/cases/buildhistory.py46
-rw-r--r--meta/lib/oeqa/selftest/cases/buildoptions.py184
-rw-r--r--meta/lib/oeqa/selftest/cases/containerimage.py83
-rw-r--r--meta/lib/oeqa/selftest/cases/devtool.py1696
-rw-r--r--meta/lib/oeqa/selftest/cases/eSDK.py111
-rw-r--r--meta/lib/oeqa/selftest/cases/image_typedep.py51
-rw-r--r--meta/lib/oeqa/selftest/cases/imagefeatures.py125
-rw-r--r--meta/lib/oeqa/selftest/cases/layerappend.py95
-rw-r--r--meta/lib/oeqa/selftest/cases/liboe.py98
-rw-r--r--meta/lib/oeqa/selftest/cases/lic_checksum.py35
-rw-r--r--meta/lib/oeqa/selftest/cases/manifest.py166
-rw-r--r--meta/lib/oeqa/selftest/cases/oelib/__init__.py0
-rw-r--r--meta/lib/oeqa/selftest/cases/oelib/buildhistory.py88
-rw-r--r--meta/lib/oeqa/selftest/cases/oelib/elf.py21
-rw-r--r--meta/lib/oeqa/selftest/cases/oelib/license.py68
-rw-r--r--meta/lib/oeqa/selftest/cases/oelib/path.py89
-rw-r--r--meta/lib/oeqa/selftest/cases/oelib/types.py50
-rw-r--r--meta/lib/oeqa/selftest/cases/oelib/utils.py51
-rw-r--r--meta/lib/oeqa/selftest/cases/oescripts.py15
-rw-r--r--meta/lib/oeqa/selftest/cases/package.py80
-rw-r--r--meta/lib/oeqa/selftest/cases/pkgdata.py224
-rw-r--r--meta/lib/oeqa/selftest/cases/prservice.py131
-rw-r--r--meta/lib/oeqa/selftest/cases/recipetool.py694
-rw-r--r--meta/lib/oeqa/selftest/cases/runqemu.py141
-rw-r--r--meta/lib/oeqa/selftest/cases/runtime_test.py239
-rw-r--r--meta/lib/oeqa/selftest/cases/signing.py184
-rw-r--r--meta/lib/oeqa/selftest/cases/sstate.py63
-rw-r--r--meta/lib/oeqa/selftest/cases/sstatetests.py479
-rw-r--r--meta/lib/oeqa/selftest/cases/tinfoil.py189
-rw-r--r--meta/lib/oeqa/selftest/cases/wic.py793
34 files changed, 6798 insertions, 0 deletions
diff --git a/meta/lib/oeqa/selftest/cases/_sstatetests_noauto.py b/meta/lib/oeqa/selftest/cases/_sstatetests_noauto.py
new file mode 100644
index 0000000000..0e5896234c
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/_sstatetests_noauto.py
@@ -0,0 +1,92 @@
+import os
+import shutil
+
+import oeqa.utils.ftools as ftools
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer
+from oeqa.selftest.cases.sstate import SStateBase
+
+
+class RebuildFromSState(SStateBase):
+
+ @classmethod
+ def setUpClass(self):
+ super(RebuildFromSState, self).setUpClass()
+ self.builddir = os.path.join(os.environ.get('BUILDDIR'))
+
+ def get_dep_targets(self, primary_targets):
+ found_targets = []
+ bitbake("-g " + ' '.join(map(str, primary_targets)))
+ with open(os.path.join(self.builddir, 'pn-buildlist'), 'r') as pnfile:
+ found_targets = pnfile.read().splitlines()
+ return found_targets
+
+ def configure_builddir(self, builddir):
+ os.mkdir(builddir)
+ self.track_for_cleanup(builddir)
+ os.mkdir(os.path.join(builddir, 'conf'))
+ shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/local.conf'), os.path.join(builddir, 'conf/local.conf'))
+ config = {}
+ config['default_sstate_dir'] = "SSTATE_DIR ?= \"${TOPDIR}/sstate-cache\""
+ config['null_sstate_mirrors'] = "SSTATE_MIRRORS = \"\""
+ config['default_tmp_dir'] = "TMPDIR = \"${TOPDIR}/tmp\""
+ for key in config:
+ ftools.append_file(os.path.join(builddir, 'conf/selftest.inc'), config[key])
+ shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/bblayers.conf'), os.path.join(builddir, 'conf/bblayers.conf'))
+ try:
+ shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/auto.conf'), os.path.join(builddir, 'conf/auto.conf'))
+ except:
+ pass
+
+ def hardlink_tree(self, src, dst):
+ os.mkdir(dst)
+ self.track_for_cleanup(dst)
+ for root, dirs, files in os.walk(src):
+ if root == src:
+ continue
+ os.mkdir(os.path.join(dst, root.split(src)[1][1:]))
+ for sstate_file in files:
+ os.link(os.path.join(root, sstate_file), os.path.join(dst, root.split(src)[1][1:], sstate_file))
+
+ def run_test_sstate_rebuild(self, primary_targets, relocate=False, rebuild_dependencies=False):
+ buildA = os.path.join(self.builddir, 'buildA')
+ if relocate:
+ buildB = os.path.join(self.builddir, 'buildB')
+ else:
+ buildB = buildA
+
+ if rebuild_dependencies:
+ rebuild_targets = self.get_dep_targets(primary_targets)
+ else:
+ rebuild_targets = primary_targets
+
+ self.configure_builddir(buildA)
+ runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildA)) + 'bitbake ' + ' '.join(map(str, primary_targets)), shell=True, executable='/bin/bash')
+ self.hardlink_tree(os.path.join(buildA, 'sstate-cache'), os.path.join(self.builddir, 'sstate-cache-buildA'))
+ shutil.rmtree(buildA)
+
+ failed_rebuild = []
+ failed_cleansstate = []
+ for target in rebuild_targets:
+ self.configure_builddir(buildB)
+ self.hardlink_tree(os.path.join(self.builddir, 'sstate-cache-buildA'), os.path.join(buildB, 'sstate-cache'))
+
+ result_cleansstate = runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildB)) + 'bitbake -ccleansstate ' + target, ignore_status=True, shell=True, executable='/bin/bash')
+ if not result_cleansstate.status == 0:
+ failed_cleansstate.append(target)
+ shutil.rmtree(buildB)
+ continue
+
+ result_build = runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildB)) + 'bitbake ' + target, ignore_status=True, shell=True, executable='/bin/bash')
+ if not result_build.status == 0:
+ failed_rebuild.append(target)
+
+ shutil.rmtree(buildB)
+
+ self.assertFalse(failed_rebuild, msg="The following recipes have failed to rebuild: %s" % ' '.join(map(str, failed_rebuild)))
+ self.assertFalse(failed_cleansstate, msg="The following recipes have failed cleansstate(all others have passed both cleansstate and rebuild from sstate tests): %s" % ' '.join(map(str, failed_cleansstate)))
+
+ def test_sstate_relocation(self):
+ self.run_test_sstate_rebuild(['core-image-sato-sdk'], relocate=True, rebuild_dependencies=True)
+
+ def test_sstate_rebuild(self):
+ self.run_test_sstate_rebuild(['core-image-sato-sdk'], relocate=False, rebuild_dependencies=True)
diff --git a/meta/lib/oeqa/selftest/cases/archiver.py b/meta/lib/oeqa/selftest/cases/archiver.py
new file mode 100644
index 0000000000..70c7282f22
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/archiver.py
@@ -0,0 +1,41 @@
+import os
+import glob
+from oeqa.utils.commands import bitbake, get_bb_vars
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.core.decorator.oeid import OETestID
+
+class Archiver(OESelftestTestCase):
+
+ @OETestID(1345)
+ def test_archiver_allows_to_filter_on_recipe_name(self):
+ """
+ Summary: The archiver should offer the possibility to filter on the recipe. (#6929)
+ Expected: 1. Included recipe (busybox) should be included
+ 2. Excluded recipe (zlib) should be excluded
+ Product: oe-core
+ Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ """
+
+ include_recipe = 'busybox'
+ exclude_recipe = 'zlib'
+
+ features = 'INHERIT += "archiver"\n'
+ features += 'ARCHIVER_MODE[src] = "original"\n'
+ features += 'COPYLEFT_PN_INCLUDE = "%s"\n' % include_recipe
+ features += 'COPYLEFT_PN_EXCLUDE = "%s"\n' % exclude_recipe
+ self.write_config(features)
+
+ bitbake('-c clean %s %s' % (include_recipe, exclude_recipe))
+ bitbake("%s %s" % (include_recipe, exclude_recipe))
+
+ bb_vars = get_bb_vars(['DEPLOY_DIR_SRC', 'TARGET_SYS'])
+ src_path = os.path.join(bb_vars['DEPLOY_DIR_SRC'], bb_vars['TARGET_SYS'])
+
+ # Check that include_recipe was included
+ included_present = len(glob.glob(src_path + '/%s-*' % include_recipe))
+ self.assertTrue(included_present, 'Recipe %s was not included.' % include_recipe)
+
+ # Check that exclude_recipe was excluded
+ excluded_present = len(glob.glob(src_path + '/%s-*' % exclude_recipe))
+ self.assertFalse(excluded_present, 'Recipe %s was not excluded.' % exclude_recipe)
diff --git a/meta/lib/oeqa/selftest/cases/bblayers.py b/meta/lib/oeqa/selftest/cases/bblayers.py
new file mode 100644
index 0000000000..90a2249b08
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/bblayers.py
@@ -0,0 +1,97 @@
+import os
+import re
+
+import oeqa.utils.ftools as ftools
+from oeqa.utils.commands import runCmd, get_bb_var
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.core.decorator.oeid import OETestID
+
+class BitbakeLayers(OESelftestTestCase):
+
+ @OETestID(756)
+ def test_bitbakelayers_showcrossdepends(self):
+ result = runCmd('bitbake-layers show-cross-depends')
+ self.assertTrue('aspell' in result.output, msg = "No dependencies were shown. bitbake-layers show-cross-depends output: %s" % result.output)
+
+ @OETestID(83)
+ def test_bitbakelayers_showlayers(self):
+ result = runCmd('bitbake-layers show-layers')
+ self.assertTrue('meta-selftest' in result.output, msg = "No layers were shown. bitbake-layers show-layers output: %s" % result.output)
+
+ @OETestID(93)
+ def test_bitbakelayers_showappends(self):
+ recipe = "xcursor-transparent-theme"
+ bb_file = self.get_recipe_basename(recipe)
+ result = runCmd('bitbake-layers show-appends')
+ self.assertTrue(bb_file in result.output, msg="%s file was not recognised. bitbake-layers show-appends output: %s" % (bb_file, result.output))
+
+ @OETestID(90)
+ def test_bitbakelayers_showoverlayed(self):
+ result = runCmd('bitbake-layers show-overlayed')
+ self.assertTrue('aspell' in result.output, msg="aspell overlayed recipe was not recognised bitbake-layers show-overlayed %s" % result.output)
+
+ @OETestID(95)
+ def test_bitbakelayers_flatten(self):
+ recipe = "xcursor-transparent-theme"
+ recipe_path = "recipes-graphics/xcursor-transparent-theme"
+ recipe_file = self.get_recipe_basename(recipe)
+ testoutdir = os.path.join(self.builddir, 'test_bitbakelayers_flatten')
+ self.assertFalse(os.path.isdir(testoutdir), msg = "test_bitbakelayers_flatten should not exist at this point in time")
+ self.track_for_cleanup(testoutdir)
+ result = runCmd('bitbake-layers flatten %s' % testoutdir)
+ bb_file = os.path.join(testoutdir, recipe_path, recipe_file)
+ self.assertTrue(os.path.isfile(bb_file), msg = "Cannot find xcursor-transparent-theme_0.1.1.bb in the test_bitbakelayers_flatten local dir.")
+ contents = ftools.read_file(bb_file)
+ find_in_contents = re.search("##### bbappended from meta-selftest #####\n(.*\n)*include test_recipe.inc", contents)
+ self.assertTrue(find_in_contents, msg = "Flattening layers did not work. bitbake-layers flatten output: %s" % result.output)
+
+ @OETestID(1195)
+ def test_bitbakelayers_add_remove(self):
+ test_layer = os.path.join(get_bb_var('COREBASE'), 'meta-skeleton')
+ result = runCmd('bitbake-layers show-layers')
+ self.assertNotIn('meta-skeleton', result.output, "This test cannot run with meta-skeleton in bblayers.conf. bitbake-layers show-layers output: %s" % result.output)
+ result = runCmd('bitbake-layers add-layer %s' % test_layer)
+ result = runCmd('bitbake-layers show-layers')
+ self.assertIn('meta-skeleton', result.output, msg = "Something wrong happened. meta-skeleton layer was not added to conf/bblayers.conf. bitbake-layers show-layers output: %s" % result.output)
+ result = runCmd('bitbake-layers remove-layer %s' % test_layer)
+ result = runCmd('bitbake-layers show-layers')
+ self.assertNotIn('meta-skeleton', result.output, msg = "meta-skeleton should have been removed at this step. bitbake-layers show-layers output: %s" % result.output)
+ result = runCmd('bitbake-layers add-layer %s' % test_layer)
+ result = runCmd('bitbake-layers show-layers')
+ self.assertIn('meta-skeleton', result.output, msg = "Something wrong happened. meta-skeleton layer was not added to conf/bblayers.conf. bitbake-layers show-layers output: %s" % result.output)
+ result = runCmd('bitbake-layers remove-layer */meta-skeleton')
+ result = runCmd('bitbake-layers show-layers')
+ self.assertNotIn('meta-skeleton', result.output, msg = "meta-skeleton should have been removed at this step. bitbake-layers show-layers output: %s" % result.output)
+
+ @OETestID(1384)
+ def test_bitbakelayers_showrecipes(self):
+ result = runCmd('bitbake-layers show-recipes')
+ self.assertIn('aspell:', result.output)
+ self.assertIn('mtd-utils:', result.output)
+ self.assertIn('core-image-minimal:', result.output)
+ result = runCmd('bitbake-layers show-recipes mtd-utils')
+ self.assertIn('mtd-utils:', result.output)
+ self.assertNotIn('aspell:', result.output)
+ result = runCmd('bitbake-layers show-recipes -i image')
+ self.assertIn('core-image-minimal', result.output)
+ self.assertNotIn('mtd-utils:', result.output)
+ result = runCmd('bitbake-layers show-recipes -i cmake,pkgconfig')
+ self.assertIn('libproxy:', result.output)
+ self.assertNotIn('mtd-utils:', result.output) # doesn't inherit either
+ self.assertNotIn('wget:', result.output) # doesn't inherit cmake
+ self.assertNotIn('waffle:', result.output) # doesn't inherit pkgconfig
+ result = runCmd('bitbake-layers show-recipes -i nonexistentclass', ignore_status=True)
+ self.assertNotEqual(result.status, 0, 'bitbake-layers show-recipes -i nonexistentclass should have failed')
+ self.assertIn('ERROR:', result.output)
+
+ def get_recipe_basename(self, recipe):
+ recipe_file = ""
+ result = runCmd("bitbake-layers show-recipes -f %s" % recipe)
+ for line in result.output.splitlines():
+ if recipe in line:
+ recipe_file = line
+ break
+
+ self.assertTrue(os.path.isfile(recipe_file), msg = "Can't find recipe file for %s" % recipe)
+ return os.path.basename(recipe_file)
diff --git a/meta/lib/oeqa/selftest/cases/bbtests.py b/meta/lib/oeqa/selftest/cases/bbtests.py
new file mode 100644
index 0000000000..4c82049032
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/bbtests.py
@@ -0,0 +1,279 @@
+import os
+import re
+
+import oeqa.utils.ftools as ftools
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.core.decorator.oeid import OETestID
+
+class BitbakeTests(OESelftestTestCase):
+
+ def getline(self, res, line):
+ for l in res.output.split('\n'):
+ if line in l:
+ return l
+
+ @OETestID(789)
+ def test_run_bitbake_from_dir_1(self):
+ os.chdir(os.path.join(self.builddir, 'conf'))
+ self.assertEqual(bitbake('-e').status, 0, msg = "bitbake couldn't run from \"conf\" dir")
+
+ @OETestID(790)
+ def test_run_bitbake_from_dir_2(self):
+ my_env = os.environ.copy()
+ my_env['BBPATH'] = my_env['BUILDDIR']
+ os.chdir(os.path.dirname(os.environ['BUILDDIR']))
+ self.assertEqual(bitbake('-e', env=my_env).status, 0, msg = "bitbake couldn't run from builddir")
+
+ @OETestID(806)
+ def test_event_handler(self):
+ self.write_config("INHERIT += \"test_events\"")
+ result = bitbake('m4-native')
+ find_build_started = re.search("NOTE: Test for bb\.event\.BuildStarted(\n.*)*NOTE: Executing RunQueue Tasks", result.output)
+ find_build_completed = re.search("Tasks Summary:.*(\n.*)*NOTE: Test for bb\.event\.BuildCompleted", result.output)
+ self.assertTrue(find_build_started, msg = "Match failed in:\n%s" % result.output)
+ self.assertTrue(find_build_completed, msg = "Match failed in:\n%s" % result.output)
+ self.assertFalse('Test for bb.event.InvalidEvent' in result.output, msg = "\"Test for bb.event.InvalidEvent\" message found during bitbake process. bitbake output: %s" % result.output)
+
+ @OETestID(103)
+ def test_local_sstate(self):
+ bitbake('m4-native')
+ bitbake('m4-native -cclean')
+ result = bitbake('m4-native')
+ find_setscene = re.search("m4-native.*do_.*_setscene", result.output)
+ self.assertTrue(find_setscene, msg = "No \"m4-native.*do_.*_setscene\" message found during bitbake m4-native. bitbake output: %s" % result.output )
+
+ @OETestID(105)
+ def test_bitbake_invalid_recipe(self):
+ result = bitbake('-b asdf', ignore_status=True)
+ self.assertTrue("ERROR: Unable to find any recipe file matching 'asdf'" in result.output, msg = "Though asdf recipe doesn't exist, bitbake didn't output any err. message. bitbake output: %s" % result.output)
+
+ @OETestID(107)
+ def test_bitbake_invalid_target(self):
+ result = bitbake('asdf', ignore_status=True)
+ self.assertTrue("ERROR: Nothing PROVIDES 'asdf'" in result.output, msg = "Though no 'asdf' target exists, bitbake didn't output any err. message. bitbake output: %s" % result.output)
+
+ @OETestID(106)
+ def test_warnings_errors(self):
+ result = bitbake('-b asdf', ignore_status=True)
+ find_warnings = re.search("Summary: There w.{2,3}? [1-9][0-9]* WARNING messages* shown", result.output)
+ find_errors = re.search("Summary: There w.{2,3}? [1-9][0-9]* ERROR messages* shown", result.output)
+ self.assertTrue(find_warnings, msg="Did not find the mumber of warnings at the end of the build:\n" + result.output)
+ self.assertTrue(find_errors, msg="Did not find the mumber of errors at the end of the build:\n" + result.output)
+
+ @OETestID(108)
+ def test_invalid_patch(self):
+ # This patch already exists in SRC_URI so adding it again will cause the
+ # patch to fail.
+ self.write_recipeinc('man', 'SRC_URI += "file://man-1.5h1-make.patch"')
+ self.write_config("INHERIT_remove = \"report-error\"")
+ result = bitbake('man -c patch', ignore_status=True)
+ self.delete_recipeinc('man')
+ bitbake('-cclean man')
+ line = self.getline(result, "Function failed: patch_do_patch")
+ self.assertTrue(line and line.startswith("ERROR:"), msg = "Repeated patch application didn't fail. bitbake output: %s" % result.output)
+
+ @OETestID(1354)
+ def test_force_task_1(self):
+ # test 1 from bug 5875
+ test_recipe = 'zlib'
+ test_data = "Microsoft Made No Profit From Anyone's Zunes Yo"
+ bb_vars = get_bb_vars(['D', 'PKGDEST', 'mandir'], test_recipe)
+ image_dir = bb_vars['D']
+ pkgsplit_dir = bb_vars['PKGDEST']
+ man_dir = bb_vars['mandir']
+
+ bitbake('-c clean %s' % test_recipe)
+ bitbake('-c package -f %s' % test_recipe)
+ self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
+
+ man_file = os.path.join(image_dir + man_dir, 'man3/zlib.3')
+ ftools.append_file(man_file, test_data)
+ bitbake('-c package -f %s' % test_recipe)
+
+ man_split_file = os.path.join(pkgsplit_dir, 'zlib-doc' + man_dir, 'man3/zlib.3')
+ man_split_content = ftools.read_file(man_split_file)
+ self.assertIn(test_data, man_split_content, 'The man file has not changed in packages-split.')
+
+ ret = bitbake(test_recipe)
+ self.assertIn('task do_package_write_rpm:', ret.output, 'Task do_package_write_rpm did not re-executed.')
+
+ @OETestID(163)
+ def test_force_task_2(self):
+ # test 2 from bug 5875
+ test_recipe = 'zlib'
+
+ bitbake(test_recipe)
+ self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
+
+ result = bitbake('-C compile %s' % test_recipe)
+ look_for_tasks = ['do_compile:', 'do_install:', 'do_populate_sysroot:', 'do_package:']
+ for task in look_for_tasks:
+ self.assertIn(task, result.output, msg="Couldn't find %s task.")
+
+ @OETestID(167)
+ def test_bitbake_g(self):
+ result = bitbake('-g core-image-minimal')
+ for f in ['pn-buildlist', 'recipe-depends.dot', 'task-depends.dot']:
+ self.addCleanup(os.remove, f)
+ self.assertTrue('Task dependencies saved to \'task-depends.dot\'' in result.output, msg = "No task dependency \"task-depends.dot\" file was generated for the given task target. bitbake output: %s" % result.output)
+ self.assertTrue('busybox' in ftools.read_file(os.path.join(self.builddir, 'task-depends.dot')), msg = "No \"busybox\" dependency found in task-depends.dot file.")
+
+ @OETestID(899)
+ def test_image_manifest(self):
+ bitbake('core-image-minimal')
+ bb_vars = get_bb_vars(["DEPLOY_DIR_IMAGE", "IMAGE_LINK_NAME"], "core-image-minimal")
+ deploydir = bb_vars["DEPLOY_DIR_IMAGE"]
+ imagename = bb_vars["IMAGE_LINK_NAME"]
+ manifest = os.path.join(deploydir, imagename + ".manifest")
+ self.assertTrue(os.path.islink(manifest), msg="No manifest file created for image. It should have been created in %s" % manifest)
+
+ @OETestID(168)
+ def test_invalid_recipe_src_uri(self):
+ data = 'SRC_URI = "file://invalid"'
+ self.write_recipeinc('man', data)
+ self.write_config("""DL_DIR = \"${TOPDIR}/download-selftest\"
+SSTATE_DIR = \"${TOPDIR}/download-selftest\"
+INHERIT_remove = \"report-error\"
+""")
+ self.track_for_cleanup(os.path.join(self.builddir, "download-selftest"))
+
+ bitbake('-ccleanall man')
+ result = bitbake('-c fetch man', ignore_status=True)
+ bitbake('-ccleanall man')
+ self.delete_recipeinc('man')
+ self.assertEqual(result.status, 1, msg="Command succeded when it should have failed. bitbake output: %s" % result.output)
+ self.assertTrue('Fetcher failure: Unable to find file file://invalid anywhere. The paths that were searched were:' in result.output, msg = "\"invalid\" file \
+doesn't exist, yet no error message encountered. bitbake output: %s" % result.output)
+ line = self.getline(result, 'Fetcher failure for URL: \'file://invalid\'. Unable to fetch URL from any source.')
+ self.assertTrue(line and line.startswith("ERROR:"), msg = "\"invalid\" file \
+doesn't exist, yet fetcher didn't report any error. bitbake output: %s" % result.output)
+
+ @OETestID(171)
+ def test_rename_downloaded_file(self):
+ # TODO unique dldir instead of using cleanall
+ # TODO: need to set sstatedir?
+ self.write_config("""DL_DIR = \"${TOPDIR}/download-selftest\"
+SSTATE_DIR = \"${TOPDIR}/download-selftest\"
+""")
+ self.track_for_cleanup(os.path.join(self.builddir, "download-selftest"))
+
+ data = 'SRC_URI = "${GNU_MIRROR}/aspell/aspell-${PV}.tar.gz;downloadfilename=test-aspell.tar.gz"'
+ self.write_recipeinc('aspell', data)
+ result = bitbake('-f -c fetch aspell', ignore_status=True)
+ self.delete_recipeinc('aspell')
+ self.assertEqual(result.status, 0, msg = "Couldn't fetch aspell. %s" % result.output)
+ dl_dir = get_bb_var("DL_DIR")
+ self.assertTrue(os.path.isfile(os.path.join(dl_dir, 'test-aspell.tar.gz')), msg = "File rename failed. No corresponding test-aspell.tar.gz file found under %s" % dl_dir)
+ self.assertTrue(os.path.isfile(os.path.join(dl_dir, 'test-aspell.tar.gz.done')), "File rename failed. No corresponding test-aspell.tar.gz.done file found under %s" % dl_dir)
+
+ @OETestID(1028)
+ def test_environment(self):
+ self.write_config("TEST_ENV=\"localconf\"")
+ result = runCmd('bitbake -e | grep TEST_ENV=')
+ self.assertTrue('localconf' in result.output, msg = "bitbake didn't report any value for TEST_ENV variable. To test, run 'bitbake -e | grep TEST_ENV='")
+
+ @OETestID(1029)
+ def test_dry_run(self):
+ result = runCmd('bitbake -n m4-native')
+ self.assertEqual(0, result.status, "bitbake dry run didn't run as expected. %s" % result.output)
+
+ @OETestID(1030)
+ def test_just_parse(self):
+ result = runCmd('bitbake -p')
+ self.assertEqual(0, result.status, "errors encountered when parsing recipes. %s" % result.output)
+
+ @OETestID(1031)
+ def test_version(self):
+ result = runCmd('bitbake -s | grep wget')
+ find = re.search("wget *:([0-9a-zA-Z\.\-]+)", result.output)
+ self.assertTrue(find, "No version returned for searched recipe. bitbake output: %s" % result.output)
+
+ @OETestID(1032)
+ def test_prefile(self):
+ preconf = os.path.join(self.builddir, 'conf/prefile.conf')
+ self.track_for_cleanup(preconf)
+ ftools.write_file(preconf ,"TEST_PREFILE=\"prefile\"")
+ result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
+ self.assertTrue('prefile' in result.output, "Preconfigure file \"prefile.conf\"was not taken into consideration. ")
+ self.write_config("TEST_PREFILE=\"localconf\"")
+ result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
+ self.assertTrue('localconf' in result.output, "Preconfigure file \"prefile.conf\"was not taken into consideration.")
+
+ @OETestID(1033)
+ def test_postfile(self):
+ postconf = os.path.join(self.builddir, 'conf/postfile.conf')
+ self.track_for_cleanup(postconf)
+ ftools.write_file(postconf , "TEST_POSTFILE=\"postfile\"")
+ self.write_config("TEST_POSTFILE=\"localconf\"")
+ result = runCmd('bitbake -R conf/postfile.conf -e | grep TEST_POSTFILE=')
+ self.assertTrue('postfile' in result.output, "Postconfigure file \"postfile.conf\"was not taken into consideration.")
+
+ @OETestID(1034)
+ def test_checkuri(self):
+ result = runCmd('bitbake -c checkuri m4')
+ self.assertEqual(0, result.status, msg = "\"checkuri\" task was not executed. bitbake output: %s" % result.output)
+
+ @OETestID(1035)
+ def test_continue(self):
+ self.write_config("""DL_DIR = \"${TOPDIR}/download-selftest\"
+SSTATE_DIR = \"${TOPDIR}/download-selftest\"
+INHERIT_remove = \"report-error\"
+""")
+ self.track_for_cleanup(os.path.join(self.builddir, "download-selftest"))
+ self.write_recipeinc('man',"\ndo_fail_task () {\nexit 1 \n}\n\naddtask do_fail_task before do_fetch\n" )
+ runCmd('bitbake -c cleanall man xcursor-transparent-theme')
+ result = runCmd('bitbake -c unpack -k man xcursor-transparent-theme', ignore_status=True)
+ errorpos = result.output.find('ERROR: Function failed: do_fail_task')
+ manver = re.search("NOTE: recipe xcursor-transparent-theme-(.*?): task do_unpack: Started", result.output)
+ continuepos = result.output.find('NOTE: recipe xcursor-transparent-theme-%s: task do_unpack: Started' % manver.group(1))
+ self.assertLess(errorpos,continuepos, msg = "bitbake didn't pass do_fail_task. bitbake output: %s" % result.output)
+
+ @OETestID(1119)
+ def test_non_gplv3(self):
+ self.write_config('INCOMPATIBLE_LICENSE = "GPLv3"')
+ result = bitbake('selftest-ed', ignore_status=True)
+ self.assertEqual(result.status, 0, "Bitbake failed, exit code %s, output %s" % (result.status, result.output))
+ lic_dir = get_bb_var('LICENSE_DIRECTORY')
+ self.assertFalse(os.path.isfile(os.path.join(lic_dir, 'selftest-ed/generic_GPLv3')))
+ self.assertTrue(os.path.isfile(os.path.join(lic_dir, 'selftest-ed/generic_GPLv2')))
+
+ @OETestID(1422)
+ def test_setscene_only(self):
+ """ Bitbake option to restore from sstate only within a build (i.e. execute no real tasks, only setscene)"""
+ test_recipe = 'ed'
+
+ bitbake(test_recipe)
+ bitbake('-c clean %s' % test_recipe)
+ ret = bitbake('--setscene-only %s' % test_recipe)
+
+ tasks = re.findall(r'task\s+(do_\S+):', ret.output)
+
+ for task in tasks:
+ self.assertIn('_setscene', task, 'A task different from _setscene ran: %s.\n'
+ 'Executed tasks were: %s' % (task, str(tasks)))
+
+ @OETestID(1425)
+ def test_bbappend_order(self):
+ """ Bitbake should bbappend to recipe in a predictable order """
+ test_recipe = 'ed'
+ bb_vars = get_bb_vars(['SUMMARY', 'PV'], test_recipe)
+ test_recipe_summary_before = bb_vars['SUMMARY']
+ test_recipe_pv = bb_vars['PV']
+ recipe_append_file = test_recipe + '_' + test_recipe_pv + '.bbappend'
+ expected_recipe_summary = test_recipe_summary_before
+
+ for i in range(5):
+ recipe_append_dir = test_recipe + '_test_' + str(i)
+ recipe_append_path = os.path.join(self.testlayer_path, 'recipes-test', recipe_append_dir, recipe_append_file)
+ os.mkdir(os.path.join(self.testlayer_path, 'recipes-test', recipe_append_dir))
+ feature = 'SUMMARY += "%s"\n' % i
+ ftools.write_file(recipe_append_path, feature)
+ expected_recipe_summary += ' %s' % i
+
+ self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, 'recipes-test',
+ test_recipe + '_test_*'))
+
+ test_recipe_summary_after = get_bb_var('SUMMARY', test_recipe)
+ self.assertEqual(expected_recipe_summary, test_recipe_summary_after)
diff --git a/meta/lib/oeqa/selftest/cases/buildhistory.py b/meta/lib/oeqa/selftest/cases/buildhistory.py
new file mode 100644
index 0000000000..06792d9146
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/buildhistory.py
@@ -0,0 +1,46 @@
+import os
+import re
+import datetime
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import bitbake, get_bb_vars
+
+
+class BuildhistoryBase(OESelftestTestCase):
+
+ def config_buildhistory(self, tmp_bh_location=False):
+ bb_vars = get_bb_vars(['USER_CLASSES', 'INHERIT'])
+ if (not 'buildhistory' in bb_vars['USER_CLASSES']) and (not 'buildhistory' in bb_vars['INHERIT']):
+ add_buildhistory_config = 'INHERIT += "buildhistory"\nBUILDHISTORY_COMMIT = "1"'
+ self.append_config(add_buildhistory_config)
+
+ if tmp_bh_location:
+ # Using a temporary buildhistory location for testing
+ tmp_bh_dir = os.path.join(self.builddir, "tmp_buildhistory_%s" % datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
+ buildhistory_dir_config = "BUILDHISTORY_DIR = \"%s\"" % tmp_bh_dir
+ self.append_config(buildhistory_dir_config)
+ self.track_for_cleanup(tmp_bh_dir)
+
+ def run_buildhistory_operation(self, target, global_config='', target_config='', change_bh_location=False, expect_error=False, error_regex=''):
+ if change_bh_location:
+ tmp_bh_location = True
+ else:
+ tmp_bh_location = False
+ self.config_buildhistory(tmp_bh_location)
+
+ self.append_config(global_config)
+ self.append_recipeinc(target, target_config)
+ bitbake("-cclean %s" % target)
+ result = bitbake(target, ignore_status=True)
+ self.remove_config(global_config)
+ self.remove_recipeinc(target, target_config)
+
+ if expect_error:
+ self.assertEqual(result.status, 1, msg="Error expected for global config '%s' and target config '%s'" % (global_config, target_config))
+ search_for_error = re.search(error_regex, result.output)
+ self.assertTrue(search_for_error, msg="Could not find desired error in output: %s (%s)" % (error_regex, result.output))
+ else:
+ self.assertEqual(result.status, 0, msg="Command 'bitbake %s' has failed unexpectedly: %s" % (target, result.output))
+
+ # No tests should be added to the base class.
+ # Please create a new class that inherit this one, or use one of those already available for adding tests.
diff --git a/meta/lib/oeqa/selftest/cases/buildoptions.py b/meta/lib/oeqa/selftest/cases/buildoptions.py
new file mode 100644
index 0000000000..1f1bb7ae63
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/buildoptions.py
@@ -0,0 +1,184 @@
+import os
+import re
+import glob as g
+import shutil
+import tempfile
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.selftest.cases.buildhistory import BuildhistoryBase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
+import oeqa.utils.ftools as ftools
+from oeqa.core.decorator.oeid import OETestID
+
+class ImageOptionsTests(OESelftestTestCase):
+
+ @OETestID(761)
+ def test_incremental_image_generation(self):
+ image_pkgtype = get_bb_var("IMAGE_PKGTYPE")
+ if image_pkgtype != 'rpm':
+ self.skipTest('Not using RPM as main package format')
+ bitbake("-c clean core-image-minimal")
+ self.write_config('INC_RPM_IMAGE_GEN = "1"')
+ self.append_config('IMAGE_FEATURES += "ssh-server-openssh"')
+ bitbake("core-image-minimal")
+ log_data_file = os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")
+ log_data_created = ftools.read_file(log_data_file)
+ incremental_created = re.search(r"Installing\s*:\s*packagegroup-core-ssh-openssh", log_data_created)
+ self.remove_config('IMAGE_FEATURES += "ssh-server-openssh"')
+ self.assertTrue(incremental_created, msg = "Match failed in:\n%s" % log_data_created)
+ bitbake("core-image-minimal")
+ log_data_removed = ftools.read_file(log_data_file)
+ incremental_removed = re.search(r"Erasing\s*:\s*packagegroup-core-ssh-openssh", log_data_removed)
+ self.assertTrue(incremental_removed, msg = "Match failed in:\n%s" % log_data_removed)
+
+ @OETestID(286)
+ def test_ccache_tool(self):
+ bitbake("ccache-native")
+ bb_vars = get_bb_vars(['SYSROOT_DESTDIR', 'bindir'], 'ccache-native')
+ p = bb_vars['SYSROOT_DESTDIR'] + bb_vars['bindir'] + "/" + "ccache"
+ 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 -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)
+
+ @OETestID(1435)
+ def test_read_only_image(self):
+ distro_features = get_bb_var('DISTRO_FEATURES')
+ if not ('x11' in distro_features and 'opengl' in distro_features):
+ self.skipTest('core-image-sato requires x11 and opengl in distro features')
+ self.write_config('IMAGE_FEATURES += "read-only-rootfs"')
+ bitbake("core-image-sato")
+ # do_image will fail if there are any pending postinsts
+
+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)
+ 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)
+ 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")
+ 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):
+ def getline(self, res, line):
+ for l in res.output.split('\n'):
+ if line in l:
+ return l
+
+ @OETestID(927)
+ def test_options_warnqa_errorqa_switch(self):
+
+ self.write_config("INHERIT_remove = \"report-error\"")
+ if "packages-list" not in get_bb_var("ERROR_QA"):
+ self.append_config("ERROR_QA_append = \" packages-list\"")
+
+ self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
+ self.add_command_to_tearDown('bitbake -c clean xcursor-transparent-theme')
+ res = bitbake("xcursor-transparent-theme -f -c package", ignore_status=True)
+ self.delete_recipeinc('xcursor-transparent-theme')
+ line = self.getline(res, "QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors.")
+ self.assertTrue(line and line.startswith("ERROR:"), msg=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_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
+ self.append_config('ERROR_QA_remove = "packages-list"')
+ self.append_config('WARN_QA_append = " packages-list"')
+ res = bitbake("xcursor-transparent-theme -f -c package")
+ self.delete_recipeinc('xcursor-transparent-theme')
+ line = self.getline(res, "QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors.")
+ self.assertTrue(line and line.startswith("WARNING:"), msg=res.output)
+
+ @OETestID(278)
+ def test_sanity_unsafe_script_references(self):
+ self.write_config('WARN_QA_append = " unsafe-references-in-scripts"')
+
+ self.add_command_to_tearDown('bitbake -c clean gzip')
+ res = bitbake("gzip -f -c package_qa")
+ line = self.getline(res, "QA Issue: gzip")
+ self.assertFalse(line, "WARNING: QA Issue: gzip message is present in bitbake's output and shouldn't be: %s" % res.output)
+
+ self.append_config("""
+do_install_append_pn-gzip () {
+ echo "\n${bindir}/test" >> ${D}${bindir}/zcat
+}
+""")
+ res = bitbake("gzip -f -c package_qa")
+ line = self.getline(res, "QA Issue: gzip")
+ self.assertTrue(line and line.startswith("WARNING:"), "WARNING: QA Issue: gzip message is not present in bitbake's output: %s" % res.output)
+
+ @OETestID(1421)
+ def test_layer_without_git_dir(self):
+ """
+ Summary: Test that layer git revisions are displayed and do not fail without git repository
+ Expected: The build to be successful and without "fatal" errors
+ Product: oe-core
+ Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ """
+
+ dirpath = tempfile.mkdtemp()
+
+ dummy_layer_name = 'meta-dummy'
+ dummy_layer_path = os.path.join(dirpath, dummy_layer_name)
+ dummy_layer_conf_dir = os.path.join(dummy_layer_path, 'conf')
+ os.makedirs(dummy_layer_conf_dir)
+ dummy_layer_conf_path = os.path.join(dummy_layer_conf_dir, 'layer.conf')
+
+ dummy_layer_content = 'BBPATH .= ":${LAYERDIR}"\n' \
+ 'BBFILES += "${LAYERDIR}/recipes-*/*/*.bb ${LAYERDIR}/recipes-*/*/*.bbappend"\n' \
+ 'BBFILE_COLLECTIONS += "%s"\n' \
+ 'BBFILE_PATTERN_%s = "^${LAYERDIR}/"\n' \
+ 'BBFILE_PRIORITY_%s = "6"\n' % (dummy_layer_name, dummy_layer_name, dummy_layer_name)
+
+ ftools.write_file(dummy_layer_conf_path, dummy_layer_content)
+
+ bblayers_conf = 'BBLAYERS += "%s"\n' % dummy_layer_path
+ self.write_bblayers_config(bblayers_conf)
+
+ test_recipe = 'ed'
+
+ ret = bitbake('-n %s' % test_recipe)
+
+ err = 'fatal: Not a git repository'
+
+ shutil.rmtree(dirpath)
+
+ self.assertNotIn(err, ret.output)
+
+
+class BuildhistoryTests(BuildhistoryBase):
+
+ @OETestID(293)
+ def test_buildhistory_basic(self):
+ self.run_buildhistory_operation('xcursor-transparent-theme')
+ self.assertTrue(os.path.isdir(get_bb_var('BUILDHISTORY_DIR')), "buildhistory dir was not created.")
+
+ @OETestID(294)
+ def test_buildhistory_buildtime_pr_backwards(self):
+ target = 'xcursor-transparent-theme'
+ error = "ERROR:.*QA Issue: Package version for package %s went backwards which would break package feeds from (.*-r1.* to .*-r0.*)" % target
+ self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
+ self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True, error_regex=error)
+
+class ArchiverTest(OESelftestTestCase):
+ @OETestID(926)
+ def test_arch_work_dir_and_export_source(self):
+ """
+ Test for archiving the work directory and exporting the source files.
+ """
+ self.write_config("INHERIT += \"archiver\"\nARCHIVER_MODE[src] = \"original\"\nARCHIVER_MODE[srpm] = \"1\"")
+ res = bitbake("xcursor-transparent-theme", ignore_status=True)
+ self.assertEqual(res.status, 0, "\nCouldn't build xcursortransparenttheme.\nbitbake output %s" % res.output)
+ deploy_dir_src = get_bb_var('DEPLOY_DIR_SRC')
+ pkgs_path = g.glob(str(deploy_dir_src) + "/allarch*/xcurs*")
+ src_file_glob = str(pkgs_path[0]) + "/xcursor*.src.rpm"
+ tar_file_glob = str(pkgs_path[0]) + "/xcursor*.tar.gz"
+ self.assertTrue((g.glob(src_file_glob) and g.glob(tar_file_glob)), "Couldn't find .src.rpm and .tar.gz files under %s/allarch*/xcursor*" % deploy_dir_src)
diff --git a/meta/lib/oeqa/selftest/cases/containerimage.py b/meta/lib/oeqa/selftest/cases/containerimage.py
new file mode 100644
index 0000000000..73162fa600
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/containerimage.py
@@ -0,0 +1,83 @@
+import os
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import bitbake, get_bb_vars, runCmd
+
+# This test builds an image with using the "container" IMAGE_FSTYPE, and
+# ensures that then files in the image are only the ones expected.
+#
+# The only package added to the image is container_image_testpkg, which
+# contains one file. However, due to some other things not cleaning up during
+# rootfs creation, there is some cruft. Ideally bugs will be filed and the
+# cruft removed, but for now we whitelist some known set.
+#
+# Also for performance reasons we're only checking the cruft when using ipk.
+# When using deb, and rpm it is a bit different and we could test all
+# of them, but this test is more to catch if other packages get added by
+# default other than what is in ROOTFS_BOOTSTRAP_INSTALL.
+#
+class ContainerImageTests(OESelftestTestCase):
+
+ # Verify that when specifying a IMAGE_TYPEDEP_ of the form "foo.bar" that
+ # the conversion type bar gets added as a dep as well
+ def test_expected_files(self):
+
+ def get_each_path_part(path):
+ if path:
+ part = [ '.' + path + '/' ]
+ result = get_each_path_part(path.rsplit('/', 1)[0])
+ if result:
+ return part + result
+ else:
+ return part
+ else:
+ return None
+
+ self.write_config("""PREFERRED_PROVIDER_virtual/kernel = "linux-dummy"
+IMAGE_FSTYPES = "container"
+PACKAGE_CLASSES = "package_ipk"
+IMAGE_FEATURES = ""
+""")
+
+ bbvars = get_bb_vars(['bindir', 'sysconfdir', 'localstatedir',
+ 'DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'],
+ target='container-test-image')
+ expected_files = [
+ './',
+ '.{bindir}/theapp',
+ '.{sysconfdir}/default/',
+ '.{sysconfdir}/default/postinst',
+ '.{sysconfdir}/ld.so.cache',
+ '.{sysconfdir}/timestamp',
+ '.{sysconfdir}/version',
+ './run/',
+ '.{localstatedir}/cache/',
+ '.{localstatedir}/cache/ldconfig/',
+ '.{localstatedir}/cache/ldconfig/aux-cache',
+ '.{localstatedir}/cache/opkg/',
+ '.{localstatedir}/lib/',
+ '.{localstatedir}/lib/opkg/'
+ ]
+
+ expected_files = [ x.format(bindir=bbvars['bindir'],
+ sysconfdir=bbvars['sysconfdir'],
+ localstatedir=bbvars['localstatedir'])
+ for x in expected_files ]
+
+ # Since tar lists all directories individually, make sure each element
+ # from bindir, sysconfdir, etc is added
+ expected_files += get_each_path_part(bbvars['bindir'])
+ expected_files += get_each_path_part(bbvars['sysconfdir'])
+ expected_files += get_each_path_part(bbvars['localstatedir'])
+
+ expected_files = sorted(expected_files)
+
+ # Build the image of course
+ bitbake('container-test-image')
+
+ image = os.path.join(bbvars['DEPLOY_DIR_IMAGE'],
+ bbvars['IMAGE_LINK_NAME'] + '.tar.bz2')
+
+ # Ensure the files in the image are what we expect
+ result = runCmd("tar tf {} | sort".format(image), shell=True)
+ self.assertEqual(result.output.split('\n'), expected_files)
diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
new file mode 100644
index 0000000000..75340d6d7b
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -0,0 +1,1696 @@
+import os
+import re
+import shutil
+import tempfile
+import glob
+import fnmatch
+
+import oeqa.utils.ftools as ftools
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer
+from oeqa.utils.commands import get_bb_vars, runqemu, get_test_layer
+from oeqa.core.decorator.oeid import OETestID
+
+class DevtoolBase(OESelftestTestCase):
+
+ def _test_recipe_contents(self, recipefile, checkvars, checkinherits):
+ with open(recipefile, 'r') as f:
+ invar = None
+ invalue = None
+ for line in f:
+ var = None
+ if invar:
+ value = line.strip().strip('"')
+ if value.endswith('\\'):
+ invalue += ' ' + value[:-1].strip()
+ continue
+ else:
+ invalue += ' ' + value.strip()
+ var = invar
+ value = invalue
+ invar = None
+ elif '=' in line:
+ splitline = line.split('=', 1)
+ var = splitline[0].rstrip()
+ value = splitline[1].strip().strip('"')
+ if value.endswith('\\'):
+ invalue = value[:-1].strip()
+ invar = var
+ continue
+ elif line.startswith('inherit '):
+ inherits = line.split()[1:]
+
+ if var and var in checkvars:
+ needvalue = checkvars.pop(var)
+ if needvalue is None:
+ self.fail('Variable %s should not appear in recipe, but value is being set to "%s"' % (var, value))
+ if isinstance(needvalue, set):
+ if var == 'LICENSE':
+ value = set(value.split(' & '))
+ else:
+ value = set(value.split())
+ self.assertEqual(value, needvalue, 'values for %s do not match' % var)
+
+
+ missingvars = {}
+ for var, value in checkvars.items():
+ if value is not None:
+ missingvars[var] = value
+ self.assertEqual(missingvars, {}, 'Some expected variables not found in recipe: %s' % checkvars)
+
+ for inherit in checkinherits:
+ self.assertIn(inherit, inherits, 'Missing inherit of %s' % inherit)
+
+ def _check_bbappend(self, testrecipe, recipefile, appenddir):
+ result = runCmd('bitbake-layers show-appends', cwd=self.builddir)
+ resultlines = result.output.splitlines()
+ inrecipe = False
+ bbappends = []
+ bbappendfile = None
+ for line in resultlines:
+ if inrecipe:
+ if line.startswith(' '):
+ bbappends.append(line.strip())
+ else:
+ break
+ elif line == '%s:' % os.path.basename(recipefile):
+ inrecipe = True
+ self.assertLessEqual(len(bbappends), 2, '%s recipe is being bbappended by another layer - bbappends found:\n %s' % (testrecipe, '\n '.join(bbappends)))
+ for bbappend in bbappends:
+ if bbappend.startswith(appenddir):
+ bbappendfile = bbappend
+ break
+ else:
+ self.fail('bbappend for recipe %s does not seem to be created in test layer' % testrecipe)
+ return bbappendfile
+
+ def _create_temp_layer(self, templayerdir, addlayer, templayername, priority=999, recipepathspec='recipes-*/*'):
+ create_temp_layer(templayerdir, templayername, priority, recipepathspec)
+ if addlayer:
+ self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
+ result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
+
+ def _process_ls_output(self, output):
+ """
+ Convert ls -l output to a format we can reasonably compare from one context
+ to another (e.g. from host to target)
+ """
+ filelist = []
+ for line in output.splitlines():
+ splitline = line.split()
+ if len(splitline) < 8:
+ self.fail('_process_ls_output: invalid output line: %s' % line)
+ # Remove trailing . on perms
+ splitline[0] = splitline[0].rstrip('.')
+ # Remove leading . on paths
+ splitline[-1] = splitline[-1].lstrip('.')
+ # Drop fields we don't want to compare
+ del splitline[7]
+ del splitline[6]
+ del splitline[5]
+ del splitline[4]
+ del splitline[1]
+ filelist.append(' '.join(splitline))
+ return filelist
+
+
+class DevtoolTests(DevtoolBase):
+
+ @classmethod
+ def setUpClass(cls):
+ super(DevtoolTests, cls).setUpClass()
+ bb_vars = get_bb_vars(['TOPDIR', 'SSTATE_DIR'])
+ cls.original_sstate = bb_vars['SSTATE_DIR']
+ cls.devtool_sstate = os.path.join(bb_vars['TOPDIR'], 'sstate_devtool')
+ cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate
+ cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n'
+ % cls.original_sstate)
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.logger.debug('Deleting devtool sstate cache on %s' % cls.devtool_sstate)
+ runCmd('rm -rf %s' % cls.devtool_sstate)
+ super(DevtoolTests, cls).tearDownClass()
+
+ def setUp(self):
+ """Test case setup function"""
+ super(DevtoolTests, self).setUp()
+ self.workspacedir = os.path.join(self.builddir, 'workspace')
+ self.assertTrue(not os.path.exists(self.workspacedir),
+ 'This test cannot be run with a workspace directory '
+ 'under the build directory')
+ self.append_config(self.sstate_conf)
+
+ def _check_src_repo(self, repo_dir):
+ """Check srctree git repository"""
+ self.assertTrue(os.path.isdir(os.path.join(repo_dir, '.git')),
+ 'git repository for external source tree not found')
+ result = runCmd('git status --porcelain', cwd=repo_dir)
+ self.assertEqual(result.output.strip(), "",
+ 'Created git repo is not clean')
+ result = runCmd('git symbolic-ref HEAD', cwd=repo_dir)
+ self.assertEqual(result.output.strip(), "refs/heads/devtool",
+ 'Wrong branch in git repo')
+
+ def _check_repo_status(self, repo_dir, expected_status):
+ """Check the worktree status of a repository"""
+ result = runCmd('git status . --porcelain',
+ cwd=repo_dir)
+ for line in result.output.splitlines():
+ for ind, (f_status, fn_re) in enumerate(expected_status):
+ if re.match(fn_re, line[3:]):
+ if f_status != line[:2]:
+ self.fail('Unexpected status in line: %s' % line)
+ expected_status.pop(ind)
+ break
+ else:
+ self.fail('Unexpected modified file in line: %s' % line)
+ if expected_status:
+ self.fail('Missing file changes: %s' % expected_status)
+
+ @OETestID(1158)
+ def test_create_workspace(self):
+ # Check preconditions
+ result = runCmd('bitbake-layers show-layers')
+ self.assertTrue('/workspace' not in result.output, 'This test cannot be run with a workspace layer in bblayers.conf')
+ # Try creating a workspace layer with a specific path
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ result = runCmd('devtool create-workspace %s' % tempdir)
+ self.assertTrue(os.path.isfile(os.path.join(tempdir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
+ result = runCmd('bitbake-layers show-layers')
+ self.assertIn(tempdir, result.output)
+ # Try creating a workspace layer with the default path
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool create-workspace')
+ self.assertTrue(os.path.isfile(os.path.join(self.workspacedir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
+ result = runCmd('bitbake-layers show-layers')
+ self.assertNotIn(tempdir, result.output)
+ self.assertIn(self.workspacedir, result.output)
+
+ @OETestID(1159)
+ def test_devtool_add(self):
+ # Fetch source
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ url = 'http://www.ivarch.com/programs/sources/pv-1.5.3.tar.bz2'
+ result = runCmd('wget %s' % url, cwd=tempdir)
+ result = runCmd('tar xfv pv-1.5.3.tar.bz2', cwd=tempdir)
+ srcdir = os.path.join(tempdir, 'pv-1.5.3')
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
+ # Test devtool add
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake -c cleansstate pv')
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool add pv %s' % srcdir)
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn('pv', result.output)
+ self.assertIn(srcdir, result.output)
+ # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
+ bitbake('pv -c cleansstate')
+ # Test devtool build
+ result = runCmd('devtool build pv')
+ bb_vars = get_bb_vars(['D', 'bindir'], 'pv')
+ installdir = bb_vars['D']
+ self.assertTrue(installdir, 'Could not query installdir variable')
+ bindir = bb_vars['bindir']
+ self.assertTrue(bindir, 'Could not query bindir variable')
+ if bindir[0] == '/':
+ bindir = bindir[1:]
+ self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D')
+
+ @OETestID(1423)
+ def test_devtool_add_git_local(self):
+ # Fetch source from a remote URL, but do it outside of devtool
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ pn = 'dbus-wait'
+ srcrev = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
+ # We choose an https:// git URL here to check rewriting the URL works
+ url = 'https://git.yoctoproject.org/git/dbus-wait'
+ # Force fetching to "noname" subdir so we verify we're picking up the name from autoconf
+ # instead of the directory name
+ result = runCmd('git clone %s noname' % url, cwd=tempdir)
+ srcdir = os.path.join(tempdir, 'noname')
+ result = runCmd('git reset --hard %s' % srcrev, cwd=srcdir)
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure script in source directory')
+ # Test devtool add
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # Don't specify a name since we should be able to auto-detect it
+ result = runCmd('devtool add %s' % srcdir)
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ # Check the recipe name is correct
+ recipefile = get_bb_var('FILE', pn)
+ self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named')
+ self.assertIn(recipefile, result.output)
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(pn, result.output)
+ self.assertIn(srcdir, result.output)
+ self.assertIn(recipefile, result.output)
+ checkvars = {}
+ checkvars['LICENSE'] = 'GPLv2'
+ checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'
+ checkvars['S'] = '${WORKDIR}/git'
+ checkvars['PV'] = '0.1+git${SRCPV}'
+ checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https'
+ checkvars['SRCREV'] = srcrev
+ checkvars['DEPENDS'] = set(['dbus'])
+ self._test_recipe_contents(recipefile, checkvars, [])
+
+ @OETestID(1162)
+ def test_devtool_add_library(self):
+ # Fetch source
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ version = '1.1'
+ url = 'https://www.intra2net.com/en/developer/libftdi/download/libftdi1-%s.tar.bz2' % version
+ result = runCmd('wget %s' % url, cwd=tempdir)
+ result = runCmd('tar xfv libftdi1-%s.tar.bz2' % version, cwd=tempdir)
+ srcdir = os.path.join(tempdir, 'libftdi1-%s' % version)
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'CMakeLists.txt')), 'Unable to find CMakeLists.txt in source directory')
+ # Test devtool add (and use -V so we test that too)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool add libftdi %s -V %s' % (srcdir, version))
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn('libftdi', result.output)
+ self.assertIn(srcdir, result.output)
+ # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
+ bitbake('libftdi -c cleansstate')
+ # libftdi's python/CMakeLists.txt is a bit broken, so let's just disable it
+ # There's also the matter of it installing cmake files to a path we don't
+ # normally cover, which triggers the installed-vs-shipped QA test we have
+ # within do_package
+ recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version)
+ result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- \'-DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules\'' % recipefile)
+ with open(recipefile, 'a') as f:
+ f.write('\nFILES_${PN}-dev += "${datadir}/cmake/Modules"\n')
+ # We don't have the ability to pick up this dependency automatically yet...
+ f.write('\nDEPENDS += "libusb1"\n')
+ f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n')
+ # Test devtool build
+ result = runCmd('devtool build libftdi')
+ bb_vars = get_bb_vars(['TESTLIBOUTPUT', 'STAMP'], 'libftdi')
+ staging_libdir = bb_vars['TESTLIBOUTPUT']
+ self.assertTrue(staging_libdir, 'Could not query TESTLIBOUTPUT variable')
+ self.assertTrue(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), "libftdi binary not found in STAGING_LIBDIR. Output of devtool build libftdi %s" % result.output)
+ # Test devtool reset
+ stampprefix = bb_vars['STAMP']
+ result = runCmd('devtool reset libftdi')
+ result = runCmd('devtool status')
+ self.assertNotIn('libftdi', result.output)
+ self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe libftdi')
+ matches = glob.glob(stampprefix + '*')
+ self.assertFalse(matches, 'Stamp files exist for recipe libftdi that should have been cleaned')
+ self.assertFalse(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary still found in STAGING_LIBDIR after cleaning')
+
+ @OETestID(1160)
+ def test_devtool_add_fetch(self):
+ # Fetch source
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ testver = '0.23'
+ url = 'https://pypi.python.org/packages/source/M/MarkupSafe/MarkupSafe-%s.tar.gz' % testver
+ testrecipe = 'python-markupsafe'
+ srcdir = os.path.join(tempdir, testrecipe)
+ # Test devtool add
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url))
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created. %s' % result.output)
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
+ self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(testrecipe, result.output)
+ self.assertIn(srcdir, result.output)
+ # Check recipe
+ recipefile = get_bb_var('FILE', testrecipe)
+ self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
+ checkvars = {}
+ checkvars['S'] = '${WORKDIR}/MarkupSafe-${PV}'
+ checkvars['SRC_URI'] = url.replace(testver, '${PV}')
+ self._test_recipe_contents(recipefile, checkvars, [])
+ # Try with version specified
+ result = runCmd('devtool reset -n %s' % testrecipe)
+ shutil.rmtree(srcdir)
+ fakever = '1.9'
+ result = runCmd('devtool add %s %s -f %s -V %s' % (testrecipe, srcdir, url, fakever))
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(testrecipe, result.output)
+ self.assertIn(srcdir, result.output)
+ # Check recipe
+ recipefile = get_bb_var('FILE', testrecipe)
+ self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named')
+ checkvars = {}
+ checkvars['S'] = '${WORKDIR}/MarkupSafe-%s' % testver
+ checkvars['SRC_URI'] = url
+ self._test_recipe_contents(recipefile, checkvars, [])
+
+ @OETestID(1161)
+ def test_devtool_add_fetch_git(self):
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ url = 'gitsm://git.yoctoproject.org/mraa'
+ checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d'
+ testrecipe = 'mraa'
+ srcdir = os.path.join(tempdir, testrecipe)
+ # Test devtool add
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool add %s %s -a -f %s' % (testrecipe, srcdir, url))
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created: %s' % result.output)
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(testrecipe, result.output)
+ self.assertIn(srcdir, result.output)
+ # Check recipe
+ recipefile = get_bb_var('FILE', testrecipe)
+ self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
+ checkvars = {}
+ checkvars['S'] = '${WORKDIR}/git'
+ checkvars['PV'] = '1.0+git${SRCPV}'
+ checkvars['SRC_URI'] = url
+ checkvars['SRCREV'] = '${AUTOREV}'
+ self._test_recipe_contents(recipefile, checkvars, [])
+ # Try with revision and version specified
+ result = runCmd('devtool reset -n %s' % testrecipe)
+ shutil.rmtree(srcdir)
+ url_rev = '%s;rev=%s' % (url, checkrev)
+ result = runCmd('devtool add %s %s -f "%s" -V 1.5' % (testrecipe, srcdir, url_rev))
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(testrecipe, result.output)
+ self.assertIn(srcdir, result.output)
+ # Check recipe
+ recipefile = get_bb_var('FILE', testrecipe)
+ self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
+ checkvars = {}
+ checkvars['S'] = '${WORKDIR}/git'
+ checkvars['PV'] = '1.5+git${SRCPV}'
+ checkvars['SRC_URI'] = url
+ checkvars['SRCREV'] = checkrev
+ self._test_recipe_contents(recipefile, checkvars, [])
+
+ @OETestID(1391)
+ def test_devtool_add_fetch_simple(self):
+ # Fetch source from a remote URL, auto-detecting name
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ testver = '1.6.0'
+ url = 'http://www.ivarch.com/programs/sources/pv-%s.tar.bz2' % testver
+ testrecipe = 'pv'
+ srcdir = os.path.join(self.workspacedir, 'sources', testrecipe)
+ # Test devtool add
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool add %s' % url)
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created. %s' % result.output)
+ self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
+ self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(testrecipe, result.output)
+ self.assertIn(srcdir, result.output)
+ # Check recipe
+ recipefile = get_bb_var('FILE', testrecipe)
+ self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
+ checkvars = {}
+ checkvars['S'] = None
+ checkvars['SRC_URI'] = url.replace(testver, '${PV}')
+ self._test_recipe_contents(recipefile, checkvars, [])
+
+ @OETestID(1164)
+ def test_devtool_modify(self):
+ import oe.path
+
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean mdadm')
+ result = runCmd('devtool modify mdadm -x %s' % tempdir)
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'mdadm_*.bbappend'))
+ self.assertTrue(matches, 'bbappend not created %s' % result.output)
+
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn('mdadm', result.output)
+ self.assertIn(tempdir, result.output)
+ self._check_src_repo(tempdir)
+
+ bitbake('mdadm -C unpack')
+
+ def check_line(checkfile, expected, message, present=True):
+ # Check for $expected, on a line on its own, in checkfile.
+ with open(checkfile, 'r') as f:
+ if present:
+ self.assertIn(expected + '\n', f, message)
+ else:
+ self.assertNotIn(expected + '\n', f, message)
+
+ modfile = os.path.join(tempdir, 'mdadm.8.in')
+ bb_vars = get_bb_vars(['PKGD', 'mandir'], 'mdadm')
+ pkgd = bb_vars['PKGD']
+ self.assertTrue(pkgd, 'Could not query PKGD variable')
+ mandir = bb_vars['mandir']
+ self.assertTrue(mandir, 'Could not query mandir variable')
+ manfile = oe.path.join(pkgd, mandir, 'man8', 'mdadm.8')
+
+ check_line(modfile, 'Linux Software RAID', 'Could not find initial string')
+ check_line(modfile, 'antique pin sardine', 'Unexpectedly found replacement string', present=False)
+
+ result = runCmd("sed -i 's!^Linux Software RAID$!antique pin sardine!' %s" % modfile)
+ check_line(modfile, 'antique pin sardine', 'mdadm.8.in file not modified (sed failed)')
+
+ bitbake('mdadm -c package')
+ check_line(manfile, 'antique pin sardine', 'man file not modified. man searched file path: %s' % manfile)
+
+ result = runCmd('git checkout -- %s' % modfile, cwd=tempdir)
+ check_line(modfile, 'Linux Software RAID', 'man .in file not restored (git failed)')
+
+ bitbake('mdadm -c package')
+ check_line(manfile, 'Linux Software RAID', 'man file not updated. man searched file path: %s' % manfile)
+
+ result = runCmd('devtool reset mdadm')
+ result = runCmd('devtool status')
+ self.assertNotIn('mdadm', result.output)
+
+ def test_devtool_buildclean(self):
+ def assertFile(path, *paths):
+ f = os.path.join(path, *paths)
+ self.assertTrue(os.path.exists(f), "%r does not exist" % f)
+ def assertNoFile(path, *paths):
+ f = os.path.join(path, *paths)
+ self.assertFalse(os.path.exists(os.path.join(f)), "%r exists" % f)
+
+ # Clean up anything in the workdir/sysroot/sstate cache
+ bitbake('mdadm m4 -c cleansstate')
+ # Try modifying a recipe
+ tempdir_mdadm = tempfile.mkdtemp(prefix='devtoolqa')
+ tempdir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
+ builddir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir_mdadm)
+ self.track_for_cleanup(tempdir_m4)
+ self.track_for_cleanup(builddir_m4)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean mdadm m4')
+ self.write_recipeinc('m4', 'EXTERNALSRC_BUILD = "%s"\ndo_clean() {\n\t:\n}\n' % builddir_m4)
+ try:
+ runCmd('devtool modify mdadm -x %s' % tempdir_mdadm)
+ runCmd('devtool modify m4 -x %s' % tempdir_m4)
+ assertNoFile(tempdir_mdadm, 'mdadm')
+ assertNoFile(builddir_m4, 'src/m4')
+ result = bitbake('m4 -e')
+ result = bitbake('mdadm m4 -c compile')
+ self.assertEqual(result.status, 0)
+ assertFile(tempdir_mdadm, 'mdadm')
+ assertFile(builddir_m4, 'src/m4')
+ # Check that buildclean task exists and does call make clean
+ bitbake('mdadm m4 -c buildclean')
+ assertNoFile(tempdir_mdadm, 'mdadm')
+ assertNoFile(builddir_m4, 'src/m4')
+ bitbake('mdadm m4 -c compile')
+ assertFile(tempdir_mdadm, 'mdadm')
+ assertFile(builddir_m4, 'src/m4')
+ bitbake('mdadm m4 -c clean')
+ # Check that buildclean task is run before clean for B == S
+ assertNoFile(tempdir_mdadm, 'mdadm')
+ # Check that buildclean task is not run before clean for B != S
+ assertFile(builddir_m4, 'src/m4')
+ finally:
+ self.delete_recipeinc('m4')
+
+ @OETestID(1166)
+ def test_devtool_modify_invalid(self):
+ # Try modifying some recipes
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+
+ testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk meta-ide-support'.split()
+ # Find actual name of gcc-source since it now includes the version - crude, but good enough for this purpose
+ result = runCmd('bitbake-layers show-recipes gcc-source*')
+ for line in result.output.splitlines():
+ # just match those lines that contain a real target
+ m = re.match('(?P<recipe>^[a-zA-Z0-9.-]+)(?P<colon>:$)', line)
+ if m:
+ testrecipes.append(m.group('recipe'))
+ for testrecipe in testrecipes:
+ # Check it's a valid recipe
+ bitbake('%s -e' % testrecipe)
+ # devtool extract should fail
+ result = runCmd('devtool extract %s %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
+ self.assertNotEqual(result.status, 0, 'devtool extract on %s should have failed. devtool output: %s' % (testrecipe, result.output))
+ self.assertNotIn('Fetching ', result.output, 'devtool extract on %s should have errored out before trying to fetch' % testrecipe)
+ self.assertIn('ERROR: ', result.output, 'devtool extract on %s should have given an ERROR' % testrecipe)
+ # devtool modify should fail
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
+ self.assertNotEqual(result.status, 0, 'devtool modify on %s should have failed. devtool output: %s' % (testrecipe, result.output))
+ self.assertIn('ERROR: ', result.output, 'devtool modify on %s should have given an ERROR' % testrecipe)
+
+ @OETestID(1365)
+ def test_devtool_modify_native(self):
+ # Check preconditions
+ self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ # Try modifying some recipes
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+
+ bbclassextended = False
+ inheritnative = False
+ testrecipes = 'mtools-native apt-native desktop-file-utils-native'.split()
+ for testrecipe in testrecipes:
+ checkextend = 'native' in (get_bb_var('BBCLASSEXTEND', testrecipe) or '').split()
+ if not bbclassextended:
+ bbclassextended = checkextend
+ if not inheritnative:
+ inheritnative = not checkextend
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)))
+ self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool modify output: %s' % result.output)
+ result = runCmd('devtool build %s' % testrecipe)
+ self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool build output: %s' % result.output)
+ result = runCmd('devtool reset %s' % testrecipe)
+ self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool reset output: %s' % result.output)
+
+ self.assertTrue(bbclassextended, 'None of these recipes are BBCLASSEXTENDed to native - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
+ self.assertTrue(inheritnative, 'None of these recipes do "inherit native" - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
+
+
+ @OETestID(1165)
+ def test_devtool_modify_git(self):
+ # Check preconditions
+ testrecipe = 'mkelfimage'
+ src_uri = get_bb_var('SRC_URI', testrecipe)
+ self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
+ # Clean up anything in the workdir/sysroot/sstate cache
+ bitbake('%s -c cleansstate' % testrecipe)
+ # Try modifying a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created. devtool output: %s' % result.output)
+ matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'mkelfimage_*.bbappend'))
+ self.assertTrue(matches, 'bbappend not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(testrecipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Check git repo
+ self._check_src_repo(tempdir)
+ # Try building
+ bitbake(testrecipe)
+
+ @OETestID(1167)
+ def test_devtool_modify_localfiles(self):
+ # Check preconditions
+ testrecipe = 'lighttpd'
+ src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split()
+ foundlocal = False
+ for item in src_uri:
+ if item.startswith('file://') and '.patch' not in item:
+ foundlocal = True
+ break
+ self.assertTrue(foundlocal, 'This test expects the %s recipe to fetch local files and it seems that it no longer does' % testrecipe)
+ # Clean up anything in the workdir/sysroot/sstate cache
+ bitbake('%s -c cleansstate' % testrecipe)
+ # Try modifying a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'configure.ac')), 'Extracted source could not be found')
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
+ self.assertTrue(matches, 'bbappend not created')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(testrecipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Try building
+ bitbake(testrecipe)
+
+ @OETestID(1378)
+ def test_devtool_modify_virtual(self):
+ # Try modifying a virtual recipe
+ virtrecipe = 'virtual/make'
+ realrecipe = 'make'
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool modify %s -x %s' % (virtrecipe, tempdir))
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile.am')), 'Extracted source could not be found')
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
+ matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % realrecipe))
+ self.assertTrue(matches, 'bbappend not created %s' % result.output)
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertNotIn(virtrecipe, result.output)
+ self.assertIn(realrecipe, result.output)
+ # Check git repo
+ self._check_src_repo(tempdir)
+ # This is probably sufficient
+
+
+ @OETestID(1169)
+ def test_devtool_update_recipe(self):
+ # Check preconditions
+ testrecipe = 'minicom'
+ bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+ recipefile = bb_vars['FILE']
+ src_uri = bb_vars['SRC_URI']
+ self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
+ self._check_repo_status(os.path.dirname(recipefile), [])
+ # First, modify a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be building it)
+ # We don't use -x here so that we test the behaviour of devtool modify without it
+ result = runCmd('devtool modify %s %s' % (testrecipe, tempdir))
+ # Check git repo
+ self._check_src_repo(tempdir)
+ # Add a couple of commits
+ # FIXME: this only tests adding, need to also test update and remove
+ result = runCmd('echo "Additional line" >> README', cwd=tempdir)
+ result = runCmd('git commit -a -m "Change the README"', cwd=tempdir)
+ result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
+ result = runCmd('git add devtool-new-file', cwd=tempdir)
+ result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
+ self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
+ result = runCmd('devtool update-recipe %s' % testrecipe)
+ expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
+ ('??', '.*/0001-Change-the-README.patch$'),
+ ('??', '.*/0002-Add-a-new-file.patch$')]
+ self._check_repo_status(os.path.dirname(recipefile), expected_status)
+
+ @OETestID(1172)
+ def test_devtool_update_recipe_git(self):
+ # Check preconditions
+ testrecipe = 'mtd-utils'
+ bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+ recipefile = bb_vars['FILE']
+ src_uri = bb_vars['SRC_URI']
+ self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
+ patches = []
+ for entry in src_uri.split():
+ if entry.startswith('file://') and entry.endswith('.patch'):
+ patches.append(entry[7:].split(';')[0])
+ self.assertGreater(len(patches), 0, 'The %s recipe does not appear to contain any patches, so this test will not be effective' % testrecipe)
+ self._check_repo_status(os.path.dirname(recipefile), [])
+ # First, modify a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be building it)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ # Check git repo
+ self._check_src_repo(tempdir)
+ # Add a couple of commits
+ # FIXME: this only tests adding, need to also test update and remove
+ result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempdir)
+ result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir)
+ result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
+ result = runCmd('git add devtool-new-file', cwd=tempdir)
+ result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
+ self.add_command_to_tearDown('cd %s; rm -rf %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
+ result = runCmd('devtool update-recipe -m srcrev %s' % testrecipe)
+ expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile))] + \
+ [(' D', '.*/%s$' % patch) for patch in patches]
+ self._check_repo_status(os.path.dirname(recipefile), expected_status)
+
+ result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
+ addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git"']
+ srcurilines = src_uri.split()
+ srcurilines[0] = 'SRC_URI = "' + srcurilines[0]
+ srcurilines.append('"')
+ removelines = ['SRCREV = ".*"'] + srcurilines
+ for line in result.output.splitlines():
+ if line.startswith('+++') or line.startswith('---'):
+ continue
+ elif line.startswith('+'):
+ matched = False
+ for item in addlines:
+ if re.match(item, line[1:].strip()):
+ matched = True
+ break
+ self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
+ elif line.startswith('-'):
+ matched = False
+ for item in removelines:
+ if re.match(item, line[1:].strip()):
+ matched = True
+ break
+ self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
+ # Now try with auto mode
+ runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile)))
+ result = runCmd('devtool update-recipe %s' % testrecipe)
+ result = runCmd('git rev-parse --show-toplevel', cwd=os.path.dirname(recipefile))
+ topleveldir = result.output.strip()
+ relpatchpath = os.path.join(os.path.relpath(os.path.dirname(recipefile), topleveldir), testrecipe)
+ expected_status = [(' M', os.path.relpath(recipefile, topleveldir)),
+ ('??', '%s/0001-Change-the-Makefile.patch' % relpatchpath),
+ ('??', '%s/0002-Add-a-new-file.patch' % relpatchpath)]
+ self._check_repo_status(os.path.dirname(recipefile), expected_status)
+
+ @OETestID(1170)
+ def test_devtool_update_recipe_append(self):
+ # Check preconditions
+ testrecipe = 'mdadm'
+ bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+ recipefile = bb_vars['FILE']
+ src_uri = bb_vars['SRC_URI']
+ self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
+ self._check_repo_status(os.path.dirname(recipefile), [])
+ # First, modify a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ tempsrcdir = os.path.join(tempdir, 'source')
+ templayerdir = os.path.join(tempdir, 'layer')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be building it)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
+ # Check git repo
+ self._check_src_repo(tempsrcdir)
+ # Add a commit
+ result = runCmd("sed 's!\\(#define VERSION\\W*\"[^\"]*\\)\"!\\1-custom\"!' -i ReadMe.c", cwd=tempsrcdir)
+ result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
+ self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
+ # Create a temporary layer and add it to bblayers.conf
+ self._create_temp_layer(templayerdir, True, 'selftestupdaterecipe')
+ # Create the bbappend
+ result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
+ self.assertNotIn('WARNING:', result.output)
+ # Check recipe is still clean
+ self._check_repo_status(os.path.dirname(recipefile), [])
+ # Check bbappend was created
+ splitpath = os.path.dirname(recipefile).split(os.sep)
+ appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
+ bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
+ patchfile = os.path.join(appenddir, testrecipe, '0001-Add-our-custom-version.patch')
+ self.assertTrue(os.path.exists(patchfile), 'Patch file not created')
+
+ # Check bbappend contents
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n',
+ '\n']
+ with open(bbappendfile, 'r') as f:
+ self.assertEqual(expectedlines, f.readlines())
+
+ # Check we can run it again and bbappend isn't modified
+ result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
+ with open(bbappendfile, 'r') as f:
+ self.assertEqual(expectedlines, f.readlines())
+ # Drop new commit and check patch gets deleted
+ result = runCmd('git reset HEAD^', cwd=tempsrcdir)
+ result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
+ self.assertFalse(os.path.exists(patchfile), 'Patch file not deleted')
+ expectedlines2 = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n']
+ with open(bbappendfile, 'r') as f:
+ self.assertEqual(expectedlines2, f.readlines())
+ # Put commit back and check we can run it if layer isn't in bblayers.conf
+ os.remove(bbappendfile)
+ result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
+ result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
+ result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
+ self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
+ self.assertTrue(os.path.exists(patchfile), 'Patch file not created (with disabled layer)')
+ with open(bbappendfile, 'r') as f:
+ self.assertEqual(expectedlines, f.readlines())
+ # Deleting isn't expected to work under these circumstances
+
+ @OETestID(1171)
+ def test_devtool_update_recipe_append_git(self):
+ # Check preconditions
+ testrecipe = 'mtd-utils'
+ bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+ recipefile = bb_vars['FILE']
+ src_uri = bb_vars['SRC_URI']
+ self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
+ for entry in src_uri.split():
+ if entry.startswith('git://'):
+ git_uri = entry
+ break
+ self._check_repo_status(os.path.dirname(recipefile), [])
+ # First, modify a recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ tempsrcdir = os.path.join(tempdir, 'source')
+ templayerdir = os.path.join(tempdir, 'layer')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be building it)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
+ # Check git repo
+ self._check_src_repo(tempsrcdir)
+ # Add a commit
+ result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir)
+ result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
+ self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
+ # Create a temporary layer
+ os.makedirs(os.path.join(templayerdir, 'conf'))
+ with open(os.path.join(templayerdir, 'conf', 'layer.conf'), 'w') as f:
+ f.write('BBPATH .= ":${LAYERDIR}"\n')
+ f.write('BBFILES += "${LAYERDIR}/recipes-*/*/*.bbappend"\n')
+ f.write('BBFILE_COLLECTIONS += "oeselftesttemplayer"\n')
+ f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n')
+ f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n')
+ f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n')
+ self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
+ result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
+ # Create the bbappend
+ result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
+ self.assertNotIn('WARNING:', result.output)
+ # Check recipe is still clean
+ self._check_repo_status(os.path.dirname(recipefile), [])
+ # Check bbappend was created
+ splitpath = os.path.dirname(recipefile).split(os.sep)
+ appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
+ bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
+ self.assertFalse(os.path.exists(os.path.join(appenddir, testrecipe)), 'Patch directory should not be created')
+
+ # Check bbappend contents
+ result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
+ expectedlines = set(['SRCREV = "%s"\n' % result.output,
+ '\n',
+ 'SRC_URI = "%s"\n' % git_uri,
+ '\n'])
+ with open(bbappendfile, 'r') as f:
+ self.assertEqual(expectedlines, set(f.readlines()))
+
+ # Check we can run it again and bbappend isn't modified
+ result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
+ with open(bbappendfile, 'r') as f:
+ self.assertEqual(expectedlines, set(f.readlines()))
+ # Drop new commit and check SRCREV changes
+ result = runCmd('git reset HEAD^', cwd=tempsrcdir)
+ result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
+ self.assertFalse(os.path.exists(os.path.join(appenddir, testrecipe)), 'Patch directory should not be created')
+ result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
+ expectedlines = set(['SRCREV = "%s"\n' % result.output,
+ '\n',
+ 'SRC_URI = "%s"\n' % git_uri,
+ '\n'])
+ with open(bbappendfile, 'r') as f:
+ self.assertEqual(expectedlines, set(f.readlines()))
+ # Put commit back and check we can run it if layer isn't in bblayers.conf
+ os.remove(bbappendfile)
+ result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
+ result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
+ result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
+ self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
+ self.assertFalse(os.path.exists(os.path.join(appenddir, testrecipe)), 'Patch directory should not be created')
+ result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
+ expectedlines = set(['SRCREV = "%s"\n' % result.output,
+ '\n',
+ 'SRC_URI = "%s"\n' % git_uri,
+ '\n'])
+ with open(bbappendfile, 'r') as f:
+ self.assertEqual(expectedlines, set(f.readlines()))
+ # Deleting isn't expected to work under these circumstances
+
+ @OETestID(1370)
+ def test_devtool_update_recipe_local_files(self):
+ """Check that local source files are copied over instead of patched"""
+ testrecipe = 'makedevs'
+ recipefile = get_bb_var('FILE', testrecipe)
+ # Setup srctree for modifying the recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be
+ # building it)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ # Check git repo
+ self._check_src_repo(tempdir)
+ # Try building just to ensure we haven't broken that
+ bitbake("%s" % testrecipe)
+ # Edit / commit local source
+ runCmd('echo "/* Foobar */" >> oe-local-files/makedevs.c', cwd=tempdir)
+ runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir)
+ runCmd('echo "Bar" > new-file', cwd=tempdir)
+ runCmd('git add new-file', cwd=tempdir)
+ runCmd('git commit -m "Add new file"', cwd=tempdir)
+ self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
+ os.path.dirname(recipefile))
+ runCmd('devtool update-recipe %s' % testrecipe)
+ expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
+ (' M', '.*/makedevs/makedevs.c$'),
+ ('??', '.*/makedevs/new-local$'),
+ ('??', '.*/makedevs/0001-Add-new-file.patch$')]
+ self._check_repo_status(os.path.dirname(recipefile), expected_status)
+
+ @OETestID(1371)
+ def test_devtool_update_recipe_local_files_2(self):
+ """Check local source files support when oe-local-files is in Git"""
+ testrecipe = 'lzo'
+ recipefile = get_bb_var('FILE', testrecipe)
+ # Setup srctree for modifying the recipe
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ # Check git repo
+ self._check_src_repo(tempdir)
+ # Add oe-local-files to Git
+ runCmd('rm oe-local-files/.gitignore', cwd=tempdir)
+ runCmd('git add oe-local-files', cwd=tempdir)
+ runCmd('git commit -m "Add local sources"', cwd=tempdir)
+ # Edit / commit local sources
+ runCmd('echo "# Foobar" >> oe-local-files/acinclude.m4', cwd=tempdir)
+ runCmd('git commit -am "Edit existing file"', cwd=tempdir)
+ runCmd('git rm oe-local-files/run-ptest', cwd=tempdir)
+ runCmd('git commit -m"Remove file"', cwd=tempdir)
+ runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir)
+ runCmd('git add oe-local-files/new-local', cwd=tempdir)
+ runCmd('git commit -m "Add new local file"', cwd=tempdir)
+ runCmd('echo "Gar" > new-file', cwd=tempdir)
+ runCmd('git add new-file', cwd=tempdir)
+ runCmd('git commit -m "Add new file"', cwd=tempdir)
+ self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
+ os.path.dirname(recipefile))
+ # Checkout unmodified file to working copy -> devtool should still pick
+ # the modified version from HEAD
+ runCmd('git checkout HEAD^ -- oe-local-files/acinclude.m4', cwd=tempdir)
+ runCmd('devtool update-recipe %s' % testrecipe)
+ expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
+ (' M', '.*/acinclude.m4$'),
+ (' D', '.*/run-ptest$'),
+ ('??', '.*/new-local$'),
+ ('??', '.*/0001-Add-new-file.patch$')]
+ self._check_repo_status(os.path.dirname(recipefile), expected_status)
+
+ def test_devtool_update_recipe_local_files_3(self):
+ # First, modify the recipe
+ testrecipe = 'devtool-test-localonly'
+ bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+ recipefile = bb_vars['FILE']
+ src_uri = bb_vars['SRC_URI']
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be building it)
+ result = runCmd('devtool modify %s' % testrecipe)
+ # Modify one file
+ runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe, 'oe-local-files'))
+ self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
+ result = runCmd('devtool update-recipe %s' % testrecipe)
+ expected_status = [(' M', '.*/%s/file2$' % testrecipe)]
+ self._check_repo_status(os.path.dirname(recipefile), expected_status)
+
+ def test_devtool_update_recipe_local_patch_gz(self):
+ # First, modify the recipe
+ testrecipe = 'devtool-test-patch-gz'
+ if get_bb_var('DISTRO') == 'poky-tiny':
+ self.skipTest("The DISTRO 'poky-tiny' does not provide the dependencies needed by %s" % testrecipe)
+ bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+ recipefile = bb_vars['FILE']
+ src_uri = bb_vars['SRC_URI']
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be building it)
+ result = runCmd('devtool modify %s' % testrecipe)
+ # Modify one file
+ srctree = os.path.join(self.workspacedir, 'sources', testrecipe)
+ runCmd('echo "Another line" >> README', cwd=srctree)
+ runCmd('git commit -a --amend --no-edit', cwd=srctree)
+ self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
+ result = runCmd('devtool update-recipe %s' % testrecipe)
+ expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)]
+ self._check_repo_status(os.path.dirname(recipefile), expected_status)
+ patch_gz = os.path.join(os.path.dirname(recipefile), testrecipe, 'readme.patch.gz')
+ result = runCmd('file %s' % patch_gz)
+ if 'gzip compressed data' not in result.output:
+ self.fail('New patch file is not gzipped - file reports:\n%s' % result.output)
+
+ def test_devtool_update_recipe_local_files_subdir(self):
+ # Try devtool extract on a recipe that has a file with subdir= set in
+ # SRC_URI such that it overwrites a file that was in an archive that
+ # was also in SRC_URI
+ # First, modify the recipe
+ testrecipe = 'devtool-test-subdir'
+ bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
+ recipefile = bb_vars['FILE']
+ src_uri = bb_vars['SRC_URI']
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # (don't bother with cleaning the recipe on teardown, we won't be building it)
+ result = runCmd('devtool modify %s' % testrecipe)
+ testfile = os.path.join(self.workspacedir, 'sources', testrecipe, 'testfile')
+ self.assertTrue(os.path.exists(testfile), 'Extracted source could not be found')
+ with open(testfile, 'r') as f:
+ contents = f.read().rstrip()
+ self.assertEqual(contents, 'Modified version', 'File has apparently not been overwritten as it should have been')
+ # Test devtool update-recipe without modifying any files
+ self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
+ result = runCmd('devtool update-recipe %s' % testrecipe)
+ expected_status = []
+ self._check_repo_status(os.path.dirname(recipefile), expected_status)
+
+ @OETestID(1163)
+ def test_devtool_extract(self):
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ # Try devtool extract
+ self.track_for_cleanup(tempdir)
+ self.append_config('PREFERRED_PROVIDER_virtual/make = "remake"')
+ result = runCmd('devtool extract remake %s' % tempdir)
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile.am')), 'Extracted source could not be found')
+ # devtool extract shouldn't create the workspace
+ self.assertFalse(os.path.exists(self.workspacedir))
+ self._check_src_repo(tempdir)
+
+ @OETestID(1379)
+ def test_devtool_extract_virtual(self):
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ # Try devtool extract
+ self.track_for_cleanup(tempdir)
+ result = runCmd('devtool extract virtual/make %s' % tempdir)
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile.am')), 'Extracted source could not be found')
+ # devtool extract shouldn't create the workspace
+ self.assertFalse(os.path.exists(self.workspacedir))
+ self._check_src_repo(tempdir)
+
+ @OETestID(1168)
+ def test_devtool_reset_all(self):
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ testrecipe1 = 'mdadm'
+ testrecipe2 = 'cronie'
+ result = runCmd('devtool modify -x %s %s' % (testrecipe1, os.path.join(tempdir, testrecipe1)))
+ result = runCmd('devtool modify -x %s %s' % (testrecipe2, os.path.join(tempdir, testrecipe2)))
+ result = runCmd('devtool build %s' % testrecipe1)
+ result = runCmd('devtool build %s' % testrecipe2)
+ stampprefix1 = get_bb_var('STAMP', testrecipe1)
+ self.assertTrue(stampprefix1, 'Unable to get STAMP value for recipe %s' % testrecipe1)
+ stampprefix2 = get_bb_var('STAMP', testrecipe2)
+ self.assertTrue(stampprefix2, 'Unable to get STAMP value for recipe %s' % testrecipe2)
+ result = runCmd('devtool reset -a')
+ self.assertIn(testrecipe1, result.output)
+ self.assertIn(testrecipe2, result.output)
+ result = runCmd('devtool status')
+ self.assertNotIn(testrecipe1, result.output)
+ self.assertNotIn(testrecipe2, result.output)
+ matches1 = glob.glob(stampprefix1 + '*')
+ self.assertFalse(matches1, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe1)
+ matches2 = glob.glob(stampprefix2 + '*')
+ self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2)
+
+ @OETestID(1272)
+ def test_devtool_deploy_target(self):
+ # NOTE: Whilst this test would seemingly be better placed as a runtime test,
+ # unfortunately the runtime tests run under bitbake and you can't run
+ # devtool within bitbake (since devtool needs to run bitbake itself).
+ # Additionally we are testing build-time functionality as well, so
+ # really this has to be done as an oe-selftest test.
+ #
+ # Check preconditions
+ machine = get_bb_var('MACHINE')
+ if not machine.startswith('qemu'):
+ self.skipTest('This test only works with qemu machines')
+ if not os.path.exists('/etc/runqemu-nosudo'):
+ self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
+ result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ip tuntap show', ignore_status=True)
+ if result.status != 0:
+ result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ifconfig -a', ignore_status=True)
+ if result.status != 0:
+ self.skipTest('Failed to determine if tap devices exist with ifconfig or ip: %s' % result.output)
+ for line in result.output.splitlines():
+ if line.startswith('tap'):
+ break
+ else:
+ self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
+ self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ # Definitions
+ testrecipe = 'mdadm'
+ testfile = '/sbin/mdadm'
+ testimage = 'oe-selftest-image'
+ testcommand = '/sbin/mdadm --help'
+ # Build an image to run
+ bitbake("%s qemu-native qemu-helper-native" % testimage)
+ deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
+ self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
+ self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage))
+ # Clean recipe so the first deploy will fail
+ bitbake("%s -c clean" % testrecipe)
+ # Try devtool modify
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
+ result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
+ # Test that deploy-target at this point fails (properly)
+ result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe, ignore_status=True)
+ self.assertNotEqual(result.output, 0, 'devtool deploy-target should have failed, output: %s' % result.output)
+ self.assertNotIn(result.output, 'Traceback', 'devtool deploy-target should have failed with a proper error not a traceback, output: %s' % result.output)
+ result = runCmd('devtool build %s' % testrecipe)
+ # First try a dry-run of deploy-target
+ result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe)
+ self.assertIn(' %s' % testfile, result.output)
+ # Boot the image
+ with runqemu(testimage) as qemu:
+ # Now really test deploy-target
+ result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, qemu.ip))
+ # Run a test command to see if it was installed properly
+ sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
+ result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand))
+ # Check if it deployed all of the files with the right ownership/perms
+ # First look on the host - need to do this under pseudo to get the correct ownership/perms
+ bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD'], testrecipe)
+ installdir = bb_vars['D']
+ fakerootenv = bb_vars['FAKEROOTENV']
+ fakerootcmd = bb_vars['FAKEROOTCMD']
+ result = runCmd('%s %s find . -type f -exec ls -l {} \;' % (fakerootenv, fakerootcmd), cwd=installdir)
+ filelist1 = self._process_ls_output(result.output)
+
+ # Now look on the target
+ tempdir2 = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir2)
+ tmpfilelist = os.path.join(tempdir2, 'files.txt')
+ with open(tmpfilelist, 'w') as f:
+ for line in filelist1:
+ splitline = line.split()
+ f.write(splitline[-1] + '\n')
+ result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, qemu.ip))
+ filelist2 = self._process_ls_output(result.output)
+ filelist1.sort(key=lambda item: item.split()[-1])
+ filelist2.sort(key=lambda item: item.split()[-1])
+ self.assertEqual(filelist1, filelist2)
+ # Test undeploy-target
+ result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip))
+ result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True)
+ self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
+
+ @OETestID(1366)
+ def test_devtool_build_image(self):
+ """Test devtool build-image plugin"""
+ # Check preconditions
+ self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ image = 'core-image-minimal'
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean %s' % image)
+ bitbake('%s -c clean' % image)
+ # Add target and native recipes to workspace
+ recipes = ['mdadm', 'parted-native']
+ for recipe in recipes:
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.add_command_to_tearDown('bitbake -c clean %s' % recipe)
+ runCmd('devtool modify %s -x %s' % (recipe, tempdir))
+ # Try to build image
+ result = runCmd('devtool build-image %s' % image)
+ self.assertNotEqual(result, 0, 'devtool build-image failed')
+ # Check if image contains expected packages
+ deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
+ image_link_name = get_bb_var('IMAGE_LINK_NAME', image)
+ reqpkgs = [item for item in recipes if not item.endswith('-native')]
+ with open(os.path.join(deploy_dir_image, image_link_name + '.manifest'), 'r') as f:
+ for line in f:
+ splitval = line.split()
+ if splitval:
+ pkg = splitval[0]
+ if pkg in reqpkgs:
+ reqpkgs.remove(pkg)
+ if reqpkgs:
+ self.fail('The following packages were not present in the image as expected: %s' % ', '.join(reqpkgs))
+
+ @OETestID(1367)
+ def test_devtool_upgrade(self):
+ # Check preconditions
+ self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # Check parameters
+ result = runCmd('devtool upgrade -h')
+ for param in 'recipename srctree --version -V --branch -b --keep-temp --no-patch'.split():
+ self.assertIn(param, result.output)
+ # For the moment, we are using a real recipe.
+ recipe = 'devtool-upgrade-test1'
+ version = '1.6.0'
+ oldrecipefile = get_bb_var('FILE', recipe)
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ # Check that recipe is not already under devtool control
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output)
+ # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
+ # we are downgrading instead of upgrading.
+ result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
+ # Check if srctree at least is populated
+ self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, version))
+ # Check new recipe subdirectory is present
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe, '%s-%s' % (recipe, version))), 'Recipe folder should exist')
+ # Check new recipe file is present
+ newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
+ self.assertTrue(os.path.exists(newrecipefile), 'Recipe file should exist after upgrade')
+ # Check devtool status and make sure recipe is present
+ result = runCmd('devtool status')
+ self.assertIn(recipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Check recipe got changed as expected
+ with open(oldrecipefile + '.upgraded', 'r') as f:
+ desiredlines = f.readlines()
+ with open(newrecipefile, 'r') as f:
+ newlines = f.readlines()
+ self.assertEqual(desiredlines, newlines)
+ # Check devtool reset recipe
+ result = runCmd('devtool reset %s -n' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output)
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after resetting')
+
+ @OETestID(1433)
+ def test_devtool_upgrade_git(self):
+ # Check preconditions
+ self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ recipe = 'devtool-upgrade-test2'
+ commit = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
+ oldrecipefile = get_bb_var('FILE', recipe)
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ # Check that recipe is not already under devtool control
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output)
+ # Check upgrade
+ result = runCmd('devtool upgrade %s %s -S %s' % (recipe, tempdir, commit))
+ # Check if srctree at least is populated
+ self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit))
+ # Check new recipe file is present
+ newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile))
+ self.assertTrue(os.path.exists(newrecipefile), 'Recipe file should exist after upgrade')
+ # Check devtool status and make sure recipe is present
+ result = runCmd('devtool status')
+ self.assertIn(recipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Check recipe got changed as expected
+ with open(oldrecipefile + '.upgraded', 'r') as f:
+ desiredlines = f.readlines()
+ with open(newrecipefile, 'r') as f:
+ newlines = f.readlines()
+ self.assertEqual(desiredlines, newlines)
+ # Check devtool reset recipe
+ result = runCmd('devtool reset %s -n' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output)
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after resetting')
+
+ @OETestID(1352)
+ def test_devtool_layer_plugins(self):
+ """Test that devtool can use plugins from other layers.
+
+ This test executes the selftest-reverse command from meta-selftest."""
+
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+
+ s = "Microsoft Made No Profit From Anyone's Zunes Yo"
+ result = runCmd("devtool --quiet selftest-reverse \"%s\"" % s)
+ self.assertEqual(result.output, s[::-1])
+
+ def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
+ dstdir = basedstdir
+ self.assertTrue(os.path.exists(dstdir))
+ for p in paths:
+ dstdir = os.path.join(dstdir, p)
+ if not os.path.exists(dstdir):
+ os.makedirs(dstdir)
+ self.track_for_cleanup(dstdir)
+ dstfile = os.path.join(dstdir, os.path.basename(srcfile))
+ if srcfile != dstfile:
+ shutil.copy(srcfile, dstfile)
+ self.track_for_cleanup(dstfile)
+
+ def test_devtool_load_plugin(self):
+ """Test that devtool loads only the first found plugin in BBPATH."""
+
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+
+ devtool = runCmd("which devtool")
+ fromname = runCmd("devtool --quiet pluginfile")
+ srcfile = fromname.output
+ bbpath = get_bb_var('BBPATH')
+ searchpath = bbpath.split(':') + [os.path.dirname(devtool.output)]
+ plugincontent = []
+ with open(srcfile) as fh:
+ plugincontent = fh.readlines()
+ try:
+ self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
+ for path in searchpath:
+ self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool')
+ result = runCmd("devtool --quiet count")
+ self.assertEqual(result.output, '1')
+ result = runCmd("devtool --quiet multiloaded")
+ self.assertEqual(result.output, "no")
+ for path in searchpath:
+ result = runCmd("devtool --quiet bbdir")
+ self.assertEqual(result.output, path)
+ os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py'))
+ finally:
+ with open(srcfile, 'w') as fh:
+ fh.writelines(plugincontent)
+
+ def _setup_test_devtool_finish_upgrade(self):
+ # Check preconditions
+ self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ # Use a "real" recipe from meta-selftest
+ recipe = 'devtool-upgrade-test1'
+ oldversion = '1.5.3'
+ newversion = '1.6.0'
+ oldrecipefile = get_bb_var('FILE', recipe)
+ recipedir = os.path.dirname(oldrecipefile)
+ result = runCmd('git status --porcelain .', cwd=recipedir)
+ if result.output.strip():
+ self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ # Check that recipe is not already under devtool control
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output)
+ # Do the upgrade
+ result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, newversion))
+ # Check devtool status and make sure recipe is present
+ result = runCmd('devtool status')
+ self.assertIn(recipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Make a change to the source
+ result = runCmd('sed -i \'/^#include "pv.h"/a \\/* Here is a new comment *\\/\' src/pv/number.c', cwd=tempdir)
+ result = runCmd('git status --porcelain', cwd=tempdir)
+ self.assertIn('M src/pv/number.c', result.output)
+ result = runCmd('git commit src/pv/number.c -m "Add a comment to the code"', cwd=tempdir)
+ # Check if patch is there
+ recipedir = os.path.dirname(oldrecipefile)
+ olddir = os.path.join(recipedir, recipe + '-' + oldversion)
+ patchfn = '0001-Add-a-note-line-to-the-quick-reference.patch'
+ self.assertTrue(os.path.exists(os.path.join(olddir, patchfn)), 'Original patch file does not exist')
+ return recipe, oldrecipefile, recipedir, olddir, newversion, patchfn
+
+ def test_devtool_finish_upgrade_origlayer(self):
+ recipe, oldrecipefile, recipedir, olddir, newversion, patchfn = self._setup_test_devtool_finish_upgrade()
+ # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
+ self.assertIn('/meta-selftest/', recipedir)
+ # Try finish to the original layer
+ self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
+ result = runCmd('devtool finish %s meta-selftest' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
+ self.assertFalse(os.path.exists(oldrecipefile), 'Old recipe file should have been deleted but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(olddir, patchfn)), 'Old patch file should have been deleted but wasn\'t')
+ newrecipefile = os.path.join(recipedir, '%s_%s.bb' % (recipe, newversion))
+ newdir = os.path.join(recipedir, recipe + '-' + newversion)
+ self.assertTrue(os.path.exists(newrecipefile), 'New recipe file should have been copied into existing layer but wasn\'t')
+ self.assertTrue(os.path.exists(os.path.join(newdir, patchfn)), 'Patch file should have been copied into new directory but wasn\'t')
+ self.assertTrue(os.path.exists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch')), 'New patch file should have been created but wasn\'t')
+
+ def test_devtool_finish_upgrade_otherlayer(self):
+ recipe, oldrecipefile, recipedir, olddir, newversion, patchfn = self._setup_test_devtool_finish_upgrade()
+ # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
+ self.assertIn('/meta-selftest/', recipedir)
+ # Try finish to a different layer - should create a bbappend
+ # This cleanup isn't strictly necessary but do it anyway just in case it goes wrong and writes to here
+ self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
+ oe_core_dir = os.path.join(get_bb_var('COREBASE'), 'meta')
+ newrecipedir = os.path.join(oe_core_dir, 'recipes-test', 'devtool')
+ newrecipefile = os.path.join(newrecipedir, '%s_%s.bb' % (recipe, newversion))
+ self.track_for_cleanup(newrecipedir)
+ result = runCmd('devtool finish %s oe-core' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
+ self.assertTrue(os.path.exists(oldrecipefile), 'Old recipe file should not have been deleted')
+ self.assertTrue(os.path.exists(os.path.join(olddir, patchfn)), 'Old patch file should not have been deleted')
+ newdir = os.path.join(newrecipedir, recipe + '-' + newversion)
+ self.assertTrue(os.path.exists(newrecipefile), 'New recipe file should have been copied into existing layer but wasn\'t')
+ self.assertTrue(os.path.exists(os.path.join(newdir, patchfn)), 'Patch file should have been copied into new directory but wasn\'t')
+ self.assertTrue(os.path.exists(os.path.join(newdir, '0002-Add-a-comment-to-the-code.patch')), 'New patch file should have been created but wasn\'t')
+
+ def _setup_test_devtool_finish_modify(self):
+ # Check preconditions
+ self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ # Try modifying a recipe
+ self.track_for_cleanup(self.workspacedir)
+ recipe = 'mdadm'
+ oldrecipefile = get_bb_var('FILE', recipe)
+ recipedir = os.path.dirname(oldrecipefile)
+ result = runCmd('git status --porcelain .', cwd=recipedir)
+ if result.output.strip():
+ self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ result = runCmd('devtool modify %s %s' % (recipe, tempdir))
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
+ # Test devtool status
+ result = runCmd('devtool status')
+ self.assertIn(recipe, result.output)
+ self.assertIn(tempdir, result.output)
+ # Make a change to the source
+ result = runCmd('sed -i \'/^#include "mdadm.h"/a \\/* Here is a new comment *\\/\' maps.c', cwd=tempdir)
+ result = runCmd('git status --porcelain', cwd=tempdir)
+ self.assertIn('M maps.c', result.output)
+ result = runCmd('git commit maps.c -m "Add a comment to the code"', cwd=tempdir)
+ for entry in os.listdir(recipedir):
+ filesdir = os.path.join(recipedir, entry)
+ if os.path.isdir(filesdir):
+ break
+ else:
+ self.fail('Unable to find recipe files directory for %s' % recipe)
+ return recipe, oldrecipefile, recipedir, filesdir
+
+ def test_devtool_finish_modify_origlayer(self):
+ recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
+ # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
+ self.assertIn('/meta/', recipedir)
+ # Try finish to the original layer
+ self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
+ result = runCmd('devtool finish %s meta' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
+ expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
+ ('??', '.*/.*-Add-a-comment-to-the-code.patch$')]
+ self._check_repo_status(recipedir, expected_status)
+
+ def test_devtool_finish_modify_otherlayer(self):
+ recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
+ # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
+ self.assertIn('/meta/', recipedir)
+ relpth = os.path.relpath(recipedir, os.path.join(get_bb_var('COREBASE'), 'meta'))
+ appenddir = os.path.join(get_test_layer(), relpth)
+ self.track_for_cleanup(appenddir)
+ # Try finish to the original layer
+ self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
+ result = runCmd('devtool finish %s meta-selftest' % recipe)
+ result = runCmd('devtool status')
+ self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
+ result = runCmd('git status --porcelain .', cwd=recipedir)
+ if result.output.strip():
+ self.fail('Recipe directory for %s contains the following unexpected changes after finish:\n%s' % (recipe, result.output.strip()))
+ recipefn = os.path.splitext(os.path.basename(oldrecipefile))[0]
+ recipefn = recipefn.split('_')[0] + '_%'
+ appendfile = os.path.join(appenddir, recipefn + '.bbappend')
+ self.assertTrue(os.path.exists(appendfile), 'bbappend %s should have been created but wasn\'t' % appendfile)
+ newdir = os.path.join(appenddir, recipe)
+ files = os.listdir(newdir)
+ foundpatch = None
+ for fn in files:
+ if fnmatch.fnmatch(fn, '*-Add-a-comment-to-the-code.patch'):
+ foundpatch = fn
+ if not foundpatch:
+ self.fail('No patch file created next to bbappend')
+ files.remove(foundpatch)
+ if files:
+ self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files))
+
+ def test_devtool_rename(self):
+ # Check preconditions
+ self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+
+ # First run devtool add
+ # We already have this recipe in OE-Core, but that doesn't matter
+ recipename = 'i2c-tools'
+ recipever = '3.1.2'
+ recipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, recipever))
+ url = 'http://downloads.yoctoproject.org/mirror/sources/i2c-tools-%s.tar.bz2' % recipever
+ def add_recipe():
+ result = runCmd('devtool add %s' % url)
+ self.assertTrue(os.path.exists(recipefile), 'Expected recipe file not created')
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'sources', recipename)), 'Source directory not created')
+ checkvars = {}
+ checkvars['S'] = None
+ checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
+ self._test_recipe_contents(recipefile, checkvars, [])
+ add_recipe()
+ # Now rename it - change both name and version
+ newrecipename = 'mynewrecipe'
+ newrecipever = '456'
+ newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, newrecipever))
+ result = runCmd('devtool rename %s %s -V %s' % (recipename, newrecipename, newrecipever))
+ self.assertTrue(os.path.exists(newrecipefile), 'Recipe file not renamed')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipename)), 'Old recipe directory still exists')
+ newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename)
+ self.assertTrue(os.path.exists(newsrctree), 'Source directory not renamed')
+ checkvars = {}
+ checkvars['S'] = '${WORKDIR}/%s-%s' % (recipename, recipever)
+ checkvars['SRC_URI'] = url
+ self._test_recipe_contents(newrecipefile, checkvars, [])
+ # Try again - change just name this time
+ result = runCmd('devtool reset -n %s' % newrecipename)
+ shutil.rmtree(newsrctree)
+ add_recipe()
+ newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever))
+ result = runCmd('devtool rename %s %s' % (recipename, newrecipename))
+ self.assertTrue(os.path.exists(newrecipefile), 'Recipe file not renamed')
+ self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipename)), 'Old recipe directory still exists')
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'sources', newrecipename)), 'Source directory not renamed')
+ checkvars = {}
+ checkvars['S'] = '${WORKDIR}/%s-${PV}' % recipename
+ checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
+ self._test_recipe_contents(newrecipefile, checkvars, [])
+ # Try again - change just version this time
+ result = runCmd('devtool reset -n %s' % newrecipename)
+ shutil.rmtree(newsrctree)
+ add_recipe()
+ newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever))
+ result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever))
+ self.assertTrue(os.path.exists(newrecipefile), 'Recipe file not renamed')
+ self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'sources', recipename)), 'Source directory no longer exists')
+ checkvars = {}
+ checkvars['S'] = '${WORKDIR}/${BPN}-%s' % recipever
+ checkvars['SRC_URI'] = url
+ self._test_recipe_contents(newrecipefile, checkvars, [])
+
+ @OETestID(1577)
+ def test_devtool_virtual_kernel_modify(self):
+ """
+ Summary: The purpose of this test case is to verify that
+ devtool modify works correctly when building
+ the kernel.
+ Dependencies: NA
+ Steps: 1. Build kernel with bitbake.
+ 2. Save the config file generated.
+ 3. Clean the environment.
+ 4. Use `devtool modify virtual/kernel` to validate following:
+ 4.1 The source is checked out correctly.
+ 4.2 The resulting configuration is the same as
+ what was get on step 2.
+ 4.3 The Kernel can be build correctly.
+ 4.4 Changes made on the source are reflected on the
+ subsequent builds.
+ 4.5 Changes on the configuration are reflected on the
+ subsequent builds
+ Expected: devtool modify is able to checkout the source of the kernel
+ and modification to the source and configurations are reflected
+ when building the kernel.
+ """
+ #Set machine to qemxu86 to be able to modify the kernel and
+ #verify the modification.
+ features = 'MACHINE = "qemux86"\n'
+ self.append_config(features)
+ kernel_provider = get_bb_var('PREFERRED_PROVIDER_virtual/kernel')
+ # Clean up the enviroment
+ bitbake('%s -c clean' % kernel_provider)
+ tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+ self.track_for_cleanup(tempdir)
+ self.track_for_cleanup(self.workspacedir)
+ self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+ self.add_command_to_tearDown('bitbake -c clean %s' % kernel_provider)
+ #Step 1
+ #Here is just generated the config file instead of all the kernel to optimize the
+ #time of executing this test case.
+ bitbake('%s -c configure' % kernel_provider)
+ bbconfig = os.path.join(get_bb_var('B', kernel_provider),'.config')
+ buildir= get_bb_var('TOPDIR')
+ #Step 2
+ runCmd('cp %s %s' % (bbconfig, buildir))
+ self.assertTrue(os.path.exists(os.path.join(buildir, '.config')),
+ 'Could not copy .config file from kernel')
+
+ tmpconfig = os.path.join(buildir, '.config')
+ #Step 3
+ bitbake('%s -c clean' % kernel_provider)
+ #Step 4.1
+ runCmd('devtool modify virtual/kernel -x %s' % tempdir)
+ self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')),
+ 'Extracted source could not be found')
+ #Step 4.2
+ configfile = os.path.join(tempdir,'.config')
+ diff = runCmd('diff %s %s' % (tmpconfig, configfile))
+ self.assertEqual(0,diff.status,'Kernel .config file is not the same using bitbake and devtool')
+ #Step 4.3
+ #NOTE: virtual/kernel is mapped to kernel_provider
+ result = runCmd('devtool build %s' % kernel_provider)
+ self.assertEqual(0,result.status,'Cannot build kernel using `devtool build`')
+ kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux')
+ self.assertTrue(os.path.exists(kernelfile),'Kernel was not build correctly')
+
+ #Modify the kernel source, this is specific for qemux86
+ modfile = os.path.join(tempdir,'arch/x86/boot/header.S')
+ modstring = "use a boot loader - Devtool kernel testing"
+ modapplied = runCmd("sed -i 's/boot loader/%s/' %s" % (modstring, modfile))
+ self.assertEqual(0,modapplied.status,'Modification to %s on kernel source failed' % modfile)
+ #Modify the configuration
+ codeconfigfile = os.path.join(tempdir,'.config.new')
+ modconfopt = "CONFIG_SG_POOL=n"
+ modconf = runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile))
+ self.assertEqual(0,modconf.status,'Modification to %s failed' % codeconfigfile)
+ #Build again kernel with devtool
+ rebuild = runCmd('devtool build %s' % kernel_provider)
+ self.assertEqual(0,rebuild.status,'Fail to build kernel after modification of source and config')
+ #Step 4.4
+ bzimagename = 'bzImage-' + get_bb_var('KERNEL_VERSION_NAME', kernel_provider)
+ bzimagefile = os.path.join(get_bb_var('D', kernel_provider),'boot', bzimagename)
+ checkmodcode = runCmd("grep '%s' %s" % (modstring, bzimagefile))
+ self.assertEqual(0,checkmodcode.status,'Modification on kernel source failed')
+ #Step 4.5
+ checkmodconfg = runCmd("grep %s %s" % (modconfopt, codeconfigfile))
+ self.assertEqual(0,checkmodconfg.status,'Modification to configuration file failed')
diff --git a/meta/lib/oeqa/selftest/cases/eSDK.py b/meta/lib/oeqa/selftest/cases/eSDK.py
new file mode 100644
index 0000000000..f36c3ccd3b
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/eSDK.py
@@ -0,0 +1,111 @@
+import tempfile
+import shutil
+import os
+import glob
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
+
+class oeSDKExtSelfTest(OESelftestTestCase):
+ """
+ # Bugzilla Test Plan: 6033
+ # This code is planned to be part of the automation for eSDK containig
+ # Install libraries and headers, image generation binary feeds, sdk-update.
+ """
+
+ @staticmethod
+ def get_esdk_environment(env_eSDK, tmpdir_eSDKQA):
+ # XXX: at this time use the first env need to investigate
+ # what environment load oe-selftest, i586, x86_64
+ pattern = os.path.join(tmpdir_eSDKQA, 'environment-setup-*')
+ return glob.glob(pattern)[0]
+
+ @staticmethod
+ def run_esdk_cmd(env_eSDK, tmpdir_eSDKQA, cmd, postconfig=None, **options):
+ if postconfig:
+ esdk_conf_file = os.path.join(tmpdir_eSDKQA, 'conf', 'local.conf')
+ with open(esdk_conf_file, 'a+') as f:
+ f.write(postconfig)
+ if not options:
+ options = {}
+ if not 'shell' in options:
+ options['shell'] = True
+
+ runCmd("cd %s; . %s; %s" % (tmpdir_eSDKQA, env_eSDK, cmd), **options)
+
+ @staticmethod
+ def generate_eSDK(image):
+ pn_task = '%s -c populate_sdk_ext' % image
+ bitbake(pn_task)
+
+ @staticmethod
+ def get_eSDK_toolchain(image):
+ pn_task = '%s -c populate_sdk_ext' % image
+
+ bb_vars = get_bb_vars(['SDK_DEPLOY', 'TOOLCHAINEXT_OUTPUTNAME'], pn_task)
+ sdk_deploy = bb_vars['SDK_DEPLOY']
+ toolchain_name = bb_vars['TOOLCHAINEXT_OUTPUTNAME']
+ return os.path.join(sdk_deploy, toolchain_name + '.sh')
+
+ @staticmethod
+ def update_configuration(cls, image, tmpdir_eSDKQA, env_eSDK, ext_sdk_path):
+ sstate_dir = os.path.join(os.environ['BUILDDIR'], 'sstate-cache')
+
+ oeSDKExtSelfTest.generate_eSDK(cls.image)
+
+ cls.ext_sdk_path = oeSDKExtSelfTest.get_eSDK_toolchain(cls.image)
+ runCmd("%s -y -d \"%s\"" % (cls.ext_sdk_path, cls.tmpdir_eSDKQA))
+
+ cls.env_eSDK = oeSDKExtSelfTest.get_esdk_environment('', cls.tmpdir_eSDKQA)
+
+ sstate_config="""
+SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS"
+SSTATE_MIRRORS = "file://.* file://%s/PATH"
+CORE_IMAGE_EXTRA_INSTALL = "perl"
+ """ % sstate_dir
+
+ with open(os.path.join(cls.tmpdir_eSDKQA, 'conf', 'local.conf'), 'a+') as f:
+ f.write(sstate_config)
+
+ @classmethod
+ def setUpClass(cls):
+ super(oeSDKExtSelfTest, cls).setUpClass()
+ cls.tmpdir_eSDKQA = tempfile.mkdtemp(prefix='eSDKQA')
+
+ sstate_dir = get_bb_var('SSTATE_DIR')
+
+ cls.image = 'core-image-minimal'
+ oeSDKExtSelfTest.generate_eSDK(cls.image)
+
+ # Install eSDK
+ cls.ext_sdk_path = oeSDKExtSelfTest.get_eSDK_toolchain(cls.image)
+ runCmd("%s -y -d \"%s\"" % (cls.ext_sdk_path, cls.tmpdir_eSDKQA))
+
+ cls.env_eSDK = oeSDKExtSelfTest.get_esdk_environment('', cls.tmpdir_eSDKQA)
+
+ # Configure eSDK to use sstate mirror from poky
+ sstate_config="""
+SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS"
+SSTATE_MIRRORS = "file://.* file://%s/PATH"
+ """ % 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)
+ super(oeSDKExtSelfTest, cls).tearDownClass()
+
+ @OETestID(1602)
+ def test_install_libraries_headers(self):
+ pn_sstate = 'bc'
+ bitbake(pn_sstate)
+ cmd = "devtool sdk-install %s " % pn_sstate
+ oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, cmd)
+
+ @OETestID(1603)
+ def test_image_generation_binary_feeds(self):
+ image = 'core-image-minimal'
+ cmd = "devtool build-image %s" % image
+ oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, cmd)
+
diff --git a/meta/lib/oeqa/selftest/cases/image_typedep.py b/meta/lib/oeqa/selftest/cases/image_typedep.py
new file mode 100644
index 0000000000..0614c765b4
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/image_typedep.py
@@ -0,0 +1,51 @@
+import os
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import bitbake
+
+class ImageTypeDepTests(OESelftestTestCase):
+
+ # Verify that when specifying a IMAGE_TYPEDEP_ of the form "foo.bar" that
+ # the conversion type bar gets added as a dep as well
+ def test_conversion_typedep_added(self):
+
+ self.write_recipeinc('emptytest', """
+# Try to empty out the default dependency list
+PACKAGE_INSTALL = ""
+DISTRO_EXTRA_RDEPENDS=""
+
+LICENSE = "MIT"
+IMAGE_FSTYPES = "testfstype"
+
+IMAGE_TYPES_MASKED += "testfstype"
+IMAGE_TYPEDEP_testfstype = "tar.bz2"
+
+inherit image
+
+""")
+ # First get the dependency that should exist for bz2, it will look
+ # like CONVERSION_DEPENDS_bz2="somedep"
+ result = bitbake('-e emptytest')
+
+ for line in result.output.split('\n'):
+ if line.startswith('CONVERSION_DEPENDS_bz2'):
+ dep = line.split('=')[1].strip('"')
+ break
+
+ # Now get the dependency task list and check for the expected task
+ # dependency
+ bitbake('-g emptytest')
+
+ taskdependsfile = os.path.join(self.builddir, 'task-depends.dot')
+ dep = dep + ".do_populate_sysroot"
+ depfound = False
+ expectedline = '"emptytest.do_rootfs" -> "{}"'.format(dep)
+
+ with open(taskdependsfile, "r") as f:
+ for line in f:
+ if line.strip() == expectedline:
+ depfound = True
+ break
+
+ if not depfound:
+ raise AssertionError("\"{}\" not found".format(expectedline))
diff --git a/meta/lib/oeqa/selftest/cases/imagefeatures.py b/meta/lib/oeqa/selftest/cases/imagefeatures.py
new file mode 100644
index 0000000000..45a06feaf3
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/imagefeatures.py
@@ -0,0 +1,125 @@
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.utils.sshcontrol import SSHControl
+import os
+
+class ImageFeatures(OESelftestTestCase):
+
+ test_user = 'tester'
+ root_user = 'root'
+
+ @OETestID(1107)
+ def test_non_root_user_can_connect_via_ssh_without_password(self):
+ """
+ Summary: Check if non root user can connect via ssh without password
+ Expected: 1. Connection to the image via ssh using root user without providing a password should be allowed.
+ 2. Connection to the image via ssh using tester user without providing a password should be allowed.
+ Product: oe-core
+ Author: Ionut Chisanovici <ionutx.chisanovici@intel.com>
+ AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ """
+
+ features = 'EXTRA_IMAGE_FEATURES = "ssh-server-openssh empty-root-password allow-empty-password"\n'
+ features += 'INHERIT += "extrausers"\n'
+ features += 'EXTRA_USERS_PARAMS = "useradd -p \'\' {}; usermod -s /bin/sh {};"'.format(self.test_user, self.test_user)
+ self.write_config(features)
+
+ # Build a core-image-minimal
+ bitbake('core-image-minimal')
+
+ with runqemu("core-image-minimal") as qemu:
+ # Attempt to ssh with each user into qemu with empty password
+ for user in [self.root_user, self.test_user]:
+ ssh = SSHControl(ip=qemu.ip, logfile=qemu.sshlog, user=user)
+ status, output = ssh.run("true")
+ self.assertEqual(status, 0, 'ssh to user %s failed with %s' % (user, output))
+
+ @OETestID(1115)
+ def test_all_users_can_connect_via_ssh_without_password(self):
+ """
+ Summary: Check if all users can connect via ssh without password
+ Expected: 1. Connection to the image via ssh using root user without providing a password should NOT be allowed.
+ 2. Connection to the image via ssh using tester user without providing a password should be allowed.
+ Product: oe-core
+ Author: Ionut Chisanovici <ionutx.chisanovici@intel.com>
+ AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ """
+
+ features = 'EXTRA_IMAGE_FEATURES = "ssh-server-openssh allow-empty-password"\n'
+ features += 'INHERIT += "extrausers"\n'
+ features += 'EXTRA_USERS_PARAMS = "useradd -p \'\' {}; usermod -s /bin/sh {};"'.format(self.test_user, self.test_user)
+ self.write_config(features)
+
+ # Build a core-image-minimal
+ bitbake('core-image-minimal')
+
+ with runqemu("core-image-minimal") as qemu:
+ # Attempt to ssh with each user into qemu with empty password
+ for user in [self.root_user, self.test_user]:
+ ssh = SSHControl(ip=qemu.ip, logfile=qemu.sshlog, user=user)
+ status, output = ssh.run("true")
+ if user == 'root':
+ self.assertNotEqual(status, 0, 'ssh to user root was allowed when it should not have been')
+ else:
+ self.assertEqual(status, 0, 'ssh to user tester failed with %s' % output)
+
+
+ @OETestID(1116)
+ def test_clutter_image_can_be_built(self):
+ """
+ Summary: Check if clutter image can be built
+ Expected: 1. core-image-clutter can be built
+ Product: oe-core
+ Author: Ionut Chisanovici <ionutx.chisanovici@intel.com>
+ AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ """
+
+ # Build a core-image-clutter
+ bitbake('core-image-clutter')
+
+ @OETestID(1117)
+ def test_wayland_support_in_image(self):
+ """
+ Summary: Check Wayland support in image
+ Expected: 1. Wayland image can be build
+ 2. Wayland feature can be installed
+ Product: oe-core
+ Author: Ionut Chisanovici <ionutx.chisanovici@intel.com>
+ AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ """
+
+ distro_features = get_bb_var('DISTRO_FEATURES')
+ if not ('opengl' in distro_features and 'wayland' in distro_features):
+ self.skipTest('neither opengl nor wayland present on DISTRO_FEATURES so core-image-weston cannot be built')
+
+ # Build a core-image-weston
+ bitbake('core-image-weston')
+
+ def test_bmap(self):
+ """
+ Summary: Check bmap support
+ Expected: 1. core-image-minimal can be build with bmap support
+ 2. core-image-minimal is sparse
+ Product: oe-core
+ Author: Ed Bartosh <ed.bartosh@linux.intel.com>
+ """
+
+ features = 'IMAGE_FSTYPES += " ext4 ext4.bmap"'
+ self.write_config(features)
+
+ image_name = 'core-image-minimal'
+ bitbake(image_name)
+
+ deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
+ link_name = get_bb_var('IMAGE_LINK_NAME', image_name)
+ image_path = os.path.join(deploy_dir_image, "%s.ext4" % link_name)
+ bmap_path = "%s.bmap" % image_path
+
+ # check if result image and bmap file are in deploy directory
+ self.assertTrue(os.path.exists(image_path))
+ self.assertTrue(os.path.exists(bmap_path))
+
+ # check if result image is sparse
+ image_stat = os.stat(image_path)
+ self.assertTrue(image_stat.st_size > image_stat.st_blocks * 512)
diff --git a/meta/lib/oeqa/selftest/cases/layerappend.py b/meta/lib/oeqa/selftest/cases/layerappend.py
new file mode 100644
index 0000000000..9562116309
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/layerappend.py
@@ -0,0 +1,95 @@
+import os
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+import oeqa.utils.ftools as ftools
+from oeqa.core.decorator.oeid import OETestID
+
+class LayerAppendTests(OESelftestTestCase):
+ layerconf = """
+# We have a conf and classes directory, append to BBPATH
+BBPATH .= ":${LAYERDIR}"
+
+# We have a recipes directory, add to BBFILES
+BBFILES += "${LAYERDIR}/recipes*/*.bb ${LAYERDIR}/recipes*/*.bbappend"
+
+BBFILE_COLLECTIONS += "meta-layerINT"
+BBFILE_PATTERN_meta-layerINT := "^${LAYERDIR}/"
+BBFILE_PRIORITY_meta-layerINT = "6"
+"""
+ recipe = """
+LICENSE="CLOSED"
+INHIBIT_DEFAULT_DEPS = "1"
+
+python do_build() {
+ bb.plain('Building ...')
+}
+addtask build
+"""
+ append = """
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+SRC_URI_append = " file://appendtest.txt"
+
+sysroot_stage_all_append() {
+ install -m 644 ${WORKDIR}/appendtest.txt ${SYSROOT_DESTDIR}/
+}
+
+"""
+
+ append2 = """
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+SRC_URI_append += "file://appendtest.txt"
+"""
+ layerappend = ''
+
+ def tearDownLocal(self):
+ if self.layerappend:
+ ftools.remove_from_file(self.builddir + "/conf/bblayers.conf", self.layerappend)
+ super(LayerAppendTests, self).tearDownLocal()
+
+ @OETestID(1196)
+ def test_layer_appends(self):
+ corebase = get_bb_var("COREBASE")
+
+ for l in ["0", "1", "2"]:
+ layer = os.path.join(corebase, "meta-layertest" + l)
+ self.assertFalse(os.path.exists(layer))
+ os.mkdir(layer)
+ os.mkdir(layer + "/conf")
+ with open(layer + "/conf/layer.conf", "w") as f:
+ f.write(self.layerconf.replace("INT", l))
+ os.mkdir(layer + "/recipes-test")
+ if l == "0":
+ with open(layer + "/recipes-test/layerappendtest.bb", "w") as f:
+ f.write(self.recipe)
+ elif l == "1":
+ with open(layer + "/recipes-test/layerappendtest.bbappend", "w") as f:
+ f.write(self.append)
+ os.mkdir(layer + "/recipes-test/layerappendtest")
+ with open(layer + "/recipes-test/layerappendtest/appendtest.txt", "w") as f:
+ f.write("Layer 1 test")
+ elif l == "2":
+ with open(layer + "/recipes-test/layerappendtest.bbappend", "w") as f:
+ f.write(self.append2)
+ os.mkdir(layer + "/recipes-test/layerappendtest")
+ with open(layer + "/recipes-test/layerappendtest/appendtest.txt", "w") as f:
+ f.write("Layer 2 test")
+ self.track_for_cleanup(layer)
+
+ self.layerappend = "BBLAYERS += \"{0}/meta-layertest0 {0}/meta-layertest1 {0}/meta-layertest2\"".format(corebase)
+ ftools.append_file(self.builddir + "/conf/bblayers.conf", self.layerappend)
+ stagingdir = get_bb_var("SYSROOT_DESTDIR", "layerappendtest")
+ bitbake("layerappendtest")
+ data = ftools.read_file(stagingdir + "/appendtest.txt")
+ self.assertEqual(data, "Layer 2 test")
+ os.remove(corebase + "/meta-layertest2/recipes-test/layerappendtest/appendtest.txt")
+ bitbake("layerappendtest")
+ data = ftools.read_file(stagingdir + "/appendtest.txt")
+ self.assertEqual(data, "Layer 1 test")
+ with open(corebase + "/meta-layertest2/recipes-test/layerappendtest/appendtest.txt", "w") as f:
+ f.write("Layer 2 test")
+ bitbake("layerappendtest")
+ data = ftools.read_file(stagingdir + "/appendtest.txt")
+ self.assertEqual(data, "Layer 2 test")
diff --git a/meta/lib/oeqa/selftest/cases/liboe.py b/meta/lib/oeqa/selftest/cases/liboe.py
new file mode 100644
index 0000000000..01b2cab7aa
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/liboe.py
@@ -0,0 +1,98 @@
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import get_bb_var, get_bb_vars, bitbake, runCmd
+import oe.path
+import os
+
+class LibOE(OESelftestTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super(LibOE, cls).setUpClass()
+ cls.tmp_dir = get_bb_var('TMPDIR')
+
+ def test_copy_tree_special(self):
+ """
+ Summary: oe.path.copytree() should copy files with special character
+ Expected: 'test file with sp£c!al @nd spaces' should exist in
+ copy destination
+ Product: OE-Core
+ Author: Joshua Lock <joshua.g.lock@intel.com>
+ """
+ testloc = oe.path.join(self.tmp_dir, 'liboetests')
+ src = oe.path.join(testloc, 'src')
+ dst = oe.path.join(testloc, 'dst')
+ bb.utils.mkdirhier(testloc)
+ bb.utils.mkdirhier(src)
+ testfilename = 'test file with sp£c!al @nd spaces'
+
+ # create the test file and copy it
+ open(oe.path.join(src, testfilename), 'w+b').close()
+ oe.path.copytree(src, dst)
+
+ # ensure path exists in dest
+ fileindst = os.path.isfile(oe.path.join(dst, testfilename))
+ self.assertTrue(fileindst, "File with spaces doesn't exist in dst")
+
+ oe.path.remove(testloc)
+
+ def test_copy_tree_xattr(self):
+ """
+ Summary: oe.path.copytree() should preserve xattr on copied files
+ Expected: testxattr file in destination should have user.oetest
+ extended attribute
+ Product: OE-Core
+ Author: Joshua Lock <joshua.g.lock@intel.com>
+ """
+ testloc = oe.path.join(self.tmp_dir, 'liboetests')
+ src = oe.path.join(testloc, 'src')
+ dst = oe.path.join(testloc, 'dst')
+ bb.utils.mkdirhier(testloc)
+ bb.utils.mkdirhier(src)
+ testfilename = 'testxattr'
+
+ # ensure we have setfattr available
+ bitbake("attr-native")
+
+ bb_vars = get_bb_vars(['SYSROOT_DESTDIR', 'bindir'], 'attr-native')
+ destdir = bb_vars['SYSROOT_DESTDIR']
+ bindir = bb_vars['bindir']
+ bindir = destdir + bindir
+
+ # create a file with xattr and copy it
+ open(oe.path.join(src, testfilename), 'w+b').close()
+ runCmd('%s/setfattr -n user.oetest -v "testing liboe" %s' % (bindir, oe.path.join(src, testfilename)))
+ oe.path.copytree(src, dst)
+
+ # ensure file in dest has user.oetest xattr
+ result = runCmd('%s/getfattr -n user.oetest %s' % (bindir, oe.path.join(dst, testfilename)))
+ self.assertIn('user.oetest="testing liboe"', result.output, 'Extended attribute not sert in dst')
+
+ oe.path.remove(testloc)
+
+ def test_copy_hardlink_tree_count(self):
+ """
+ Summary: oe.path.copyhardlinktree() shouldn't miss out files
+ Expected: src and dst should have the same number of files
+ Product: OE-Core
+ Author: Joshua Lock <joshua.g.lock@intel.com>
+ """
+ testloc = oe.path.join(self.tmp_dir, 'liboetests')
+ src = oe.path.join(testloc, 'src')
+ dst = oe.path.join(testloc, 'dst')
+ bb.utils.mkdirhier(testloc)
+ bb.utils.mkdirhier(src)
+ testfiles = ['foo', 'bar', '.baz', 'quux']
+
+ def touchfile(tf):
+ open(oe.path.join(src, tf), 'w+b').close()
+
+ for f in testfiles:
+ touchfile(f)
+
+ oe.path.copyhardlinktree(src, dst)
+
+ dstcnt = len(os.listdir(dst))
+ srccnt = len(os.listdir(src))
+ self.assertEquals(dstcnt, len(testfiles), "Number of files in dst (%s) differs from number of files in src(%s)." % (dstcnt, srccnt))
+
+ oe.path.remove(testloc)
diff --git a/meta/lib/oeqa/selftest/cases/lic_checksum.py b/meta/lib/oeqa/selftest/cases/lic_checksum.py
new file mode 100644
index 0000000000..37407157c1
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/lic_checksum.py
@@ -0,0 +1,35 @@
+import os
+import tempfile
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import bitbake
+from oeqa.utils import CommandError
+from oeqa.core.decorator.oeid import OETestID
+
+class LicenseTests(OESelftestTestCase):
+
+ # Verify that changing a license file that has an absolute path causes
+ # the license qa to fail due to a mismatched md5sum.
+ @OETestID(1197)
+ def test_nonmatching_checksum(self):
+ bitbake_cmd = '-c populate_lic emptytest'
+ error_msg = 'emptytest: The new md5 checksum is 8d777f385d3dfec8815d20f7496026dc'
+
+ lic_file, lic_path = tempfile.mkstemp()
+ os.close(lic_file)
+ self.track_for_cleanup(lic_path)
+
+ self.write_recipeinc('emptytest', """
+INHIBIT_DEFAULT_DEPS = "1"
+LIC_FILES_CHKSUM = "file://%s;md5=d41d8cd98f00b204e9800998ecf8427e"
+SRC_URI = "file://%s;md5=d41d8cd98f00b204e9800998ecf8427e"
+""" % (lic_path, lic_path))
+ result = bitbake(bitbake_cmd)
+
+ with open(lic_path, "w") as f:
+ f.write("data")
+
+ self.write_config("INHERIT_remove = \"report-error\"")
+ result = bitbake(bitbake_cmd, ignore_status=True)
+ if error_msg not in result.output:
+ raise AssertionError(result.output)
diff --git a/meta/lib/oeqa/selftest/cases/manifest.py b/meta/lib/oeqa/selftest/cases/manifest.py
new file mode 100644
index 0000000000..146071934d
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/manifest.py
@@ -0,0 +1,166 @@
+import os
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import get_bb_var, get_bb_vars, bitbake
+from oeqa.core.decorator.oeid import OETestID
+
+class ManifestEntry:
+ '''A manifest item of a collection able to list missing packages'''
+ def __init__(self, entry):
+ self.file = entry
+ self.missing = []
+
+class VerifyManifest(OESelftestTestCase):
+ '''Tests for the manifest files and contents of an image'''
+
+ @classmethod
+ def check_manifest_entries(self, manifest, path):
+ manifest_errors = []
+ try:
+ with open(manifest, "r") as mfile:
+ for line in mfile:
+ manifest_entry = os.path.join(path, line.split()[0])
+ self.logger.debug("{}: looking for {}"\
+ .format(self.classname, manifest_entry))
+ if not os.path.isfile(manifest_entry):
+ manifest_errors.append(manifest_entry)
+ self.logger.debug("{}: {} not found"\
+ .format(self.classname, manifest_entry))
+ except OSError as e:
+ self.logger.debug("{}: checking of {} failed"\
+ .format(self.classname, manifest))
+ raise e
+
+ return manifest_errors
+
+ #this will possibly move from here
+ @classmethod
+ def get_dir_from_bb_var(self, bb_var, target = None):
+ target == self.buildtarget if target == None else target
+ directory = get_bb_var(bb_var, target);
+ if not directory or not os.path.isdir(directory):
+ self.logger.debug("{}: {} points to {} when target = {}"\
+ .format(self.classname, bb_var, directory, target))
+ raise OSError
+ return directory
+
+ @classmethod
+ def setUpClass(self):
+
+ super(VerifyManifest, self).setUpClass()
+ self.buildtarget = 'core-image-minimal'
+ self.classname = 'VerifyManifest'
+
+ self.logger.info("{}: doing bitbake {} as a prerequisite of the test"\
+ .format(self.classname, self.buildtarget))
+ if bitbake(self.buildtarget).status:
+ self.logger.debug("{} Failed to setup {}"\
+ .format(self.classname, self.buildtarget))
+ self.skipTest("{}: Cannot setup testing scenario"\
+ .format(self.classname))
+
+ @OETestID(1380)
+ def test_SDK_manifest_entries(self):
+ '''Verifying the SDK manifest entries exist, this may take a build'''
+
+ # the setup should bitbake core-image-minimal and here it is required
+ # to do an additional setup for the sdk
+ sdktask = '-c populate_sdk'
+ bbargs = sdktask + ' ' + self.buildtarget
+ self.logger.debug("{}: doing bitbake {} as a prerequisite of the test"\
+ .format(self.classname, bbargs))
+ if bitbake(bbargs).status:
+ self.logger.debug("{} Failed to bitbake {}"\
+ .format(self.classname, bbargs))
+ self.skipTest("{}: Cannot setup testing scenario"\
+ .format(self.classname))
+
+
+ pkgdata_dir = reverse_dir = {}
+ mfilename = mpath = m_entry = {}
+ # get manifest location based on target to query about
+ d_target= dict(target = self.buildtarget,
+ host = 'nativesdk-packagegroup-sdk-host')
+ try:
+ mdir = self.get_dir_from_bb_var('SDK_DEPLOY', self.buildtarget)
+ for k in d_target.keys():
+ bb_vars = get_bb_vars(['SDK_NAME', 'SDK_VERSION'], self.buildtarget)
+ mfilename[k] = "{}-toolchain-{}.{}.manifest".format(
+ bb_vars['SDK_NAME'],
+ bb_vars['SDK_VERSION'],
+ k)
+ mpath[k] = os.path.join(mdir, mfilename[k])
+ if not os.path.isfile(mpath[k]):
+ self.logger.debug("{}: {} does not exist".format(
+ self.classname, mpath[k]))
+ raise IOError
+ m_entry[k] = ManifestEntry(mpath[k])
+
+ pkgdata_dir[k] = self.get_dir_from_bb_var('PKGDATA_DIR',
+ d_target[k])
+ reverse_dir[k] = os.path.join(pkgdata_dir[k],
+ 'runtime-reverse')
+ if not os.path.exists(reverse_dir[k]):
+ self.logger.debug("{}: {} does not exist".format(
+ self.classname, reverse_dir[k]))
+ raise IOError
+ except OSError:
+ raise self.skipTest("{}: Error in obtaining manifest dirs"\
+ .format(self.classname))
+ except IOError:
+ msg = "{}: Error cannot find manifests in the specified dir:\n{}"\
+ .format(self.classname, mdir)
+ self.fail(msg)
+
+ for k in d_target.keys():
+ self.logger.debug("{}: Check manifest {}".format(
+ self.classname, m_entry[k].file))
+
+ m_entry[k].missing = self.check_manifest_entries(\
+ m_entry[k].file,reverse_dir[k])
+ if m_entry[k].missing:
+ msg = '{}: {} Error has the following missing entries'\
+ .format(self.classname, m_entry[k].file)
+ logmsg = msg+':\n'+'\n'.join(m_entry[k].missing)
+ self.logger.debug(logmsg)
+ self.logger.info(msg)
+ self.fail(logmsg)
+
+ @OETestID(1381)
+ def test_image_manifest_entries(self):
+ '''Verifying the image manifest entries exist'''
+
+ # get manifest location based on target to query about
+ try:
+ mdir = self.get_dir_from_bb_var('DEPLOY_DIR_IMAGE',
+ self.buildtarget)
+ mfilename = get_bb_var("IMAGE_LINK_NAME", self.buildtarget)\
+ + ".manifest"
+ mpath = os.path.join(mdir, mfilename)
+ if not os.path.isfile(mpath): raise IOError
+ m_entry = ManifestEntry(mpath)
+
+ pkgdata_dir = {}
+ pkgdata_dir = self.get_dir_from_bb_var('PKGDATA_DIR',
+ self.buildtarget)
+ revdir = os.path.join(pkgdata_dir, 'runtime-reverse')
+ if not os.path.exists(revdir): raise IOError
+ except OSError:
+ raise self.skipTest("{}: Error in obtaining manifest dirs"\
+ .format(self.classname))
+ except IOError:
+ msg = "{}: Error cannot find manifests in dir:\n{}"\
+ .format(self.classname, mdir)
+ self.fail(msg)
+
+ self.logger.debug("{}: Check manifest {}"\
+ .format(self.classname, m_entry.file))
+ m_entry.missing = self.check_manifest_entries(\
+ m_entry.file, revdir)
+ if m_entry.missing:
+ msg = '{}: {} Error has the following missing entries'\
+ .format(self.classname, m_entry.file)
+ logmsg = msg+':\n'+'\n'.join(m_entry.missing)
+ self.logger.debug(logmsg)
+ self.logger.info(msg)
+ self.fail(logmsg)
diff --git a/meta/lib/oeqa/selftest/cases/oelib/__init__.py b/meta/lib/oeqa/selftest/cases/oelib/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/oelib/__init__.py
diff --git a/meta/lib/oeqa/selftest/cases/oelib/buildhistory.py b/meta/lib/oeqa/selftest/cases/oelib/buildhistory.py
new file mode 100644
index 0000000000..4e877517c1
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/oelib/buildhistory.py
@@ -0,0 +1,88 @@
+import os
+from oeqa.selftest.case import OESelftestTestCase
+import tempfile
+from git import Repo
+from oeqa.utils.commands import get_bb_var
+from oe.buildhistory_analysis import blob_to_dict, compare_dict_blobs
+
+class TestBlobParsing(OESelftestTestCase):
+
+ def setUp(self):
+ import time
+ self.repo_path = tempfile.mkdtemp(prefix='selftest-buildhistory',
+ dir=get_bb_var('TOPDIR'))
+
+ self.repo = Repo.init(self.repo_path)
+ self.test_file = "test"
+ self.var_map = {}
+
+ def tearDown(self):
+ import shutil
+ shutil.rmtree(self.repo_path)
+
+ def commit_vars(self, to_add={}, to_remove = [], msg="A commit message"):
+ if len(to_add) == 0 and len(to_remove) == 0:
+ return
+
+ for k in to_remove:
+ self.var_map.pop(x,None)
+ for k in to_add:
+ self.var_map[k] = to_add[k]
+
+ with open(os.path.join(self.repo_path, self.test_file), 'w') as repo_file:
+ for k in self.var_map:
+ repo_file.write("%s = %s\n" % (k, self.var_map[k]))
+
+ self.repo.git.add("--all")
+ self.repo.git.commit(message=msg)
+
+ def test_blob_to_dict(self):
+ """
+ Test convertion of git blobs to dictionary
+ """
+ valuesmap = { "foo" : "1", "bar" : "2" }
+ self.commit_vars(to_add = valuesmap)
+
+ blob = self.repo.head.commit.tree.blobs[0]
+ self.assertEqual(valuesmap, blob_to_dict(blob),
+ "commit was not translated correctly to dictionary")
+
+ def test_compare_dict_blobs(self):
+ """
+ Test comparisson of dictionaries extracted from git blobs
+ """
+ changesmap = { "foo-2" : ("2", "8"), "bar" : ("","4"), "bar-2" : ("","5")}
+
+ self.commit_vars(to_add = { "foo" : "1", "foo-2" : "2", "foo-3" : "3" })
+ blob1 = self.repo.heads.master.commit.tree.blobs[0]
+
+ self.commit_vars(to_add = { "foo-2" : "8", "bar" : "4", "bar-2" : "5" })
+ blob2 = self.repo.heads.master.commit.tree.blobs[0]
+
+ change_records = compare_dict_blobs(os.path.join(self.repo_path, self.test_file),
+ blob1, blob2, False, False)
+
+ var_changes = { x.fieldname : (x.oldvalue, x.newvalue) for x in change_records}
+ self.assertEqual(changesmap, var_changes, "Changes not reported correctly")
+
+ def test_compare_dict_blobs_default(self):
+ """
+ Test default values for comparisson of git blob dictionaries
+ """
+ defaultmap = { x : ("default", "1") for x in ["PKG", "PKGE", "PKGV", "PKGR"]}
+
+ self.commit_vars(to_add = { "foo" : "1" })
+ blob1 = self.repo.heads.master.commit.tree.blobs[0]
+
+ self.commit_vars(to_add = { "PKG" : "1", "PKGE" : "1", "PKGV" : "1", "PKGR" : "1" })
+ blob2 = self.repo.heads.master.commit.tree.blobs[0]
+
+ change_records = compare_dict_blobs(os.path.join(self.repo_path, self.test_file),
+ blob1, blob2, False, False)
+
+ var_changes = {}
+ for x in change_records:
+ oldvalue = "default" if ("default" in x.oldvalue) else x.oldvalue
+ var_changes[x.fieldname] = (oldvalue, x.newvalue)
+
+ self.assertEqual(defaultmap, var_changes, "Defaults not set properly")
diff --git a/meta/lib/oeqa/selftest/cases/oelib/elf.py b/meta/lib/oeqa/selftest/cases/oelib/elf.py
new file mode 100644
index 0000000000..0451ebaffb
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/oelib/elf.py
@@ -0,0 +1,21 @@
+from oeqa.selftest.case import OESelftestTestCase
+import oe.qa
+
+class TestElf(OESelftestTestCase):
+ def test_machine_name(self):
+ """
+ Test elf_machine_to_string()
+ """
+ self.assertEqual(oe.qa.elf_machine_to_string(0x02), "SPARC")
+ self.assertEqual(oe.qa.elf_machine_to_string(0x03), "x86")
+ self.assertEqual(oe.qa.elf_machine_to_string(0x08), "MIPS")
+ self.assertEqual(oe.qa.elf_machine_to_string(0x14), "PowerPC")
+ self.assertEqual(oe.qa.elf_machine_to_string(0x28), "ARM")
+ self.assertEqual(oe.qa.elf_machine_to_string(0x2A), "SuperH")
+ self.assertEqual(oe.qa.elf_machine_to_string(0x32), "IA-64")
+ self.assertEqual(oe.qa.elf_machine_to_string(0x3E), "x86-64")
+ self.assertEqual(oe.qa.elf_machine_to_string(0xB7), "AArch64")
+
+ self.assertEqual(oe.qa.elf_machine_to_string(0x00), "Unknown (0)")
+ self.assertEqual(oe.qa.elf_machine_to_string(0xDEADBEEF), "Unknown (3735928559)")
+ self.assertEqual(oe.qa.elf_machine_to_string("foobar"), "Unknown ('foobar')")
diff --git a/meta/lib/oeqa/selftest/cases/oelib/license.py b/meta/lib/oeqa/selftest/cases/oelib/license.py
new file mode 100644
index 0000000000..a6d9c9ac7a
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/oelib/license.py
@@ -0,0 +1,68 @@
+from oeqa.selftest.case import OESelftestTestCase
+import oe.license
+
+class SeenVisitor(oe.license.LicenseVisitor):
+ def __init__(self):
+ self.seen = []
+ oe.license.LicenseVisitor.__init__(self)
+
+ def visit_Str(self, node):
+ self.seen.append(node.s)
+
+class TestSingleLicense(OESelftestTestCase):
+ licenses = [
+ "GPLv2",
+ "LGPL-2.0",
+ "Artistic",
+ "MIT",
+ "GPLv3+",
+ "FOO_BAR",
+ ]
+ invalid_licenses = ["GPL/BSD"]
+
+ @staticmethod
+ def parse(licensestr):
+ visitor = SeenVisitor()
+ visitor.visit_string(licensestr)
+ return visitor.seen
+
+ def test_single_licenses(self):
+ for license in self.licenses:
+ licenses = self.parse(license)
+ self.assertListEqual(licenses, [license])
+
+ def test_invalid_licenses(self):
+ for license in self.invalid_licenses:
+ with self.assertRaises(oe.license.InvalidLicense) as cm:
+ self.parse(license)
+ self.assertEqual(cm.exception.license, license)
+
+class TestSimpleCombinations(OESelftestTestCase):
+ tests = {
+ "FOO&BAR": ["FOO", "BAR"],
+ "BAZ & MOO": ["BAZ", "MOO"],
+ "ALPHA|BETA": ["ALPHA"],
+ "BAZ&MOO|FOO": ["FOO"],
+ "FOO&BAR|BAZ": ["FOO", "BAR"],
+ }
+ preferred = ["ALPHA", "FOO", "BAR"]
+
+ def test_tests(self):
+ def choose(a, b):
+ if all(lic in self.preferred for lic in b):
+ return b
+ else:
+ return a
+
+ for license, expected in self.tests.items():
+ licenses = oe.license.flattened_licenses(license, choose)
+ self.assertListEqual(licenses, expected)
+
+class TestComplexCombinations(TestSimpleCombinations):
+ tests = {
+ "FOO & (BAR | BAZ)&MOO": ["FOO", "BAR", "MOO"],
+ "(ALPHA|(BETA&THETA)|OMEGA)&DELTA": ["OMEGA", "DELTA"],
+ "((ALPHA|BETA)&FOO)|BAZ": ["BETA", "FOO"],
+ "(GPL-2.0|Proprietary)&BSD-4-clause&MIT": ["GPL-2.0", "BSD-4-clause", "MIT"],
+ }
+ preferred = ["BAR", "OMEGA", "BETA", "GPL-2.0"]
diff --git a/meta/lib/oeqa/selftest/cases/oelib/path.py b/meta/lib/oeqa/selftest/cases/oelib/path.py
new file mode 100644
index 0000000000..2ae5eaf89e
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/oelib/path.py
@@ -0,0 +1,89 @@
+from oeqa.selftest.case import OESelftestTestCase
+import oe, oe.path
+import tempfile
+import os
+import errno
+import shutil
+
+class TestRealPath(OESelftestTestCase):
+ DIRS = [ "a", "b", "etc", "sbin", "usr", "usr/bin", "usr/binX", "usr/sbin", "usr/include", "usr/include/gdbm" ]
+ FILES = [ "etc/passwd", "b/file" ]
+ LINKS = [
+ ( "bin", "/usr/bin", "/usr/bin" ),
+ ( "binX", "usr/binX", "/usr/binX" ),
+ ( "c", "broken", "/broken" ),
+ ( "etc/passwd-1", "passwd", "/etc/passwd" ),
+ ( "etc/passwd-2", "passwd-1", "/etc/passwd" ),
+ ( "etc/passwd-3", "/etc/passwd-1", "/etc/passwd" ),
+ ( "etc/shadow-1", "/etc/shadow", "/etc/shadow" ),
+ ( "etc/shadow-2", "/etc/shadow-1", "/etc/shadow" ),
+ ( "prog-A", "bin/prog-A", "/usr/bin/prog-A" ),
+ ( "prog-B", "/bin/prog-B", "/usr/bin/prog-B" ),
+ ( "usr/bin/prog-C", "../../sbin/prog-C", "/sbin/prog-C" ),
+ ( "usr/bin/prog-D", "/sbin/prog-D", "/sbin/prog-D" ),
+ ( "usr/binX/prog-E", "../sbin/prog-E", None ),
+ ( "usr/bin/prog-F", "../../../sbin/prog-F", "/sbin/prog-F" ),
+ ( "loop", "a/loop", None ),
+ ( "a/loop", "../loop", None ),
+ ( "b/test", "file/foo", "/b/file/foo" ),
+ ]
+
+ LINKS_PHYS = [
+ ( "./", "/", "" ),
+ ( "binX/prog-E", "/usr/sbin/prog-E", "/sbin/prog-E" ),
+ ]
+
+ EXCEPTIONS = [
+ ( "loop", errno.ELOOP ),
+ ( "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")
+
+ os.mkdir(os.path.join(self.tmpdir, "_real"))
+ os.symlink("_real", self.root)
+
+ for d in self.DIRS:
+ os.mkdir(os.path.join(self.root, d))
+ for f in self.FILES:
+ open(os.path.join(self.root, f), "w")
+ for l in self.LINKS:
+ os.symlink(l[1], os.path.join(self.root, l[0]))
+
+ 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)
+
+ def test_norm(self):
+ for l in self.LINKS:
+ if l[2] == None:
+ continue
+
+ target_p = self.__realpath(l[0], True)
+ target_l = self.__realpath(l[0], False)
+
+ if l[2] != False:
+ self.assertEqual(target_p, target_l)
+ self.assertEqual(l[2], target_p[len(self.root):])
+
+ def test_phys(self):
+ for l in self.LINKS_PHYS:
+ target_p = self.__realpath(l[0], True)
+ target_l = self.__realpath(l[0], False)
+
+ self.assertEqual(l[1], target_p[len(self.root):])
+ self.assertEqual(l[2], target_l[len(self.root):])
+
+ def test_loop(self):
+ for e in self.EXCEPTIONS:
+ self.assertRaisesRegex(OSError, r'\[Errno %u\]' % e[1],
+ self.__realpath, e[0], False, False)
diff --git a/meta/lib/oeqa/selftest/cases/oelib/types.py b/meta/lib/oeqa/selftest/cases/oelib/types.py
new file mode 100644
index 0000000000..99c84044be
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/oelib/types.py
@@ -0,0 +1,50 @@
+from oeqa.selftest.case import OESelftestTestCase
+from oe.maketype import create
+
+class TestBooleanType(OESelftestTestCase):
+ def test_invalid(self):
+ self.assertRaises(ValueError, create, '', 'boolean')
+ self.assertRaises(ValueError, create, 'foo', 'boolean')
+ self.assertRaises(TypeError, create, object(), 'boolean')
+
+ def test_true(self):
+ self.assertTrue(create('y', 'boolean'))
+ self.assertTrue(create('yes', 'boolean'))
+ self.assertTrue(create('1', 'boolean'))
+ self.assertTrue(create('t', 'boolean'))
+ self.assertTrue(create('true', 'boolean'))
+ self.assertTrue(create('TRUE', 'boolean'))
+ self.assertTrue(create('truE', 'boolean'))
+
+ def test_false(self):
+ self.assertFalse(create('n', 'boolean'))
+ self.assertFalse(create('no', 'boolean'))
+ self.assertFalse(create('0', 'boolean'))
+ self.assertFalse(create('f', 'boolean'))
+ self.assertFalse(create('false', 'boolean'))
+ self.assertFalse(create('FALSE', 'boolean'))
+ self.assertFalse(create('faLse', 'boolean'))
+
+ def test_bool_equality(self):
+ self.assertEqual(create('n', 'boolean'), False)
+ self.assertNotEqual(create('n', 'boolean'), True)
+ self.assertEqual(create('y', 'boolean'), True)
+ self.assertNotEqual(create('y', 'boolean'), False)
+
+class TestList(OESelftestTestCase):
+ def assertListEqual(self, value, valid, sep=None):
+ obj = create(value, 'list', separator=sep)
+ self.assertEqual(obj, valid)
+ if sep is not None:
+ self.assertEqual(obj.separator, sep)
+ self.assertEqual(str(obj), obj.separator.join(obj))
+
+ def test_list_nosep(self):
+ testlist = ['alpha', 'beta', 'theta']
+ self.assertListEqual('alpha beta theta', testlist)
+ self.assertListEqual('alpha beta\ttheta', testlist)
+ self.assertListEqual('alpha', ['alpha'])
+
+ def test_list_usersep(self):
+ self.assertListEqual('foo:bar', ['foo', 'bar'], ':')
+ self.assertListEqual('foo:bar:baz', ['foo', 'bar', 'baz'], ':')
diff --git a/meta/lib/oeqa/selftest/cases/oelib/utils.py b/meta/lib/oeqa/selftest/cases/oelib/utils.py
new file mode 100644
index 0000000000..5bc5fffae7
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/oelib/utils.py
@@ -0,0 +1,51 @@
+from oeqa.selftest.case import OESelftestTestCase
+from oe.utils import packages_filter_out_system, trim_version
+
+class TestPackagesFilterOutSystem(OESelftestTestCase):
+ def test_filter(self):
+ """
+ Test that oe.utils.packages_filter_out_system works.
+ """
+ try:
+ import bb
+ except ImportError:
+ self.skipTest("Cannot import bb")
+
+ d = bb.data_smart.DataSmart()
+ d.setVar("PN", "foo")
+
+ d.setVar("PACKAGES", "foo foo-doc foo-dev")
+ pkgs = packages_filter_out_system(d)
+ self.assertEqual(pkgs, [])
+
+ d.setVar("PACKAGES", "foo foo-doc foo-data foo-dev")
+ pkgs = packages_filter_out_system(d)
+ self.assertEqual(pkgs, ["foo-data"])
+
+ d.setVar("PACKAGES", "foo foo-locale-en-gb")
+ pkgs = packages_filter_out_system(d)
+ self.assertEqual(pkgs, [])
+
+ d.setVar("PACKAGES", "foo foo-data foo-locale-en-gb")
+ pkgs = packages_filter_out_system(d)
+ self.assertEqual(pkgs, ["foo-data"])
+
+
+class TestTrimVersion(OESelftestTestCase):
+ def test_version_exception(self):
+ with self.assertRaises(TypeError):
+ trim_version(None, 2)
+ with self.assertRaises(TypeError):
+ trim_version((1, 2, 3), 2)
+
+ def test_num_exception(self):
+ with self.assertRaises(ValueError):
+ trim_version("1.2.3", 0)
+ with self.assertRaises(ValueError):
+ trim_version("1.2.3", -1)
+
+ def test_valid(self):
+ self.assertEqual(trim_version("1.2.3", 1), "1")
+ self.assertEqual(trim_version("1.2.3", 2), "1.2")
+ self.assertEqual(trim_version("1.2.3", 3), "1.2.3")
+ self.assertEqual(trim_version("1.2.3", 4), "1.2.3")
diff --git a/meta/lib/oeqa/selftest/cases/oescripts.py b/meta/lib/oeqa/selftest/cases/oescripts.py
new file mode 100644
index 0000000000..1ee753763e
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/oescripts.py
@@ -0,0 +1,15 @@
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.selftest.cases.buildhistory import BuildhistoryBase
+from oeqa.utils.commands import Command, runCmd, bitbake, get_bb_var, get_test_layer
+from oeqa.core.decorator.oeid import OETestID
+
+class BuildhistoryDiffTests(BuildhistoryBase):
+
+ @OETestID(295)
+ def test_buildhistory_diff(self):
+ target = 'xcursor-transparent-theme'
+ self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
+ self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True)
+ result = runCmd("buildhistory-diff -p %s" % get_bb_var('BUILDHISTORY_DIR'))
+ expected_output = 'PR changed from "r1" to "r0"'
+ self.assertTrue(expected_output in result.output, msg="Did not find expected output: %s" % result.output)
diff --git a/meta/lib/oeqa/selftest/cases/package.py b/meta/lib/oeqa/selftest/cases/package.py
new file mode 100644
index 0000000000..6a8bc9283f
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/package.py
@@ -0,0 +1,80 @@
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import bitbake, get_bb_vars
+import subprocess, os
+import oe.path
+
+class VersionOrdering(OESelftestTestCase):
+ # version1, version2, sort order
+ tests = (
+ ("1.0", "1.0", 0),
+ ("1.0", "2.0", -1),
+ ("2.0", "1.0", 1),
+ ("2.0-rc", "2.0", 1),
+ ("2.0~rc", "2.0", -1),
+ ("1.2rc2", "1.2.0", -1)
+ )
+
+ @classmethod
+ def setUpClass(cls):
+ # Build the tools we need and populate a sysroot
+ bitbake("dpkg-native opkg-native rpm-native python3-native")
+ bitbake("build-sysroots -c build_native_sysroot")
+
+ # Get the paths so we can point into the sysroot correctly
+ vars = get_bb_vars(["STAGING_DIR", "BUILD_ARCH", "bindir_native", "libdir_native"])
+ cls.staging = oe.path.join(vars["STAGING_DIR"], vars["BUILD_ARCH"])
+ cls.bindir = oe.path.join(cls.staging, vars["bindir_native"])
+ cls.libdir = oe.path.join(cls.staging, vars["libdir_native"])
+
+ def setUp(self):
+ # Just for convenience
+ self.staging = type(self).staging
+ self.bindir = type(self).bindir
+ self.libdir = type(self).libdir
+
+ def test_dpkg(self):
+ for ver1, ver2, sort in self.tests:
+ op = { -1: "<<", 0: "=", 1: ">>" }[sort]
+ status = subprocess.call((oe.path.join(self.bindir, "dpkg"), "--compare-versions", ver1, op, ver2))
+ self.assertEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
+
+ # Now do it again but with incorrect operations
+ op = { -1: ">>", 0: ">>", 1: "<<" }[sort]
+ status = subprocess.call((oe.path.join(self.bindir, "dpkg"), "--compare-versions", ver1, op, ver2))
+ self.assertNotEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
+
+ # Now do it again but with incorrect operations
+ op = { -1: "=", 0: "<<", 1: "=" }[sort]
+ status = subprocess.call((oe.path.join(self.bindir, "dpkg"), "--compare-versions", ver1, op, ver2))
+ self.assertNotEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
+
+ def test_opkg(self):
+ for ver1, ver2, sort in self.tests:
+ op = { -1: "<<", 0: "=", 1: ">>" }[sort]
+ status = subprocess.call((oe.path.join(self.bindir, "opkg"), "compare-versions", ver1, op, ver2))
+ self.assertEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
+
+ # Now do it again but with incorrect operations
+ op = { -1: ">>", 0: ">>", 1: "<<" }[sort]
+ status = subprocess.call((oe.path.join(self.bindir, "opkg"), "compare-versions", ver1, op, ver2))
+ self.assertNotEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
+
+ # Now do it again but with incorrect operations
+ op = { -1: "=", 0: "<<", 1: "=" }[sort]
+ status = subprocess.call((oe.path.join(self.bindir, "opkg"), "compare-versions", ver1, op, ver2))
+ self.assertNotEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
+
+ def test_rpm(self):
+ # Need to tell the Python bindings where to find its configuration
+ env = os.environ.copy()
+ env["RPM_CONFIGDIR"] = oe.path.join(self.libdir, "rpm")
+
+ for ver1, ver2, sort in self.tests:
+ # The only way to test rpm is via the Python module, so we need to
+ # execute python3-native. labelCompare returns -1/0/1 (like strcmp)
+ # so add 100 and use that as the exit code.
+ command = (oe.path.join(self.bindir, "python3-native", "python3"), "-c",
+ "import sys, rpm; v1=(None, \"%s\", None); v2=(None, \"%s\", None); sys.exit(rpm.labelCompare(v1, v2) + 100)" % (ver1, ver2))
+ status = subprocess.call(command, env=env)
+ self.assertIn(status, (99, 100, 101))
+ self.assertEqual(status - 100, sort, "%s %s (%d) failed" % (ver1, ver2, sort))
diff --git a/meta/lib/oeqa/selftest/cases/pkgdata.py b/meta/lib/oeqa/selftest/cases/pkgdata.py
new file mode 100644
index 0000000000..0b4caf1b2c
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/pkgdata.py
@@ -0,0 +1,224 @@
+import os
+import tempfile
+import fnmatch
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
+from oeqa.core.decorator.oeid import OETestID
+
+class OePkgdataUtilTests(OESelftestTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super(OePkgdataUtilTests, cls).setUpClass()
+ # Ensure we have the right data in pkgdata
+ cls.logger.info('Running bitbake to generate pkgdata')
+ bitbake('busybox zlib m4')
+
+ @OETestID(1203)
+ def test_lookup_pkg(self):
+ # Forward tests
+ result = runCmd('oe-pkgdata-util lookup-pkg "zlib busybox"')
+ self.assertEqual(result.output, 'libz1\nbusybox')
+ result = runCmd('oe-pkgdata-util lookup-pkg zlib-dev')
+ self.assertEqual(result.output, 'libz-dev')
+ result = runCmd('oe-pkgdata-util lookup-pkg nonexistentpkg', ignore_status=True)
+ self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
+ self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
+ # Reverse tests
+ result = runCmd('oe-pkgdata-util lookup-pkg -r "libz1 busybox"')
+ self.assertEqual(result.output, 'zlib\nbusybox')
+ result = runCmd('oe-pkgdata-util lookup-pkg -r libz-dev')
+ self.assertEqual(result.output, 'zlib-dev')
+ result = runCmd('oe-pkgdata-util lookup-pkg -r nonexistentpkg', ignore_status=True)
+ self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
+ self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
+
+ @OETestID(1205)
+ def test_read_value(self):
+ result = runCmd('oe-pkgdata-util read-value PN libz1')
+ self.assertEqual(result.output, 'zlib')
+ result = runCmd('oe-pkgdata-util read-value PKG libz1')
+ self.assertEqual(result.output, 'libz1')
+ result = runCmd('oe-pkgdata-util read-value PKGSIZE m4')
+ pkgsize = int(result.output.strip())
+ self.assertGreater(pkgsize, 1, "Size should be greater than 1. %s" % result.output)
+
+ @OETestID(1198)
+ def test_find_path(self):
+ result = runCmd('oe-pkgdata-util find-path /lib/libz.so.1')
+ self.assertEqual(result.output, 'zlib: /lib/libz.so.1')
+ result = runCmd('oe-pkgdata-util find-path /usr/bin/m4')
+ self.assertEqual(result.output, 'm4: /usr/bin/m4')
+ result = runCmd('oe-pkgdata-util find-path /not/exist', ignore_status=True)
+ self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
+ self.assertEqual(result.output, 'ERROR: Unable to find any package producing path /not/exist')
+
+ @OETestID(1204)
+ def test_lookup_recipe(self):
+ result = runCmd('oe-pkgdata-util lookup-recipe "libz-staticdev busybox"')
+ self.assertEqual(result.output, 'zlib\nbusybox')
+ result = runCmd('oe-pkgdata-util lookup-recipe libz-dbg')
+ self.assertEqual(result.output, 'zlib')
+ result = runCmd('oe-pkgdata-util lookup-recipe nonexistentpkg', ignore_status=True)
+ self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
+ self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
+
+ @OETestID(1202)
+ def test_list_pkgs(self):
+ # No arguments
+ result = runCmd('oe-pkgdata-util list-pkgs')
+ pkglist = result.output.split()
+ self.assertIn('zlib', pkglist, "Listed packages: %s" % result.output)
+ self.assertIn('zlib-dev', pkglist, "Listed packages: %s" % result.output)
+ # No pkgspec, runtime
+ result = runCmd('oe-pkgdata-util list-pkgs -r')
+ pkglist = result.output.split()
+ self.assertIn('libz-dev', pkglist, "Listed packages: %s" % result.output)
+ # With recipe specified
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib')
+ pkglist = sorted(result.output.split())
+ try:
+ pkglist.remove('zlib-ptest') # in case ptest is disabled
+ except ValueError:
+ pass
+ self.assertEqual(pkglist, ['zlib', 'zlib-dbg', 'zlib-dev', 'zlib-doc', 'zlib-staticdev'], "Packages listed after remove: %s" % result.output)
+ # With recipe specified, runtime
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib -r')
+ pkglist = sorted(result.output.split())
+ try:
+ pkglist.remove('libz-ptest') # in case ptest is disabled
+ except ValueError:
+ pass
+ self.assertEqual(pkglist, ['libz-dbg', 'libz-dev', 'libz-doc', 'libz-staticdev', 'libz1'], "Packages listed after remove: %s" % result.output)
+ # With recipe specified and unpackaged
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib -u')
+ pkglist = sorted(result.output.split())
+ self.assertIn('zlib-locale', pkglist, "Listed packages: %s" % result.output)
+ # With recipe specified and unpackaged, runtime
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib -u -r')
+ pkglist = sorted(result.output.split())
+ self.assertIn('libz-locale', pkglist, "Listed packages: %s" % result.output)
+ # With recipe specified and pkgspec
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib "*-d*"')
+ pkglist = sorted(result.output.split())
+ self.assertEqual(pkglist, ['zlib-dbg', 'zlib-dev', 'zlib-doc'], "Packages listed: %s" % result.output)
+ # With recipe specified and pkgspec, runtime
+ result = runCmd('oe-pkgdata-util list-pkgs -p zlib -r "*-d*"')
+ pkglist = sorted(result.output.split())
+ self.assertEqual(pkglist, ['libz-dbg', 'libz-dev', 'libz-doc'], "Packages listed: %s" % result.output)
+
+ @OETestID(1201)
+ def test_list_pkg_files(self):
+ def splitoutput(output):
+ files = {}
+ curpkg = None
+ for line in output.splitlines():
+ if line.startswith('\t'):
+ self.assertTrue(curpkg, 'Unexpected non-package line:\n%s' % line)
+ files[curpkg].append(line.strip())
+ else:
+ self.assertTrue(line.rstrip().endswith(':'), 'Invalid package line in output:\n%s' % line)
+ curpkg = line.split(':')[0]
+ files[curpkg] = []
+ return files
+ bb_vars = get_bb_vars(['base_libdir', 'libdir', 'includedir', 'mandir'])
+ base_libdir = bb_vars['base_libdir']
+ libdir = bb_vars['libdir']
+ includedir = bb_vars['includedir']
+ mandir = bb_vars['mandir']
+ # Test recipe-space package name
+ result = runCmd('oe-pkgdata-util list-pkg-files zlib-dev zlib-doc')
+ files = splitoutput(result.output)
+ self.assertIn('zlib-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn(os.path.join(includedir, 'zlib.h'), files['zlib-dev'])
+ self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['zlib-doc'])
+ # Test runtime package name
+ result = runCmd('oe-pkgdata-util list-pkg-files -r libz1 libz-dev')
+ files = splitoutput(result.output)
+ self.assertIn('libz1', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertGreater(len(files['libz1']), 1)
+ libspec = os.path.join(base_libdir, 'libz.so.1.*')
+ found = False
+ for fileitem in files['libz1']:
+ if fnmatch.fnmatchcase(fileitem, libspec):
+ found = True
+ break
+ self.assertTrue(found, 'Could not find zlib library file %s in libz1 package file list: %s' % (libspec, files['libz1']))
+ self.assertIn(os.path.join(includedir, 'zlib.h'), files['libz-dev'])
+ # Test recipe
+ result = runCmd('oe-pkgdata-util list-pkg-files -p zlib')
+ files = splitoutput(result.output)
+ self.assertIn('zlib-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertNotIn('zlib-locale', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ # (ignore ptest, might not be there depending on config)
+ self.assertIn(os.path.join(includedir, 'zlib.h'), files['zlib-dev'])
+ self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['zlib-doc'])
+ self.assertIn(os.path.join(libdir, 'libz.a'), files['zlib-staticdev'])
+ # Test recipe, runtime
+ result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -r')
+ files = splitoutput(result.output)
+ self.assertIn('libz-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz1', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertNotIn('libz-locale', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn(os.path.join(includedir, 'zlib.h'), files['libz-dev'])
+ self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['libz-doc'])
+ self.assertIn(os.path.join(libdir, 'libz.a'), files['libz-staticdev'])
+ # Test recipe, unpackaged
+ result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -u')
+ files = splitoutput(result.output)
+ self.assertIn('zlib-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('zlib-locale', list(files.keys()), "listed pkgs. files: %s" %result.output) # this is the key one
+ self.assertIn(os.path.join(includedir, 'zlib.h'), files['zlib-dev'])
+ self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['zlib-doc'])
+ self.assertIn(os.path.join(libdir, 'libz.a'), files['zlib-staticdev'])
+ # Test recipe, runtime, unpackaged
+ result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -r -u')
+ files = splitoutput(result.output)
+ self.assertIn('libz-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz1', list(files.keys()), "listed pkgs. files: %s" %result.output)
+ self.assertIn('libz-locale', list(files.keys()), "listed pkgs. files: %s" %result.output) # this is the key one
+ self.assertIn(os.path.join(includedir, 'zlib.h'), files['libz-dev'])
+ self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['libz-doc'])
+ self.assertIn(os.path.join(libdir, 'libz.a'), files['libz-staticdev'])
+
+ @OETestID(1200)
+ def test_glob(self):
+ tempdir = tempfile.mkdtemp(prefix='pkgdataqa')
+ self.track_for_cleanup(tempdir)
+ pkglistfile = os.path.join(tempdir, 'pkglist')
+ with open(pkglistfile, 'w') as f:
+ f.write('libz1\n')
+ f.write('busybox\n')
+ result = runCmd('oe-pkgdata-util glob %s "*-dev"' % pkglistfile)
+ desiredresult = ['libz-dev', 'busybox-dev']
+ self.assertEqual(sorted(result.output.split()), sorted(desiredresult))
+ # The following should not error (because when we use this during rootfs construction, sometimes the complementary package won't exist)
+ result = runCmd('oe-pkgdata-util glob %s "*-nonexistent"' % pkglistfile)
+ self.assertEqual(result.output, '')
+ # Test exclude option
+ result = runCmd('oe-pkgdata-util glob %s "*-dev *-dbg" -x "^libz"' % pkglistfile)
+ resultlist = result.output.split()
+ self.assertNotIn('libz-dev', resultlist)
+ self.assertNotIn('libz-dbg', resultlist)
+
+ @OETestID(1206)
+ def test_specify_pkgdatadir(self):
+ result = runCmd('oe-pkgdata-util -p %s lookup-pkg zlib' % get_bb_var('PKGDATA_DIR'))
+ self.assertEqual(result.output, 'libz1')
diff --git a/meta/lib/oeqa/selftest/cases/prservice.py b/meta/lib/oeqa/selftest/cases/prservice.py
new file mode 100644
index 0000000000..ed36f0fed7
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/prservice.py
@@ -0,0 +1,131 @@
+import os
+import re
+import shutil
+import datetime
+
+import oeqa.utils.ftools as ftools
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.utils.network import get_free_port
+
+class BitbakePrTests(OESelftestTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super(BitbakePrTests, cls).setUpClass()
+ cls.pkgdata_dir = get_bb_var('PKGDATA_DIR')
+
+ def get_pr_version(self, package_name):
+ package_data_file = os.path.join(self.pkgdata_dir, 'runtime', package_name)
+ package_data = ftools.read_file(package_data_file)
+ find_pr = re.search("PKGR: r[0-9]+\.([0-9]+)", package_data)
+ self.assertTrue(find_pr, "No PKG revision found in %s" % package_data_file)
+ return int(find_pr.group(1))
+
+ def get_task_stamp(self, package_name, recipe_task):
+ stampdata = get_bb_var('STAMP', target=package_name).split('/')
+ prefix = stampdata[-1]
+ package_stamps_path = "/".join(stampdata[:-1])
+ stamps = []
+ for stamp in os.listdir(package_stamps_path):
+ find_stamp = re.match("%s\.%s\.([a-z0-9]{32})" % (re.escape(prefix), recipe_task), stamp)
+ if find_stamp:
+ stamps.append(find_stamp.group(1))
+ self.assertFalse(len(stamps) == 0, msg="Cound not find stamp for task %s for recipe %s" % (recipe_task, package_name))
+ self.assertFalse(len(stamps) > 1, msg="Found multiple %s stamps for the %s recipe in the %s directory." % (recipe_task, package_name, package_stamps_path))
+ return str(stamps[0])
+
+ def increment_package_pr(self, package_name):
+ inc_data = "do_package_append() {\n bb.build.exec_func('do_test_prserv', d)\n}\ndo_test_prserv() {\necho \"The current date is: %s\"\n}" % datetime.datetime.now()
+ self.write_recipeinc(package_name, inc_data)
+ res = bitbake(package_name, ignore_status=True)
+ self.delete_recipeinc(package_name)
+ self.assertEqual(res.status, 0, msg=res.output)
+ self.assertTrue("NOTE: Started PRServer with DBfile" in res.output, msg=res.output)
+
+ def config_pr_tests(self, package_name, package_type='rpm', pr_socket='localhost:0'):
+ config_package_data = 'PACKAGE_CLASSES = "package_%s"' % package_type
+ self.write_config(config_package_data)
+ config_server_data = 'PRSERV_HOST = "%s"' % pr_socket
+ self.append_config(config_server_data)
+
+ def run_test_pr_service(self, package_name, package_type='rpm', track_task='do_package', pr_socket='localhost:0'):
+ self.config_pr_tests(package_name, package_type, pr_socket)
+
+ self.increment_package_pr(package_name)
+ pr_1 = self.get_pr_version(package_name)
+ stamp_1 = self.get_task_stamp(package_name, track_task)
+
+ self.increment_package_pr(package_name)
+ pr_2 = self.get_pr_version(package_name)
+ stamp_2 = self.get_task_stamp(package_name, track_task)
+
+ self.assertTrue(pr_2 - pr_1 == 1, "Step between same pkg. revision is greater than 1")
+ self.assertTrue(stamp_1 != stamp_2, "Different pkg rev. but same stamp: %s" % stamp_1)
+
+ def run_test_pr_export_import(self, package_name, replace_current_db=True):
+ self.config_pr_tests(package_name)
+
+ self.increment_package_pr(package_name)
+ pr_1 = self.get_pr_version(package_name)
+
+ exported_db_path = os.path.join(self.builddir, 'export.inc')
+ export_result = runCmd("bitbake-prserv-tool export %s" % exported_db_path, ignore_status=True)
+ self.assertEqual(export_result.status, 0, msg="PR Service database export failed: %s" % export_result.output)
+
+ if replace_current_db:
+ current_db_path = os.path.join(get_bb_var('PERSISTENT_DIR'), 'prserv.sqlite3')
+ self.assertTrue(os.path.exists(current_db_path), msg="Path to current PR Service database is invalid: %s" % current_db_path)
+ os.remove(current_db_path)
+
+ import_result = runCmd("bitbake-prserv-tool import %s" % exported_db_path, ignore_status=True)
+ os.remove(exported_db_path)
+ self.assertEqual(import_result.status, 0, msg="PR Service database import failed: %s" % import_result.output)
+
+ self.increment_package_pr(package_name)
+ pr_2 = self.get_pr_version(package_name)
+
+ self.assertTrue(pr_2 - pr_1 == 1, "Step between same pkg. revision is greater than 1")
+
+ @OETestID(930)
+ def test_import_export_replace_db(self):
+ self.run_test_pr_export_import('m4')
+
+ @OETestID(931)
+ def test_import_export_override_db(self):
+ self.run_test_pr_export_import('m4', replace_current_db=False)
+
+ @OETestID(932)
+ def test_pr_service_rpm_arch_dep(self):
+ self.run_test_pr_service('m4', 'rpm', 'do_package')
+
+ @OETestID(934)
+ def test_pr_service_deb_arch_dep(self):
+ self.run_test_pr_service('m4', 'deb', 'do_package')
+
+ @OETestID(933)
+ def test_pr_service_ipk_arch_dep(self):
+ self.run_test_pr_service('m4', 'ipk', 'do_package')
+
+ @OETestID(935)
+ def test_pr_service_rpm_arch_indep(self):
+ self.run_test_pr_service('xcursor-transparent-theme', 'rpm', 'do_package')
+
+ @OETestID(937)
+ def test_pr_service_deb_arch_indep(self):
+ self.run_test_pr_service('xcursor-transparent-theme', 'deb', 'do_package')
+
+ @OETestID(936)
+ def test_pr_service_ipk_arch_indep(self):
+ self.run_test_pr_service('xcursor-transparent-theme', 'ipk', 'do_package')
+
+ @OETestID(1419)
+ def test_stopping_prservice_message(self):
+ port = get_free_port()
+
+ runCmd('bitbake-prserv --host localhost --port %s --loglevel=DEBUG --start' % port)
+ ret = runCmd('bitbake-prserv --host localhost --port %s --loglevel=DEBUG --stop' % port)
+
+ self.assertEqual(ret.status, 0)
+
diff --git a/meta/lib/oeqa/selftest/cases/recipetool.py b/meta/lib/oeqa/selftest/cases/recipetool.py
new file mode 100644
index 0000000000..46f0a7206b
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/recipetool.py
@@ -0,0 +1,694 @@
+import os
+import shutil
+import tempfile
+import urllib.parse
+
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var
+from oeqa.utils.commands import get_bb_vars, create_temp_layer
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.selftest.cases import devtool
+
+templayerdir = None
+
+def setUpModule():
+ global templayerdir
+ templayerdir = tempfile.mkdtemp(prefix='recipetoolqa')
+ create_temp_layer(templayerdir, 'selftestrecipetool')
+ runCmd('bitbake-layers add-layer %s' % templayerdir)
+
+
+def tearDownModule():
+ runCmd('bitbake-layers remove-layer %s' % templayerdir, ignore_status=True)
+ runCmd('rm -rf %s' % templayerdir)
+
+
+class RecipetoolBase(devtool.DevtoolBase):
+
+ def setUpLocal(self):
+ super(RecipetoolBase, self).setUpLocal()
+ self.templayerdir = templayerdir
+ self.tempdir = tempfile.mkdtemp(prefix='recipetoolqa')
+ self.track_for_cleanup(self.tempdir)
+ self.testfile = os.path.join(self.tempdir, 'testfile')
+ with open(self.testfile, 'w') as f:
+ f.write('Test file\n')
+
+ def tearDownLocal(self):
+ runCmd('rm -rf %s/recipes-*' % self.templayerdir)
+ super(RecipetoolBase, self).tearDownLocal()
+
+ def _try_recipetool_appendcmd(self, cmd, testrecipe, expectedfiles, expectedlines=None):
+ result = runCmd(cmd)
+ self.assertNotIn('Traceback', result.output)
+
+ # Check the bbappend was created and applies properly
+ recipefile = get_bb_var('FILE', testrecipe)
+ bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir)
+
+ # Check the bbappend contents
+ if expectedlines is not None:
+ with open(bbappendfile, 'r') as f:
+ self.assertEqual(expectedlines, f.readlines(), "Expected lines are not present in %s" % bbappendfile)
+
+ # Check file was copied
+ filesdir = os.path.join(os.path.dirname(bbappendfile), testrecipe)
+ for expectedfile in expectedfiles:
+ self.assertTrue(os.path.isfile(os.path.join(filesdir, expectedfile)), 'Expected file %s to be copied next to bbappend, but it wasn\'t' % expectedfile)
+
+ # Check no other files created
+ createdfiles = []
+ for root, _, files in os.walk(filesdir):
+ for f in files:
+ createdfiles.append(os.path.relpath(os.path.join(root, f), filesdir))
+ self.assertTrue(sorted(createdfiles), sorted(expectedfiles))
+
+ return bbappendfile, result.output
+
+
+class RecipetoolTests(RecipetoolBase):
+
+ @classmethod
+ def setUpClass(cls):
+ super(RecipetoolTests, cls).setUpClass()
+ # Ensure we have the right data in shlibs/pkgdata
+ cls.logger.info('Running bitbake to generate pkgdata')
+ bitbake('-c packagedata base-files coreutils busybox selftest-recipetool-appendfile')
+ bb_vars = get_bb_vars(['COREBASE', 'BBPATH'])
+ cls.corebase = bb_vars['COREBASE']
+ cls.bbpath = bb_vars['BBPATH']
+
+ def _try_recipetool_appendfile(self, testrecipe, destfile, newfile, options, expectedlines, expectedfiles):
+ cmd = 'recipetool appendfile %s %s %s %s' % (self.templayerdir, destfile, newfile, options)
+ return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines)
+
+ def _try_recipetool_appendfile_fail(self, destfile, newfile, checkerror):
+ cmd = 'recipetool appendfile %s %s %s' % (self.templayerdir, destfile, newfile)
+ result = runCmd(cmd, ignore_status=True)
+ self.assertNotEqual(result.status, 0, 'Command "%s" should have failed but didn\'t' % cmd)
+ self.assertNotIn('Traceback', result.output)
+ for errorstr in checkerror:
+ self.assertIn(errorstr, result.output)
+
+ @OETestID(1177)
+ def test_recipetool_appendfile_basic(self):
+ # Basic test
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n']
+ _, output = self._try_recipetool_appendfile('base-files', '/etc/motd', self.testfile, '', expectedlines, ['motd'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1183)
+ def test_recipetool_appendfile_invalid(self):
+ # Test some commands that should error
+ self._try_recipetool_appendfile_fail('/etc/passwd', self.testfile, ['ERROR: /etc/passwd cannot be handled by this tool', 'useradd', 'extrausers'])
+ self._try_recipetool_appendfile_fail('/etc/timestamp', self.testfile, ['ERROR: /etc/timestamp cannot be handled by this tool'])
+ self._try_recipetool_appendfile_fail('/dev/console', self.testfile, ['ERROR: /dev/console cannot be handled by this tool'])
+
+ @OETestID(1176)
+ def test_recipetool_appendfile_alternatives(self):
+ # Now try with a file we know should be an alternative
+ # (this is very much a fake example, but one we know is reliably an alternative)
+ self._try_recipetool_appendfile_fail('/bin/ls', self.testfile, ['ERROR: File /bin/ls is an alternative possibly provided by the following recipes:', 'coreutils', 'busybox'])
+ # Need a test file - should be executable
+ testfile2 = os.path.join(self.corebase, 'oe-init-build-env')
+ testfile2name = os.path.basename(testfile2)
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://%s"\n' % testfile2name,
+ '\n',
+ 'do_install_append() {\n',
+ ' install -d ${D}${base_bindir}\n',
+ ' install -m 0755 ${WORKDIR}/%s ${D}${base_bindir}/ls\n' % testfile2name,
+ '}\n']
+ self._try_recipetool_appendfile('coreutils', '/bin/ls', testfile2, '-r coreutils', expectedlines, [testfile2name])
+ # Now try bbappending the same file again, contents should not change
+ bbappendfile, _ = self._try_recipetool_appendfile('coreutils', '/bin/ls', self.testfile, '-r coreutils', expectedlines, [testfile2name])
+ # But file should have
+ copiedfile = os.path.join(os.path.dirname(bbappendfile), 'coreutils', testfile2name)
+ result = runCmd('diff -q %s %s' % (testfile2, copiedfile), ignore_status=True)
+ self.assertNotEqual(result.status, 0, 'New file should have been copied but was not %s' % result.output)
+
+ @OETestID(1178)
+ def test_recipetool_appendfile_binary(self):
+ # Try appending a binary file
+ # /bin/ls can be a symlink to /usr/bin/ls
+ ls = os.path.realpath("/bin/ls")
+ result = runCmd('recipetool appendfile %s /bin/ls %s -r coreutils' % (self.templayerdir, ls))
+ self.assertIn('WARNING: ', result.output)
+ self.assertIn('is a binary', result.output)
+
+ @OETestID(1173)
+ def test_recipetool_appendfile_add(self):
+ # Try arbitrary file add to a recipe
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://testfile"\n',
+ '\n',
+ 'do_install_append() {\n',
+ ' install -d ${D}${datadir}\n',
+ ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n',
+ '}\n']
+ self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase', expectedlines, ['testfile'])
+ # Try adding another file, this time where the source file is executable
+ # (so we're testing that, plus modifying an existing bbappend)
+ testfile2 = os.path.join(self.corebase, 'oe-init-build-env')
+ testfile2name = os.path.basename(testfile2)
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://testfile \\\n',
+ ' file://%s \\\n' % testfile2name,
+ ' "\n',
+ '\n',
+ 'do_install_append() {\n',
+ ' install -d ${D}${datadir}\n',
+ ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n',
+ ' install -m 0755 ${WORKDIR}/%s ${D}${datadir}/scriptname\n' % testfile2name,
+ '}\n']
+ self._try_recipetool_appendfile('netbase', '/usr/share/scriptname', testfile2, '-r netbase', expectedlines, ['testfile', testfile2name])
+
+ @OETestID(1174)
+ def test_recipetool_appendfile_add_bindir(self):
+ # Try arbitrary file add to a recipe, this time to a location such that should be installed as executable
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://testfile"\n',
+ '\n',
+ 'do_install_append() {\n',
+ ' install -d ${D}${bindir}\n',
+ ' install -m 0755 ${WORKDIR}/testfile ${D}${bindir}/selftest-recipetool-testbin\n',
+ '}\n']
+ _, output = self._try_recipetool_appendfile('netbase', '/usr/bin/selftest-recipetool-testbin', self.testfile, '-r netbase', expectedlines, ['testfile'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1175)
+ def test_recipetool_appendfile_add_machine(self):
+ # Try arbitrary file add to a recipe, this time to a location such that should be installed as executable
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'PACKAGE_ARCH = "${MACHINE_ARCH}"\n',
+ '\n',
+ 'SRC_URI_append_mymachine = " file://testfile"\n',
+ '\n',
+ 'do_install_append_mymachine() {\n',
+ ' install -d ${D}${datadir}\n',
+ ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n',
+ '}\n']
+ _, output = self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase -m mymachine', expectedlines, ['mymachine/testfile'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1184)
+ def test_recipetool_appendfile_orig(self):
+ # A file that's in SRC_URI and in do_install with the same name
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-orig', self.testfile, '', expectedlines, ['selftest-replaceme-orig'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1191)
+ def test_recipetool_appendfile_todir(self):
+ # A file that's in SRC_URI and in do_install with destination directory rather than file
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-todir', self.testfile, '', expectedlines, ['selftest-replaceme-todir'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1187)
+ def test_recipetool_appendfile_renamed(self):
+ # A file that's in SRC_URI with a different name to the destination file
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-renamed', self.testfile, '', expectedlines, ['file1'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1190)
+ def test_recipetool_appendfile_subdir(self):
+ # A file that's in SRC_URI in a subdir
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://testfile"\n',
+ '\n',
+ 'do_install_append() {\n',
+ ' install -d ${D}${datadir}\n',
+ ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-subdir\n',
+ '}\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-subdir', self.testfile, '', expectedlines, ['testfile'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1189)
+ def test_recipetool_appendfile_src_glob(self):
+ # A file that's in SRC_URI as a glob
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://testfile"\n',
+ '\n',
+ 'do_install_append() {\n',
+ ' install -d ${D}${datadir}\n',
+ ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-src-globfile\n',
+ '}\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-src-globfile', self.testfile, '', expectedlines, ['testfile'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1181)
+ def test_recipetool_appendfile_inst_glob(self):
+ # A file that's in do_install as a glob
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-globfile', self.testfile, '', expectedlines, ['selftest-replaceme-inst-globfile'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1182)
+ def test_recipetool_appendfile_inst_todir_glob(self):
+ # A file that's in do_install as a glob with destination as a directory
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-todir-globfile', self.testfile, '', expectedlines, ['selftest-replaceme-inst-todir-globfile'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1185)
+ def test_recipetool_appendfile_patch(self):
+ # A file that's added by a patch in SRC_URI
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://testfile"\n',
+ '\n',
+ 'do_install_append() {\n',
+ ' install -d ${D}${sysconfdir}\n',
+ ' install -m 0644 ${WORKDIR}/testfile ${D}${sysconfdir}/selftest-replaceme-patched\n',
+ '}\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/etc/selftest-replaceme-patched', self.testfile, '', expectedlines, ['testfile'])
+ for line in output.splitlines():
+ if 'WARNING: ' in line:
+ self.assertIn('add-file.patch', line, 'Unexpected warning found in output:\n%s' % line)
+ break
+ else:
+ self.fail('Patch warning not found in output:\n%s' % output)
+
+ @OETestID(1188)
+ def test_recipetool_appendfile_script(self):
+ # Now, a file that's in SRC_URI but installed by a script (so no mention in do_install)
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://testfile"\n',
+ '\n',
+ 'do_install_append() {\n',
+ ' install -d ${D}${datadir}\n',
+ ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-scripted\n',
+ '}\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-scripted', self.testfile, '', expectedlines, ['testfile'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1180)
+ def test_recipetool_appendfile_inst_func(self):
+ # A file that's installed from a function called by do_install
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-func', self.testfile, '', expectedlines, ['selftest-replaceme-inst-func'])
+ self.assertNotIn('WARNING: ', output)
+
+ @OETestID(1186)
+ def test_recipetool_appendfile_postinstall(self):
+ # A file that's created by a postinstall script (and explicitly mentioned in it)
+ # First try without specifying recipe
+ self._try_recipetool_appendfile_fail('/usr/share/selftest-replaceme-postinst', self.testfile, ['File /usr/share/selftest-replaceme-postinst may be written out in a pre/postinstall script of the following recipes:', 'selftest-recipetool-appendfile'])
+ # Now specify recipe
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n',
+ 'SRC_URI += "file://testfile"\n',
+ '\n',
+ 'do_install_append() {\n',
+ ' install -d ${D}${datadir}\n',
+ ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-postinst\n',
+ '}\n']
+ _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-postinst', self.testfile, '-r selftest-recipetool-appendfile', expectedlines, ['testfile'])
+
+ @OETestID(1179)
+ def test_recipetool_appendfile_extlayer(self):
+ # Try creating a bbappend in a layer that's not in bblayers.conf and has a different structure
+ exttemplayerdir = os.path.join(self.tempdir, 'extlayer')
+ self._create_temp_layer(exttemplayerdir, False, 'oeselftestextlayer', recipepathspec='metadata/recipes/recipes-*/*')
+ result = runCmd('recipetool appendfile %s /usr/share/selftest-replaceme-orig %s' % (exttemplayerdir, self.testfile))
+ self.assertNotIn('Traceback', result.output)
+ createdfiles = []
+ for root, _, files in os.walk(exttemplayerdir):
+ for f in files:
+ createdfiles.append(os.path.relpath(os.path.join(root, f), exttemplayerdir))
+ createdfiles.remove('conf/layer.conf')
+ expectedfiles = ['metadata/recipes/recipes-test/selftest-recipetool-appendfile/selftest-recipetool-appendfile.bbappend',
+ 'metadata/recipes/recipes-test/selftest-recipetool-appendfile/selftest-recipetool-appendfile/selftest-replaceme-orig']
+ self.assertEqual(sorted(createdfiles), sorted(expectedfiles))
+
+ @OETestID(1192)
+ def test_recipetool_appendfile_wildcard(self):
+
+ def try_appendfile_wc(options):
+ result = runCmd('recipetool appendfile %s /etc/profile %s %s' % (self.templayerdir, self.testfile, options))
+ self.assertNotIn('Traceback', result.output)
+ bbappendfile = None
+ for root, _, files in os.walk(self.templayerdir):
+ for f in files:
+ if f.endswith('.bbappend'):
+ bbappendfile = f
+ break
+ if not bbappendfile:
+ self.fail('No bbappend file created')
+ runCmd('rm -rf %s/recipes-*' % self.templayerdir)
+ return bbappendfile
+
+ # Check without wildcard option
+ recipefn = os.path.basename(get_bb_var('FILE', 'base-files'))
+ filename = try_appendfile_wc('')
+ self.assertEqual(filename, recipefn.replace('.bb', '.bbappend'))
+ # Now check with wildcard option
+ filename = try_appendfile_wc('-w')
+ self.assertEqual(filename, recipefn.split('_')[0] + '_%.bbappend')
+
+ @OETestID(1193)
+ def test_recipetool_create(self):
+ # Try adding a recipe
+ tempsrc = os.path.join(self.tempdir, 'srctree')
+ os.makedirs(tempsrc)
+ recipefile = os.path.join(self.tempdir, 'logrotate_3.8.7.bb')
+ srcuri = 'https://github.com/logrotate/logrotate/archive/r3-8-7.tar.gz'
+ result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc))
+ self.assertTrue(os.path.isfile(recipefile))
+ checkvars = {}
+ checkvars['LICENSE'] = 'GPLv2'
+ checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=18810669f13b87348459e611d31ab760'
+ checkvars['SRC_URI'] = 'https://github.com/logrotate/logrotate/archive/r3-8-7.tar.gz'
+ checkvars['SRC_URI[md5sum]'] = '6b1aa0e0d07eda3c9a2526520850397a'
+ checkvars['SRC_URI[sha256sum]'] = 'dece4bfeb9d8374a0ecafa34be139b5a697db5c926dcc69a9b8715431a22e733'
+ self._test_recipe_contents(recipefile, checkvars, [])
+
+ @OETestID(1194)
+ def test_recipetool_create_git(self):
+ if 'x11' not in get_bb_var('DISTRO_FEATURES'):
+ self.skipTest('Test requires x11 as distro feature')
+ # Ensure we have the right data in shlibs/pkgdata
+ bitbake('libpng pango libx11 libxext jpeg libcheck')
+ # Try adding a recipe
+ tempsrc = os.path.join(self.tempdir, 'srctree')
+ os.makedirs(tempsrc)
+ recipefile = os.path.join(self.tempdir, 'libmatchbox.bb')
+ srcuri = 'git://git.yoctoproject.org/libmatchbox'
+ result = runCmd(['recipetool', 'create', '-o', recipefile, srcuri + ";rev=9f7cf8895ae2d39c465c04cc78e918c157420269", '-x', tempsrc])
+ self.assertTrue(os.path.isfile(recipefile), 'recipetool did not create recipe file; output:\n%s' % result.output)
+ checkvars = {}
+ checkvars['LICENSE'] = 'LGPLv2.1'
+ checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34'
+ checkvars['S'] = '${WORKDIR}/git'
+ checkvars['PV'] = '1.11+git${SRCPV}'
+ checkvars['SRC_URI'] = srcuri
+ checkvars['DEPENDS'] = set(['libcheck', 'libjpeg-turbo', 'libpng', 'libx11', 'libxext', 'pango'])
+ inherits = ['autotools', 'pkgconfig']
+ self._test_recipe_contents(recipefile, checkvars, inherits)
+
+ @OETestID(1392)
+ def test_recipetool_create_simple(self):
+ # Try adding a recipe
+ temprecipe = os.path.join(self.tempdir, 'recipe')
+ os.makedirs(temprecipe)
+ pv = '1.7.3.0'
+ srcuri = 'http://www.dest-unreach.org/socat/download/socat-%s.tar.bz2' % pv
+ result = runCmd('recipetool create %s -o %s' % (srcuri, temprecipe))
+ dirlist = os.listdir(temprecipe)
+ if len(dirlist) > 1:
+ self.fail('recipetool created more than just one file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist)))
+ if len(dirlist) < 1 or not os.path.isfile(os.path.join(temprecipe, dirlist[0])):
+ self.fail('recipetool did not create recipe file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist)))
+ self.assertEqual(dirlist[0], 'socat_%s.bb' % pv, 'Recipe file incorrectly named')
+ checkvars = {}
+ checkvars['LICENSE'] = set(['Unknown', 'GPLv2'])
+ checkvars['LIC_FILES_CHKSUM'] = set(['file://COPYING.OpenSSL;md5=5c9bccc77f67a8328ef4ebaf468116f4', 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'])
+ # We don't check DEPENDS since they are variable for this recipe depending on what's in the sysroot
+ checkvars['S'] = None
+ checkvars['SRC_URI'] = srcuri.replace(pv, '${PV}')
+ inherits = ['autotools']
+ self._test_recipe_contents(os.path.join(temprecipe, dirlist[0]), checkvars, inherits)
+
+ @OETestID(1418)
+ def test_recipetool_create_cmake(self):
+ # Try adding a recipe
+ temprecipe = os.path.join(self.tempdir, 'recipe')
+ os.makedirs(temprecipe)
+ recipefile = os.path.join(temprecipe, 'navit_0.5.0.bb')
+ srcuri = 'http://downloads.sourceforge.net/project/navit/v0.5.0/navit-0.5.0.tar.gz'
+ result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+ self.assertTrue(os.path.isfile(recipefile))
+ checkvars = {}
+ checkvars['LICENSE'] = set(['Unknown', 'GPLv2', 'LGPLv2'])
+ checkvars['SRC_URI'] = 'http://downloads.sourceforge.net/project/navit/v${PV}/navit-${PV}.tar.gz'
+ checkvars['SRC_URI[md5sum]'] = '242f398e979a6b8c0f3c802b63435b68'
+ checkvars['SRC_URI[sha256sum]'] = '13353481d7fc01a4f64e385dda460b51496366bba0fd2cc85a89a0747910e94d'
+ checkvars['DEPENDS'] = set(['freetype', 'zlib', 'openssl', 'glib-2.0', 'virtual/libgl', 'virtual/egl', 'gtk+', 'libpng', 'libsdl', 'freeglut', 'dbus-glib'])
+ inherits = ['cmake', 'python-dir', 'gettext', 'pkgconfig']
+ self._test_recipe_contents(recipefile, checkvars, inherits)
+
+ def test_recipetool_create_github(self):
+ # Basic test to see if github URL mangling works
+ temprecipe = os.path.join(self.tempdir, 'recipe')
+ os.makedirs(temprecipe)
+ recipefile = os.path.join(temprecipe, 'meson_git.bb')
+ srcuri = 'https://github.com/mesonbuild/meson;rev=0.32.0'
+ result = runCmd(['recipetool', 'create', '-o', temprecipe, srcuri])
+ self.assertTrue(os.path.isfile(recipefile))
+ checkvars = {}
+ checkvars['LICENSE'] = set(['Apache-2.0'])
+ checkvars['SRC_URI'] = 'git://github.com/mesonbuild/meson;protocol=https'
+ inherits = ['setuptools']
+ self._test_recipe_contents(recipefile, checkvars, inherits)
+
+ def test_recipetool_create_github_tarball(self):
+ # Basic test to ensure github URL mangling doesn't apply to release tarballs
+ temprecipe = os.path.join(self.tempdir, 'recipe')
+ os.makedirs(temprecipe)
+ pv = '0.32.0'
+ recipefile = os.path.join(temprecipe, 'meson_%s.bb' % pv)
+ srcuri = 'https://github.com/mesonbuild/meson/releases/download/%s/meson-%s.tar.gz' % (pv, pv)
+ result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+ self.assertTrue(os.path.isfile(recipefile))
+ checkvars = {}
+ checkvars['LICENSE'] = set(['Apache-2.0'])
+ checkvars['SRC_URI'] = 'https://github.com/mesonbuild/meson/releases/download/${PV}/meson-${PV}.tar.gz'
+ inherits = ['setuptools']
+ self._test_recipe_contents(recipefile, checkvars, inherits)
+
+ def test_recipetool_create_git_http(self):
+ # Basic test to check http git URL mangling works
+ temprecipe = os.path.join(self.tempdir, 'recipe')
+ os.makedirs(temprecipe)
+ recipefile = os.path.join(temprecipe, 'matchbox-terminal_git.bb')
+ srcuri = 'http://git.yoctoproject.org/git/matchbox-terminal'
+ result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+ self.assertTrue(os.path.isfile(recipefile))
+ checkvars = {}
+ checkvars['LICENSE'] = set(['GPLv2'])
+ checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/matchbox-terminal;protocol=http'
+ inherits = ['pkgconfig', 'autotools']
+ self._test_recipe_contents(recipefile, checkvars, inherits)
+
+ def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
+ dstdir = basedstdir
+ self.assertTrue(os.path.exists(dstdir))
+ for p in paths:
+ dstdir = os.path.join(dstdir, p)
+ if not os.path.exists(dstdir):
+ os.makedirs(dstdir)
+ self.track_for_cleanup(dstdir)
+ dstfile = os.path.join(dstdir, os.path.basename(srcfile))
+ if srcfile != dstfile:
+ shutil.copy(srcfile, dstfile)
+ self.track_for_cleanup(dstfile)
+
+ def test_recipetool_load_plugin(self):
+ """Test that recipetool loads only the first found plugin in BBPATH."""
+
+ recipetool = runCmd("which recipetool")
+ fromname = runCmd("recipetool --quiet pluginfile")
+ srcfile = fromname.output
+ searchpath = self.bbpath.split(':') + [os.path.dirname(recipetool.output)]
+ plugincontent = []
+ with open(srcfile) as fh:
+ plugincontent = fh.readlines()
+ try:
+ self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
+ for path in searchpath:
+ self._copy_file_with_cleanup(srcfile, path, 'lib', 'recipetool')
+ result = runCmd("recipetool --quiet count")
+ self.assertEqual(result.output, '1')
+ result = runCmd("recipetool --quiet multiloaded")
+ self.assertEqual(result.output, "no")
+ for path in searchpath:
+ result = runCmd("recipetool --quiet bbdir")
+ self.assertEqual(result.output, path)
+ os.unlink(os.path.join(result.output, 'lib', 'recipetool', 'bbpath.py'))
+ finally:
+ with open(srcfile, 'w') as fh:
+ fh.writelines(plugincontent)
+
+
+class RecipetoolAppendsrcBase(RecipetoolBase):
+ def _try_recipetool_appendsrcfile(self, testrecipe, newfile, destfile, options, expectedlines, expectedfiles):
+ cmd = 'recipetool appendsrcfile %s %s %s %s %s' % (options, self.templayerdir, testrecipe, newfile, destfile)
+ return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines)
+
+ def _try_recipetool_appendsrcfiles(self, testrecipe, newfiles, expectedlines=None, expectedfiles=None, destdir=None, options=''):
+
+ if destdir:
+ options += ' -D %s' % destdir
+
+ if expectedfiles is None:
+ expectedfiles = [os.path.basename(f) for f in newfiles]
+
+ cmd = 'recipetool appendsrcfiles %s %s %s %s' % (options, self.templayerdir, testrecipe, ' '.join(newfiles))
+ return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines)
+
+ def _try_recipetool_appendsrcfile_fail(self, testrecipe, newfile, destfile, checkerror):
+ cmd = 'recipetool appendsrcfile %s %s %s %s' % (self.templayerdir, testrecipe, newfile, destfile or '')
+ result = runCmd(cmd, ignore_status=True)
+ self.assertNotEqual(result.status, 0, 'Command "%s" should have failed but didn\'t' % cmd)
+ self.assertNotIn('Traceback', result.output)
+ for errorstr in checkerror:
+ self.assertIn(errorstr, result.output)
+
+ @staticmethod
+ def _get_first_file_uri(recipe):
+ '''Return the first file:// in SRC_URI for the specified recipe.'''
+ src_uri = get_bb_var('SRC_URI', recipe).split()
+ for uri in src_uri:
+ p = urllib.parse.urlparse(uri)
+ if p.scheme == 'file':
+ return p.netloc + p.path
+
+ def _test_appendsrcfile(self, testrecipe, filename=None, destdir=None, has_src_uri=True, srcdir=None, newfile=None, options=''):
+ if newfile is None:
+ newfile = self.testfile
+
+ if srcdir:
+ if destdir:
+ expected_subdir = os.path.join(srcdir, destdir)
+ else:
+ expected_subdir = srcdir
+ else:
+ options += " -W"
+ expected_subdir = destdir
+
+ if filename:
+ if destdir:
+ destpath = os.path.join(destdir, filename)
+ else:
+ destpath = filename
+ else:
+ filename = os.path.basename(newfile)
+ if destdir:
+ destpath = destdir + os.sep
+ else:
+ destpath = '.' + os.sep
+
+ expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
+ '\n']
+ if has_src_uri:
+ uri = 'file://%s' % filename
+ if expected_subdir:
+ uri += ';subdir=%s' % expected_subdir
+ expectedlines[0:0] = ['SRC_URI += "%s"\n' % uri,
+ '\n']
+
+ return self._try_recipetool_appendsrcfile(testrecipe, newfile, destpath, options, expectedlines, [filename])
+
+ def _test_appendsrcfiles(self, testrecipe, newfiles, expectedfiles=None, destdir=None, options=''):
+ if expectedfiles is None:
+ expectedfiles = [os.path.basename(n) for n in newfiles]
+
+ self._try_recipetool_appendsrcfiles(testrecipe, newfiles, expectedfiles=expectedfiles, destdir=destdir, options=options)
+
+ bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'FILESEXTRAPATHS'], testrecipe)
+ src_uri = bb_vars['SRC_URI'].split()
+ for f in expectedfiles:
+ if destdir:
+ self.assertIn('file://%s;subdir=%s' % (f, destdir), src_uri)
+ else:
+ self.assertIn('file://%s' % f, src_uri)
+
+ recipefile = bb_vars['FILE']
+ bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir)
+ filesdir = os.path.join(os.path.dirname(bbappendfile), testrecipe)
+ filesextrapaths = bb_vars['FILESEXTRAPATHS'].split(':')
+ self.assertIn(filesdir, filesextrapaths)
+
+
+
+
+class RecipetoolAppendsrcTests(RecipetoolAppendsrcBase):
+
+ @OETestID(1273)
+ def test_recipetool_appendsrcfile_basic(self):
+ self._test_appendsrcfile('base-files', 'a-file')
+
+ @OETestID(1274)
+ def test_recipetool_appendsrcfile_basic_wildcard(self):
+ testrecipe = 'base-files'
+ self._test_appendsrcfile(testrecipe, 'a-file', options='-w')
+ recipefile = get_bb_var('FILE', testrecipe)
+ bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir)
+ self.assertEqual(os.path.basename(bbappendfile), '%s_%%.bbappend' % testrecipe)
+
+ @OETestID(1281)
+ def test_recipetool_appendsrcfile_subdir_basic(self):
+ self._test_appendsrcfile('base-files', 'a-file', 'tmp')
+
+ @OETestID(1282)
+ def test_recipetool_appendsrcfile_subdir_basic_dirdest(self):
+ self._test_appendsrcfile('base-files', destdir='tmp')
+
+ @OETestID(1280)
+ def test_recipetool_appendsrcfile_srcdir_basic(self):
+ testrecipe = 'bash'
+ bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe)
+ srcdir = bb_vars['S']
+ workdir = bb_vars['WORKDIR']
+ subdir = os.path.relpath(srcdir, workdir)
+ self._test_appendsrcfile(testrecipe, 'a-file', srcdir=subdir)
+
+ @OETestID(1275)
+ def test_recipetool_appendsrcfile_existing_in_src_uri(self):
+ testrecipe = 'base-files'
+ filepath = self._get_first_file_uri(testrecipe)
+ self.assertTrue(filepath, 'Unable to test, no file:// uri found in SRC_URI for %s' % testrecipe)
+ self._test_appendsrcfile(testrecipe, filepath, has_src_uri=False)
+
+ @OETestID(1276)
+ def test_recipetool_appendsrcfile_existing_in_src_uri_diff_params(self):
+ testrecipe = 'base-files'
+ subdir = 'tmp'
+ filepath = self._get_first_file_uri(testrecipe)
+ self.assertTrue(filepath, 'Unable to test, no file:// uri found in SRC_URI for %s' % testrecipe)
+
+ output = self._test_appendsrcfile(testrecipe, filepath, subdir, has_src_uri=False)
+ self.assertTrue(any('with different parameters' in l for l in output))
+
+ @OETestID(1277)
+ def test_recipetool_appendsrcfile_replace_file_srcdir(self):
+ testrecipe = 'bash'
+ filepath = 'Makefile.in'
+ bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe)
+ srcdir = bb_vars['S']
+ workdir = bb_vars['WORKDIR']
+ subdir = os.path.relpath(srcdir, workdir)
+
+ self._test_appendsrcfile(testrecipe, filepath, srcdir=subdir)
+ bitbake('%s:do_unpack' % testrecipe)
+ self.assertEqual(open(self.testfile, 'r').read(), open(os.path.join(srcdir, filepath), 'r').read())
+
+ @OETestID(1278)
+ def test_recipetool_appendsrcfiles_basic(self, destdir=None):
+ newfiles = [self.testfile]
+ for i in range(1, 5):
+ testfile = os.path.join(self.tempdir, 'testfile%d' % i)
+ with open(testfile, 'w') as f:
+ f.write('Test file %d\n' % i)
+ newfiles.append(testfile)
+ self._test_appendsrcfiles('gcc', newfiles, destdir=destdir, options='-W')
+
+ @OETestID(1279)
+ def test_recipetool_appendsrcfiles_basic_subdir(self):
+ self.test_recipetool_appendsrcfiles_basic(destdir='testdir')
diff --git a/meta/lib/oeqa/selftest/cases/runqemu.py b/meta/lib/oeqa/selftest/cases/runqemu.py
new file mode 100644
index 0000000000..4050a4123b
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/runqemu.py
@@ -0,0 +1,141 @@
+#
+# Copyright (c) 2017 Wind River Systems, Inc.
+#
+
+import re
+import logging
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import bitbake, runqemu, get_bb_var
+from oeqa.core.decorator.oeid import OETestID
+
+class RunqemuTests(OESelftestTestCase):
+ """Runqemu test class"""
+
+ image_is_ready = False
+ deploy_dir_image = ''
+
+ def setUpLocal(self):
+ super(RunqemuTests, self).setUpLocal()
+ self.recipe = 'core-image-minimal'
+ self.machine = 'qemux86-64'
+ self.fstypes = "ext4 iso hddimg vmdk qcow2 vdi"
+ self.cmd_common = "runqemu nographic"
+
+ # Avoid emit the same record multiple times.
+ mainlogger = logging.getLogger("BitBake.Main")
+ mainlogger.propagate = False
+
+ self.write_config(
+"""
+MACHINE = "%s"
+IMAGE_FSTYPES = "%s"
+# 10 means 1 second
+SYSLINUX_TIMEOUT = "10"
+"""
+% (self.machine, self.fstypes)
+ )
+
+ if not RunqemuTests.image_is_ready:
+ RunqemuTests.deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
+ bitbake(self.recipe)
+ RunqemuTests.image_is_ready = True
+
+ @OETestID(2001)
+ def test_boot_machine(self):
+ """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)
+
+ @OETestID(2002)
+ def test_boot_machine_ext4(self):
+ """Test runqemu machine ext4"""
+ 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)
+
+ @OETestID(2003)
+ def test_boot_machine_iso(self):
+ """Test runqemu machine iso"""
+ 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(' -cdrom ' in 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)
+
+ @OETestID(2005)
+ def test_boot_recipe_image_vmdk(self):
+ """Test runqemu recipe-image vmdk"""
+ cmd = "%s %s 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)
+
+ @OETestID(2006)
+ def test_boot_recipe_image_vdi(self):
+ """Test runqemu recipe-image vdi"""
+ cmd = "%s %s 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)
+
+ @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)
+
+ @OETestID(2008)
+ def test_boot_deploy_hddimg(self):
+ """Test runqemu deploy_dir_image hddimg"""
+ 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)
+
+ @OETestID(2009)
+ def test_boot_machine_slirp(self):
+ """Test runqemu machine slirp"""
+ 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)
+
+ @OETestID(2009)
+ def test_boot_machine_slirp_qcow2(self):
+ """Test runqemu machine slirp qcow2"""
+ cmd = "%s slirp 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)
+
+ @OETestID(2010)
+ def test_boot_qemu_boot(self):
+ """Test runqemu /path/to/image.qemuboot.conf"""
+ qemuboot_conf = "%s-%s.qemuboot.conf" % (self.recipe, self.machine)
+ qemuboot_conf = os.path.join(self.deploy_dir_image, qemuboot_conf)
+ if not os.path.exists(qemuboot_conf):
+ 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)
+
+ @OETestID(2011)
+ def test_boot_rootfs(self):
+ """Test runqemu /path/to/rootfs.ext4"""
+ rootfs = "%s-%s.ext4" % (self.recipe, self.machine)
+ rootfs = os.path.join(self.deploy_dir_image, rootfs)
+ if not os.path.exists(rootfs):
+ 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)
diff --git a/meta/lib/oeqa/selftest/cases/runtime_test.py b/meta/lib/oeqa/selftest/cases/runtime_test.py
new file mode 100644
index 0000000000..9fec4d869b
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/runtime_test.py
@@ -0,0 +1,239 @@
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
+from oeqa.core.decorator.oeid import OETestID
+import os
+import re
+
+class TestExport(OESelftestTestCase):
+
+ @classmethod
+ def tearDownClass(cls):
+ runCmd("rm -rf /tmp/sdk")
+ super(TestExport, cls).tearDownClass()
+
+ def test_testexport_basic(self):
+ """
+ Summary: Check basic testexport functionality with only ping test enabled.
+ Expected: 1. testexport directory must be created.
+ 2. runexported.py must run without any error/exception.
+ 3. ping test must succeed.
+ Product: oe-core
+ Author: Mariano Lopez <mariano.lopez@intel.com>
+ """
+
+ features = 'INHERIT += "testexport"\n'
+ # These aren't the actual IP addresses but testexport class needs something defined
+ features += 'TEST_SERVER_IP = "192.168.7.1"\n'
+ features += 'TEST_TARGET_IP = "192.168.7.1"\n'
+ features += 'TEST_SUITES = "ping"\n'
+ self.write_config(features)
+
+ # Build tesexport for core-image-minimal
+ bitbake('core-image-minimal')
+ bitbake('-c testexport core-image-minimal')
+
+ testexport_dir = get_bb_var('TEST_EXPORT_DIR', 'core-image-minimal')
+
+ # Verify if TEST_EXPORT_DIR was created
+ isdir = os.path.isdir(testexport_dir)
+ self.assertEqual(True, isdir, 'Failed to create testexport dir: %s' % testexport_dir)
+
+ with runqemu('core-image-minimal') as qemu:
+ # Attempt to run runexported.py to perform ping test
+ test_path = os.path.join(testexport_dir, "oe-test")
+ data_file = os.path.join(testexport_dir, 'data', 'testdata.json')
+ manifest = os.path.join(testexport_dir, 'data', 'manifest')
+ cmd = ("%s runtime --test-data-file %s --packages-manifest %s "
+ "--target-ip %s --server-ip %s --quiet"
+ % (test_path, data_file, manifest, qemu.ip, qemu.server_ip))
+ result = runCmd(cmd)
+ # Verify ping test was succesful
+ self.assertEqual(0, result.status, 'oe-test runtime returned a non 0 status')
+
+ def test_testexport_sdk(self):
+ """
+ Summary: Check sdk functionality for testexport.
+ Expected: 1. testexport directory must be created.
+ 2. SDK tarball must exists.
+ 3. Uncompressing of tarball must succeed.
+ 4. Check if the SDK directory is added to PATH.
+ 5. Run tar from the SDK directory.
+ Product: oe-core
+ Author: Mariano Lopez <mariano.lopez@intel.com>
+ """
+
+ features = 'INHERIT += "testexport"\n'
+ # These aren't the actual IP addresses but testexport class needs something defined
+ features += 'TEST_SERVER_IP = "192.168.7.1"\n'
+ features += 'TEST_TARGET_IP = "192.168.7.1"\n'
+ features += 'TEST_SUITES = "ping"\n'
+ features += 'TEST_EXPORT_SDK_ENABLED = "1"\n'
+ features += 'TEST_EXPORT_SDK_PACKAGES = "nativesdk-tar"\n'
+ self.write_config(features)
+
+ # Build tesexport for core-image-minimal
+ bitbake('core-image-minimal')
+ bitbake('-c testexport core-image-minimal')
+
+ needed_vars = ['TEST_EXPORT_DIR', 'TEST_EXPORT_SDK_DIR', 'TEST_EXPORT_SDK_NAME']
+ bb_vars = get_bb_vars(needed_vars, 'core-image-minimal')
+ testexport_dir = bb_vars['TEST_EXPORT_DIR']
+ sdk_dir = bb_vars['TEST_EXPORT_SDK_DIR']
+ sdk_name = bb_vars['TEST_EXPORT_SDK_NAME']
+
+ # Check for SDK
+ tarball_name = "%s.sh" % sdk_name
+ tarball_path = os.path.join(testexport_dir, sdk_dir, tarball_name)
+ msg = "Couldn't find SDK tarball: %s" % tarball_path
+ self.assertEqual(os.path.isfile(tarball_path), True, msg)
+
+ # Extract SDK and run tar from SDK
+ result = runCmd("%s -y -d /tmp/sdk" % tarball_path)
+ self.assertEqual(0, result.status, "Couldn't extract SDK")
+
+ env_script = result.output.split()[-1]
+ result = runCmd(". %s; which tar" % env_script, shell=True)
+ self.assertEqual(0, result.status, "Couldn't setup SDK environment")
+ is_sdk_tar = True if "/tmp/sdk" in result.output else False
+ self.assertTrue(is_sdk_tar, "Couldn't setup SDK environment")
+
+ tar_sdk = result.output
+ result = runCmd("%s --version" % tar_sdk)
+ self.assertEqual(0, result.status, "Couldn't run tar from SDK")
+
+
+class TestImage(OESelftestTestCase):
+
+ def test_testimage_install(self):
+ """
+ Summary: Check install packages functionality for testimage/testexport.
+ Expected: 1. Import tests from a directory other than meta.
+ 2. Check install/uninstall of socat.
+ 3. Check that remote package feeds can be accessed
+ Product: oe-core
+ Author: Mariano Lopez <mariano.lopez@intel.com>
+ Author: Alexander Kanavin <alexander.kanavin@intel.com>
+ """
+ if get_bb_var('DISTRO') == 'poky-tiny':
+ self.skipTest('core-image-full-cmdline not buildable for poky-tiny')
+
+ features = 'INHERIT += "testimage"\n'
+ features += 'TEST_SUITES = "ping ssh selftest"\n'
+ # We don't yet know what the server ip and port will be - they will be patched
+ # in at the start of the on-image test
+ features += 'PACKAGE_FEED_URIS = "http://bogus_ip:bogus_port"\n'
+ features += 'EXTRA_IMAGE_FEATURES += "package-management"\n'
+ features += 'PACKAGE_CLASSES = "package_rpm"'
+ self.write_config(features)
+
+ # Build core-image-sato and testimage
+ bitbake('core-image-full-cmdline socat')
+ bitbake('-c testimage core-image-full-cmdline')
+
+class Postinst(OESelftestTestCase):
+ @OETestID(1540)
+ def test_verify_postinst(self):
+ """
+ Summary: The purpose of this test is to verify the execution order of postinst Bugzilla ID: [5319]
+ Expected :
+ 1. Compile a minimal image.
+ 2. The compiled image will add the created layer with the recipes postinst[ abdpt]
+ 3. Run qemux86
+ 4. Validate the task execution order
+ Author: Francisco Pedraza <francisco.j.pedraza.gonzalez@intel.com>
+ """
+ features = 'INHERIT += "testimage"\n'
+ features += 'CORE_IMAGE_EXTRA_INSTALL += "postinst-at-rootfs \
+postinst-delayed-a \
+postinst-delayed-b \
+postinst-delayed-d \
+postinst-delayed-p \
+postinst-delayed-t \
+"\n'
+ self.write_config(features)
+
+ bitbake('core-image-minimal -f ')
+
+ postinst_list = ['100-postinst-at-rootfs',
+ '101-postinst-delayed-a',
+ '102-postinst-delayed-b',
+ '103-postinst-delayed-d',
+ '104-postinst-delayed-p',
+ '105-postinst-delayed-t']
+ path_workdir = get_bb_var('WORKDIR','core-image-minimal')
+ workspacedir = 'testimage/qemu_boot_log'
+ workspacedir = os.path.join(path_workdir, workspacedir)
+ rexp = re.compile("^Running postinst .*/(?P<postinst>.*)\.\.\.$")
+ with runqemu('core-image-minimal') as qemu:
+ with open(workspacedir) as f:
+ found = False
+ idx = 0
+ for line in f.readlines():
+ line = line.strip().replace("^M","")
+ if not line: # To avoid empty lines
+ continue
+ m = rexp.search(line)
+ if m:
+ self.assertEqual(postinst_list[idx], m.group('postinst'), "Fail")
+ idx = idx+1
+ found = True
+ elif found:
+ self.assertEqual(idx, len(postinst_list), "Not found all postinsts")
+ break
+
+ @OETestID(1545)
+ def test_postinst_rootfs_and_boot(self):
+ """
+ Summary: The purpose of this test case is to verify Post-installation
+ scripts are called when rootfs is created and also test
+ that script can be delayed to run at first boot.
+ Dependencies: NA
+ Steps: 1. Add proper configuration to local.conf file
+ 2. Build a "core-image-minimal" image
+ 3. Verify that file created by postinst_rootfs recipe is
+ present on rootfs dir.
+ 4. Boot the image created on qemu and verify that the file
+ created by postinst_boot recipe is present on image.
+ Expected: The files are successfully created during rootfs and boot
+ time for 3 different package managers: rpm,ipk,deb and
+ for initialization managers: sysvinit and systemd.
+
+ """
+ file_rootfs_name = "this-was-created-at-rootfstime"
+ fileboot_name = "this-was-created-at-first-boot"
+ rootfs_pkg = 'postinst-at-rootfs'
+ boot_pkg = 'postinst-delayed-a'
+ #Step 1
+ common_features = 'MACHINE = "qemux86"\n'
+ common_features += 'CORE_IMAGE_EXTRA_INSTALL += "%s %s "\n'% (rootfs_pkg, boot_pkg)
+ common_features += 'IMAGE_FEATURES += "ssh-server-openssh"\n'
+ for init_manager in ("sysvinit", "systemd"):
+ #for sysvinit no extra configuration is needed,
+ features = ''
+ if (init_manager is "systemd"):
+ features += 'DISTRO_FEATURES_append = " systemd"\n'
+ features += 'VIRTUAL-RUNTIME_init_manager = "systemd"\n'
+ features += 'DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"\n'
+ features += 'VIRTUAL-RUNTIME_initscripts = ""\n'
+ for classes in ("package_rpm package_deb package_ipk",
+ "package_deb package_rpm package_ipk",
+ "package_ipk package_deb package_rpm"):
+ features += 'PACKAGE_CLASSES = "%s"\n' % classes
+ self.write_config(common_features + features)
+
+ #Step 2
+ bitbake('core-image-minimal')
+
+ #Step 3
+ file_rootfs_created = os.path.join(get_bb_var('IMAGE_ROOTFS',"core-image-minimal"),
+ file_rootfs_name)
+ found = os.path.isfile(file_rootfs_created)
+ self.assertTrue(found, "File %s was not created at rootfs time by %s" % \
+ (file_rootfs_name, rootfs_pkg))
+
+ #Step 4
+ testcommand = 'ls /etc/'+fileboot_name
+ with runqemu('core-image-minimal') as qemu:
+ sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
+ result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand))
+ self.assertEqual(result.status, 0, 'File %s was not created at firts boot'% fileboot_name)
diff --git a/meta/lib/oeqa/selftest/cases/signing.py b/meta/lib/oeqa/selftest/cases/signing.py
new file mode 100644
index 0000000000..edb5f653f2
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/signing.py
@@ -0,0 +1,184 @@
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
+import os
+import glob
+import re
+import shutil
+import tempfile
+from oeqa.core.decorator.oeid import OETestID
+from oeqa.utils.ftools import write_file
+
+
+class Signing(OESelftestTestCase):
+
+ gpg_dir = ""
+ pub_key_path = ""
+ secret_key_path = ""
+
+ @classmethod
+ def setUpClass(cls):
+ super(Signing, cls).setUpClass()
+ # 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")
+
+ cls.gpg_home_dir = tempfile.TemporaryDirectory(prefix="oeqa-signing-")
+ cls.gpg_dir = cls.gpg_home_dir.name
+
+ 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")
+
+ runCmd('gpg --batch --homedir %s --import %s %s' % (cls.gpg_dir, cls.pub_key_path, cls.secret_key_path))
+
+ @OETestID(1362)
+ def test_signing_packages(self):
+ """
+ Summary: Test that packages can be signed in the package feed
+ Expected: Package should be signed with the correct key
+ Expected: Images can be created from signed packages
+ Product: oe-core
+ Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ Author: Alexander Kanavin <alexander.kanavin@intel.com>
+ AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ """
+ import oe.packagedata
+
+ package_classes = get_bb_var('PACKAGE_CLASSES')
+ if 'package_rpm' not in package_classes:
+ self.skipTest('This test requires RPM Packaging.')
+
+ test_recipe = 'ed'
+
+ feature = 'INHERIT += "sign_rpm"\n'
+ feature += 'RPM_GPG_PASSPHRASE = "test123"\n'
+ feature += 'RPM_GPG_NAME = "testuser"\n'
+ feature += 'GPG_PATH = "%s"\n' % self.gpg_dir
+
+ self.write_config(feature)
+
+ bitbake('-c clean %s' % test_recipe)
+ bitbake('-f -c package_write_rpm %s' % test_recipe)
+
+ self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
+
+ needed_vars = ['PKGDATA_DIR', 'DEPLOY_DIR_RPM', 'PACKAGE_ARCH', 'STAGING_BINDIR_NATIVE']
+ bb_vars = get_bb_vars(needed_vars, test_recipe)
+ pkgdatadir = bb_vars['PKGDATA_DIR']
+ pkgdata = oe.packagedata.read_pkgdatafile(pkgdatadir + "/runtime/ed")
+ if 'PKGE' in pkgdata:
+ pf = pkgdata['PN'] + "-" + pkgdata['PKGE'] + pkgdata['PKGV'] + '-' + pkgdata['PKGR']
+ else:
+ pf = pkgdata['PN'] + "-" + pkgdata['PKGV'] + '-' + pkgdata['PKGR']
+ deploy_dir_rpm = bb_vars['DEPLOY_DIR_RPM']
+ package_arch = bb_vars['PACKAGE_ARCH'].replace('-', '_')
+ staging_bindir_native = bb_vars['STAGING_BINDIR_NATIVE']
+
+ pkg_deploy = os.path.join(deploy_dir_rpm, package_arch, '.'.join((pf, package_arch, 'rpm')))
+
+ # Use a temporary rpmdb
+ rpmdb = tempfile.mkdtemp(prefix='oeqa-rpmdb')
+
+ runCmd('%s/rpmkeys --define "_dbpath %s" --import %s' %
+ (staging_bindir_native, rpmdb, self.pub_key_path))
+
+ ret = runCmd('%s/rpmkeys --define "_dbpath %s" --checksig %s' %
+ (staging_bindir_native, rpmdb, pkg_deploy))
+ # tmp/deploy/rpm/i586/ed-1.9-r0.i586.rpm: rsa sha1 md5 OK
+ self.assertIn('rsa sha1 (md5) pgp md5 OK', ret.output, 'Package signed incorrectly.')
+ shutil.rmtree(rpmdb)
+
+ #Check that an image can be built from signed packages
+ self.add_command_to_tearDown('bitbake -c clean core-image-minimal')
+ bitbake('-c clean core-image-minimal')
+ bitbake('core-image-minimal')
+
+
+ @OETestID(1382)
+ def test_signing_sstate_archive(self):
+ """
+ Summary: Test that sstate archives can be signed
+ Expected: Package should be signed with the correct key
+ Product: oe-core
+ Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ """
+
+ test_recipe = 'ed'
+
+ builddir = os.environ.get('BUILDDIR')
+ sstatedir = os.path.join(builddir, 'test-sstate')
+
+ self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
+ self.add_command_to_tearDown('rm -rf %s' % sstatedir)
+
+ feature = 'SSTATE_SIG_KEY ?= "testuser"\n'
+ feature += 'SSTATE_SIG_PASSPHRASE ?= "test123"\n'
+ feature += 'SSTATE_VERIFY_SIG ?= "1"\n'
+ feature += 'GPG_PATH = "%s"\n' % self.gpg_dir
+ feature += 'SSTATE_DIR = "%s"\n' % sstatedir
+ # Any mirror might have partial sstate without .sig files, triggering failures
+ feature += 'SSTATE_MIRRORS_forcevariable = ""\n'
+
+ self.write_config(feature)
+
+ 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')
+
+ 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) <testuser@email.com>"
+ self.assertIn('gpg: Good signature from', ret.output, 'Package signed incorrectly.')
+
+
+class LockedSignatures(OESelftestTestCase):
+
+ @OETestID(1420)
+ def test_locked_signatures(self):
+ """
+ Summary: Test locked signature mechanism
+ Expected: Locked signatures will prevent task to run
+ Product: oe-core
+ Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
+ """
+
+ test_recipe = 'ed'
+ locked_sigs_file = 'locked-sigs.inc'
+
+ self.add_command_to_tearDown('rm -f %s' % os.path.join(self.builddir, locked_sigs_file))
+
+ bitbake(test_recipe)
+ # Generate locked sigs include file
+ bitbake('-S none %s' % test_recipe)
+
+ feature = 'require %s\n' % locked_sigs_file
+ feature += 'SIGGEN_LOCKEDSIGS_TASKSIG_CHECK = "warn"\n'
+ self.write_config(feature)
+
+ # Build a locked recipe
+ bitbake(test_recipe)
+
+ # Make a change that should cause the locked task signature to change
+ recipe_append_file = test_recipe + '_' + get_bb_var('PV', test_recipe) + '.bbappend'
+ recipe_append_path = os.path.join(self.testlayer_path, 'recipes-test', test_recipe, recipe_append_file)
+ feature = 'SUMMARY += "test locked signature"\n'
+
+ os.mkdir(os.path.join(self.testlayer_path, 'recipes-test', test_recipe))
+ write_file(recipe_append_path, feature)
+
+ self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, 'recipes-test', test_recipe))
+
+ # Build the recipe again
+ ret = bitbake(test_recipe)
+
+ # Verify you get the warning and that the real task *isn't* run (i.e. the locked signature has worked)
+ patt = r'WARNING: The %s:do_package sig is computed to be \S+, but the sig is locked to \S+ in SIGGEN_LOCKEDSIGS\S+' % test_recipe
+ found_warn = re.search(patt, ret.output)
+
+ self.assertIsNotNone(found_warn, "Didn't find the expected warning message. Output: %s" % ret.output)
diff --git a/meta/lib/oeqa/selftest/cases/sstate.py b/meta/lib/oeqa/selftest/cases/sstate.py
new file mode 100644
index 0000000000..bc2fdbd8cc
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/sstate.py
@@ -0,0 +1,63 @@
+import datetime
+import unittest
+import os
+import re
+import shutil
+
+import oeqa.utils.ftools as ftools
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_vars, get_test_layer
+
+
+class SStateBase(OESelftestTestCase):
+
+ def setUpLocal(self):
+ super(SStateBase, self).setUpLocal()
+ self.temp_sstate_location = None
+ needed_vars = ['SSTATE_DIR', 'NATIVELSBSTRING', 'TCLIBC', 'TUNE_ARCH',
+ 'TOPDIR', 'TARGET_VENDOR', 'TARGET_OS']
+ bb_vars = get_bb_vars(needed_vars)
+ self.sstate_path = bb_vars['SSTATE_DIR']
+ self.hostdistro = bb_vars['NATIVELSBSTRING']
+ self.tclibc = bb_vars['TCLIBC']
+ self.tune_arch = bb_vars['TUNE_ARCH']
+ self.topdir = bb_vars['TOPDIR']
+ self.target_vendor = bb_vars['TARGET_VENDOR']
+ self.target_os = bb_vars['TARGET_OS']
+ self.distro_specific_sstate = os.path.join(self.sstate_path, self.hostdistro)
+
+ # Creates a special sstate configuration with the option to add sstate mirrors
+ def config_sstate(self, temp_sstate_location=False, add_local_mirrors=[]):
+ self.temp_sstate_location = temp_sstate_location
+
+ if self.temp_sstate_location:
+ temp_sstate_path = os.path.join(self.builddir, "temp_sstate_%s" % datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
+ config_temp_sstate = "SSTATE_DIR = \"%s\"" % temp_sstate_path
+ self.append_config(config_temp_sstate)
+ self.track_for_cleanup(temp_sstate_path)
+ bb_vars = get_bb_vars(['SSTATE_DIR', 'NATIVELSBSTRING'])
+ self.sstate_path = bb_vars['SSTATE_DIR']
+ self.hostdistro = bb_vars['NATIVELSBSTRING']
+ self.distro_specific_sstate = os.path.join(self.sstate_path, self.hostdistro)
+
+ if add_local_mirrors:
+ config_set_sstate_if_not_set = 'SSTATE_MIRRORS ?= ""'
+ self.append_config(config_set_sstate_if_not_set)
+ for local_mirror in add_local_mirrors:
+ self.assertFalse(os.path.join(local_mirror) == os.path.join(self.sstate_path), msg='Cannot add the current sstate path as a sstate mirror')
+ config_sstate_mirror = "SSTATE_MIRRORS += \"file://.* file:///%s/PATH\"" % local_mirror
+ self.append_config(config_sstate_mirror)
+
+ # Returns a list containing sstate files
+ def search_sstate(self, filename_regex, distro_specific=True, distro_nonspecific=True):
+ result = []
+ for root, dirs, files in os.walk(self.sstate_path):
+ if distro_specific and re.search("%s/[a-z0-9]{2}$" % self.hostdistro, root):
+ for f in files:
+ if re.search(filename_regex, f):
+ result.append(f)
+ if distro_nonspecific and re.search("%s/[a-z0-9]{2}$" % self.sstate_path, root):
+ for f in files:
+ if re.search(filename_regex, f):
+ result.append(f)
+ return result
diff --git a/meta/lib/oeqa/selftest/cases/sstatetests.py b/meta/lib/oeqa/selftest/cases/sstatetests.py
new file mode 100644
index 0000000000..8d05e30423
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/sstatetests.py
@@ -0,0 +1,479 @@
+import os
+import shutil
+import glob
+import subprocess
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer
+from oeqa.selftest.cases.sstate import SStateBase
+from oeqa.core.decorator.oeid import OETestID
+
+class SStateTests(SStateBase):
+
+ # Test sstate files creation and their location
+ def run_test_sstate_creation(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True, should_pass=True):
+ self.config_sstate(temp_sstate_location, [self.sstate_path])
+
+ if self.temp_sstate_location:
+ bitbake(['-cclean'] + targets)
+ else:
+ bitbake(['-ccleansstate'] + targets)
+
+ bitbake(targets)
+ file_tracker = []
+ results = self.search_sstate('|'.join(map(str, targets)), distro_specific, distro_nonspecific)
+ if distro_nonspecific:
+ for r in results:
+ if r.endswith(("_populate_lic.tgz", "_populate_lic.tgz.siginfo", "_fetch.tgz.siginfo", "_unpack.tgz.siginfo", "_patch.tgz.siginfo")):
+ continue
+ file_tracker.append(r)
+ else:
+ file_tracker = results
+
+ if should_pass:
+ self.assertTrue(file_tracker , msg="Could not find sstate files for: %s" % ', '.join(map(str, targets)))
+ else:
+ self.assertTrue(not file_tracker , msg="Found sstate files in the wrong place for: %s (found %s)" % (', '.join(map(str, targets)), str(file_tracker)))
+
+ @OETestID(975)
+ def test_sstate_creation_distro_specific_pass(self):
+ self.run_test_sstate_creation(['binutils-cross-'+ self.tune_arch, 'binutils-native'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
+
+ @OETestID(1374)
+ def test_sstate_creation_distro_specific_fail(self):
+ self.run_test_sstate_creation(['binutils-cross-'+ self.tune_arch, 'binutils-native'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True, should_pass=False)
+
+ @OETestID(976)
+ def test_sstate_creation_distro_nonspecific_pass(self):
+ self.run_test_sstate_creation(['linux-libc-headers'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
+
+ @OETestID(1375)
+ def test_sstate_creation_distro_nonspecific_fail(self):
+ self.run_test_sstate_creation(['linux-libc-headers'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True, should_pass=False)
+
+ # Test the sstate files deletion part of the do_cleansstate task
+ def run_test_cleansstate_task(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True):
+ self.config_sstate(temp_sstate_location, [self.sstate_path])
+
+ bitbake(['-ccleansstate'] + targets)
+
+ bitbake(targets)
+ tgz_created = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific)
+ self.assertTrue(tgz_created, msg="Could not find sstate .tgz files for: %s (%s)" % (', '.join(map(str, targets)), str(tgz_created)))
+
+ siginfo_created = self.search_sstate('|'.join(map(str, [s + '.*?\.siginfo$' for s in targets])), distro_specific, distro_nonspecific)
+ self.assertTrue(siginfo_created, msg="Could not find sstate .siginfo files for: %s (%s)" % (', '.join(map(str, targets)), str(siginfo_created)))
+
+ bitbake(['-ccleansstate'] + targets)
+ tgz_removed = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific)
+ self.assertTrue(not tgz_removed, msg="do_cleansstate didn't remove .tgz sstate files for: %s (%s)" % (', '.join(map(str, targets)), str(tgz_removed)))
+
+ @OETestID(977)
+ def test_cleansstate_task_distro_specific_nonspecific(self):
+ targets = ['binutils-cross-'+ self.tune_arch, 'binutils-native']
+ targets.append('linux-libc-headers')
+ self.run_test_cleansstate_task(targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True)
+
+ @OETestID(1376)
+ def test_cleansstate_task_distro_nonspecific(self):
+ self.run_test_cleansstate_task(['linux-libc-headers'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
+
+ @OETestID(1377)
+ def test_cleansstate_task_distro_specific(self):
+ targets = ['binutils-cross-'+ self.tune_arch, 'binutils-native']
+ targets.append('linux-libc-headers')
+ self.run_test_cleansstate_task(targets, distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
+
+
+ # Test rebuilding of distro-specific sstate files
+ def run_test_rebuild_distro_specific_sstate(self, targets, temp_sstate_location=True):
+ self.config_sstate(temp_sstate_location, [self.sstate_path])
+
+ bitbake(['-ccleansstate'] + targets)
+
+ bitbake(targets)
+ results = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=False, distro_nonspecific=True)
+ filtered_results = []
+ for r in results:
+ if r.endswith(("_populate_lic.tgz", "_populate_lic.tgz.siginfo")):
+ continue
+ filtered_results.append(r)
+ self.assertTrue(filtered_results == [], msg="Found distro non-specific sstate for: %s (%s)" % (', '.join(map(str, targets)), str(filtered_results)))
+ file_tracker_1 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False)
+ self.assertTrue(len(file_tracker_1) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets)))
+
+ self.track_for_cleanup(self.distro_specific_sstate + "_old")
+ shutil.copytree(self.distro_specific_sstate, self.distro_specific_sstate + "_old")
+ shutil.rmtree(self.distro_specific_sstate)
+
+ bitbake(['-cclean'] + targets)
+ bitbake(targets)
+ file_tracker_2 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False)
+ self.assertTrue(len(file_tracker_2) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets)))
+
+ not_recreated = [x for x in file_tracker_1 if x not in file_tracker_2]
+ self.assertTrue(not_recreated == [], msg="The following sstate files ware not recreated: %s" % ', '.join(map(str, not_recreated)))
+
+ created_once = [x for x in file_tracker_2 if x not in file_tracker_1]
+ self.assertTrue(created_once == [], msg="The following sstate files ware created only in the second run: %s" % ', '.join(map(str, created_once)))
+
+ @OETestID(175)
+ def test_rebuild_distro_specific_sstate_cross_native_targets(self):
+ self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + self.tune_arch, 'binutils-native'], temp_sstate_location=True)
+
+ @OETestID(1372)
+ def test_rebuild_distro_specific_sstate_cross_target(self):
+ self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + self.tune_arch], temp_sstate_location=True)
+
+ @OETestID(1373)
+ def test_rebuild_distro_specific_sstate_native_target(self):
+ self.run_test_rebuild_distro_specific_sstate(['binutils-native'], temp_sstate_location=True)
+
+
+ # Test the sstate-cache-management script. Each element in the global_config list is used with the corresponding element in the target_config list
+ # global_config elements are expected to not generate any sstate files that would be removed by sstate-cache-management.sh (such as changing the value of MACHINE)
+ def run_test_sstate_cache_management_script(self, target, global_config=[''], target_config=[''], ignore_patterns=[]):
+ self.assertTrue(global_config)
+ self.assertTrue(target_config)
+ self.assertTrue(len(global_config) == len(target_config), msg='Lists global_config and target_config should have the same number of elements')
+ self.config_sstate(temp_sstate_location=True, add_local_mirrors=[self.sstate_path])
+
+ # If buildhistory is enabled, we need to disable version-going-backwards
+ # QA checks for this test. It may report errors otherwise.
+ self.append_config('ERROR_QA_remove = "version-going-backwards"')
+
+ # For not this only checks if random sstate tasks are handled correctly as a group.
+ # In the future we should add control over what tasks we check for.
+
+ sstate_archs_list = []
+ expected_remaining_sstate = []
+ for idx in range(len(target_config)):
+ self.append_config(global_config[idx])
+ self.append_recipeinc(target, target_config[idx])
+ sstate_arch = get_bb_var('SSTATE_PKGARCH', target)
+ if not sstate_arch in sstate_archs_list:
+ sstate_archs_list.append(sstate_arch)
+ if target_config[idx] == target_config[-1]:
+ target_sstate_before_build = self.search_sstate(target + '.*?\.tgz$')
+ bitbake("-cclean %s" % target)
+ result = bitbake(target, ignore_status=True)
+ if target_config[idx] == target_config[-1]:
+ target_sstate_after_build = self.search_sstate(target + '.*?\.tgz$')
+ expected_remaining_sstate += [x for x in target_sstate_after_build if x not in target_sstate_before_build if not any(pattern in x for pattern in ignore_patterns)]
+ self.remove_config(global_config[idx])
+ self.remove_recipeinc(target, target_config[idx])
+ self.assertEqual(result.status, 0, msg = "build of %s failed with %s" % (target, result.output))
+
+ runCmd("sstate-cache-management.sh -y --cache-dir=%s --remove-duplicated --extra-archs=%s" % (self.sstate_path, ','.join(map(str, sstate_archs_list))))
+ actual_remaining_sstate = [x for x in self.search_sstate(target + '.*?\.tgz$') if not any(pattern in x for pattern in ignore_patterns)]
+
+ actual_not_expected = [x for x in actual_remaining_sstate if x not in expected_remaining_sstate]
+ self.assertFalse(actual_not_expected, msg="Files should have been removed but ware not: %s" % ', '.join(map(str, actual_not_expected)))
+ expected_not_actual = [x for x in expected_remaining_sstate if x not in actual_remaining_sstate]
+ self.assertFalse(expected_not_actual, msg="Extra files ware removed: %s" ', '.join(map(str, expected_not_actual)))
+
+ @OETestID(973)
+ def test_sstate_cache_management_script_using_pr_1(self):
+ global_config = []
+ target_config = []
+ global_config.append('')
+ target_config.append('PR = "0"')
+ self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
+
+ @OETestID(978)
+ def test_sstate_cache_management_script_using_pr_2(self):
+ global_config = []
+ target_config = []
+ global_config.append('')
+ target_config.append('PR = "0"')
+ global_config.append('')
+ target_config.append('PR = "1"')
+ self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
+
+ @OETestID(979)
+ def test_sstate_cache_management_script_using_pr_3(self):
+ global_config = []
+ target_config = []
+ global_config.append('MACHINE = "qemux86-64"')
+ target_config.append('PR = "0"')
+ global_config.append(global_config[0])
+ target_config.append('PR = "1"')
+ global_config.append('MACHINE = "qemux86"')
+ target_config.append('PR = "1"')
+ self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
+
+ @OETestID(974)
+ def test_sstate_cache_management_script_using_machine(self):
+ global_config = []
+ target_config = []
+ global_config.append('MACHINE = "qemux86-64"')
+ target_config.append('')
+ global_config.append('MACHINE = "qemux86"')
+ target_config.append('')
+ self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
+
+ @OETestID(1270)
+ def test_sstate_32_64_same_hash(self):
+ """
+ The sstate checksums for both native and target should not vary whether
+ they're built on a 32 or 64 bit system. Rather than requiring two different
+ build machines and running a builds, override the variables calling uname()
+ manually and check using bitbake -S.
+ """
+
+ self.write_config("""
+MACHINE = "qemux86"
+TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
+BUILD_ARCH = "x86_64"
+BUILD_OS = "linux"
+SDKMACHINE = "x86_64"
+PACKAGE_CLASSES = "package_rpm package_ipk package_deb"
+""")
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
+ bitbake("core-image-sato -S none")
+ self.write_config("""
+MACHINE = "qemux86"
+TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
+BUILD_ARCH = "i686"
+BUILD_OS = "linux"
+SDKMACHINE = "i686"
+PACKAGE_CLASSES = "package_rpm package_ipk package_deb"
+""")
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
+ bitbake("core-image-sato -S none")
+
+ def get_files(d):
+ f = []
+ for root, dirs, files in os.walk(d):
+ if "core-image-sato" in root:
+ # SDKMACHINE changing will change
+ # do_rootfs/do_testimage/do_build stamps of images which
+ # is safe to ignore.
+ continue
+ f.extend(os.path.join(root, name) for name in files)
+ return f
+ files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/")
+ files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/")
+ files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash").replace("i686-linux", "x86_64-linux").replace("i686" + self.target_vendor + "-linux", "x86_64" + self.target_vendor + "-linux", ) for x in files2]
+ self.maxDiff = None
+ self.assertCountEqual(files1, files2)
+
+
+ @OETestID(1271)
+ def test_sstate_nativelsbstring_same_hash(self):
+ """
+ The sstate checksums should be independent of whichever NATIVELSBSTRING is
+ detected. Rather than requiring two different build machines and running
+ builds, override the variables manually and check using bitbake -S.
+ """
+
+ self.write_config("""
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
+NATIVELSBSTRING = \"DistroA\"
+""")
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
+ bitbake("core-image-sato -S none")
+ self.write_config("""
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
+NATIVELSBSTRING = \"DistroB\"
+""")
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
+ bitbake("core-image-sato -S none")
+
+ def get_files(d):
+ f = []
+ for root, dirs, files in os.walk(d):
+ f.extend(os.path.join(root, name) for name in files)
+ return f
+ files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/")
+ files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/")
+ files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash") for x in files2]
+ self.maxDiff = None
+ self.assertCountEqual(files1, files2)
+
+ @OETestID(1368)
+ def test_sstate_allarch_samesigs(self):
+ """
+ The sstate checksums of allarch packages should be independent of whichever
+ MACHINE is set. Check this using bitbake -S.
+ Also, rather than duplicate the test, check nativesdk stamps are the same between
+ the two MACHINE values.
+ """
+
+ configA = """
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
+MACHINE = \"qemux86-64\"
+"""
+ configB = """
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
+MACHINE = \"qemuarm\"
+"""
+ self.sstate_allarch_samesigs(configA, configB)
+
+ def test_sstate_allarch_samesigs_multilib(self):
+ """
+ The sstate checksums of allarch multilib packages should be independent of whichever
+ MACHINE is set. Check this using bitbake -S.
+ Also, rather than duplicate the test, check nativesdk stamps are the same between
+ the two MACHINE values.
+ """
+
+ configA = """
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
+MACHINE = \"qemux86-64\"
+require conf/multilib.conf
+MULTILIBS = \"multilib:lib32\"
+DEFAULTTUNE_virtclass-multilib-lib32 = \"x86\"
+"""
+ configB = """
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
+MACHINE = \"qemuarm\"
+require conf/multilib.conf
+MULTILIBS = \"\"
+"""
+ self.sstate_allarch_samesigs(configA, configB)
+
+ def sstate_allarch_samesigs(self, configA, configB):
+
+ self.write_config(configA)
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
+ bitbake("world meta-toolchain -S none")
+ self.write_config(configB)
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
+ bitbake("world meta-toolchain -S none")
+
+ def get_files(d):
+ f = {}
+ for root, dirs, files in os.walk(d):
+ for name in files:
+ if "meta-environment" in root or "cross-canadian" in root:
+ continue
+ if "do_build" not in name:
+ # 1.4.1+gitAUTOINC+302fca9f4c-r0.do_package_write_ipk.sigdata.f3a2a38697da743f0dbed8b56aafcf79
+ (_, task, _, shash) = name.rsplit(".", 3)
+ f[os.path.join(os.path.basename(root), task)] = shash
+ return f
+ files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/all" + self.target_vendor + "-" + self.target_os)
+ files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/all" + self.target_vendor + "-" + self.target_os)
+ self.maxDiff = None
+ self.assertEqual(files1, files2)
+
+ nativesdkdir = os.path.basename(glob.glob(self.topdir + "/tmp-sstatesamehash/stamps/*-nativesdk*-linux")[0])
+
+ files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/" + nativesdkdir)
+ files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/" + nativesdkdir)
+ self.maxDiff = None
+ self.assertEqual(files1, files2)
+
+ @OETestID(1369)
+ def test_sstate_sametune_samesigs(self):
+ """
+ The sstate checksums of two identical machines (using the same tune) should be the
+ same, apart from changes within the machine specific stamps directory. We use the
+ qemux86copy machine to test this. Also include multilibs in the test.
+ """
+
+ self.write_config("""
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
+MACHINE = \"qemux86\"
+require conf/multilib.conf
+MULTILIBS = "multilib:lib32"
+DEFAULTTUNE_virtclass-multilib-lib32 = "x86"
+""")
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
+ bitbake("world meta-toolchain -S none")
+ self.write_config("""
+TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
+MACHINE = \"qemux86copy\"
+require conf/multilib.conf
+MULTILIBS = "multilib:lib32"
+DEFAULTTUNE_virtclass-multilib-lib32 = "x86"
+""")
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
+ bitbake("world meta-toolchain -S none")
+
+ def get_files(d):
+ f = []
+ for root, dirs, files in os.walk(d):
+ for name in files:
+ if "meta-environment" in root or "cross-canadian" in root:
+ continue
+ if "qemux86copy-" in root or "qemux86-" in root:
+ continue
+ if "do_build" not in name and "do_populate_sdk" not in name:
+ f.append(os.path.join(root, name))
+ return f
+ files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps")
+ files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps")
+ files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash") for x in files2]
+ self.maxDiff = None
+ self.assertCountEqual(files1, files2)
+
+
+ def test_sstate_noop_samesigs(self):
+ """
+ The sstate checksums of two builds with these variables changed or
+ classes inherits should be the same.
+ """
+
+ self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
+BB_NUMBER_THREADS = "1"
+PARALLEL_MAKE = "-j 1"
+DL_DIR = "${TOPDIR}/download1"
+TIME = "111111"
+DATE = "20161111"
+INHERIT_remove = "buildstats-summary buildhistory uninative"
+http_proxy = ""
+""")
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
+ self.track_for_cleanup(self.topdir + "/download1")
+ bitbake("world meta-toolchain -S none")
+ self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
+BB_NUMBER_THREADS = "2"
+PARALLEL_MAKE = "-j 2"
+DL_DIR = "${TOPDIR}/download2"
+TIME = "222222"
+DATE = "20161212"
+# Always remove uninative as we're changing proxies
+INHERIT_remove = "uninative"
+INHERIT += "buildstats-summary buildhistory"
+http_proxy = "http://example.com/"
+""")
+ self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
+ self.track_for_cleanup(self.topdir + "/download2")
+ bitbake("world meta-toolchain -S none")
+
+ def get_files(d):
+ f = {}
+ for root, dirs, files in os.walk(d):
+ for name in files:
+ name, shash = name.rsplit('.', 1)
+ # Extract just the machine and recipe name
+ base = os.sep.join(root.rsplit(os.sep, 2)[-2:] + [name])
+ f[base] = shash
+ return f
+ files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/")
+ files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/")
+ # Remove items that are identical in both sets
+ for k,v in files1.items() & files2.items():
+ del files1[k]
+ del files2[k]
+ if not files1 and not files2:
+ # No changes, so we're done
+ return
+
+ for k in files1.keys() | files2.keys():
+ if k in files1 and k in files2:
+ print("%s differs:" % k)
+ print(subprocess.check_output(("bitbake-diffsigs",
+ self.topdir + "/tmp-sstatesamehash/stamps/" + k + "." + files1[k],
+ self.topdir + "/tmp-sstatesamehash2/stamps/" + k + "." + files2[k])))
+ elif k in files1 and k not in files2:
+ print("%s in files1" % k)
+ elif k not in files1 and k in files2:
+ print("%s in files2" % k)
+ else:
+ assert "shouldn't reach here"
+ self.fail("sstate hashes not identical.")
diff --git a/meta/lib/oeqa/selftest/cases/tinfoil.py b/meta/lib/oeqa/selftest/cases/tinfoil.py
new file mode 100644
index 0000000000..1394d426e7
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/tinfoil.py
@@ -0,0 +1,189 @@
+import os
+import re
+import bb.tinfoil
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd
+from oeqa.core.decorator.oeid import OETestID
+
+class TinfoilTests(OESelftestTestCase):
+ """ Basic tests for the tinfoil API """
+
+ @OETestID(1568)
+ def test_getvar(self):
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(True)
+ machine = tinfoil.config_data.getVar('MACHINE')
+ if not machine:
+ self.fail('Unable to get MACHINE value - returned %s' % machine)
+
+ @OETestID(1569)
+ def test_expand(self):
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(True)
+ expr = '${@os.getpid()}'
+ pid = tinfoil.config_data.expand(expr)
+ if not pid:
+ self.fail('Unable to expand "%s" - returned %s' % (expr, pid))
+
+ @OETestID(1570)
+ def test_getvar_bb_origenv(self):
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(True)
+ origenv = tinfoil.config_data.getVar('BB_ORIGENV', False)
+ if not origenv:
+ self.fail('Unable to get BB_ORIGENV value - returned %s' % origenv)
+ self.assertEqual(origenv.getVar('HOME', False), os.environ['HOME'])
+
+ @OETestID(1571)
+ def test_parse_recipe(self):
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(config_only=False, quiet=2)
+ testrecipe = 'mdadm'
+ best = tinfoil.find_best_provider(testrecipe)
+ if not best:
+ self.fail('Unable to find recipe providing %s' % testrecipe)
+ rd = tinfoil.parse_recipe_file(best[3])
+ self.assertEqual(testrecipe, rd.getVar('PN'))
+
+ @OETestID(1572)
+ def test_parse_recipe_copy_expand(self):
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(config_only=False, quiet=2)
+ testrecipe = 'mdadm'
+ best = tinfoil.find_best_provider(testrecipe)
+ if not best:
+ self.fail('Unable to find recipe providing %s' % testrecipe)
+ rd = tinfoil.parse_recipe_file(best[3])
+ # Check we can get variable values
+ self.assertEqual(testrecipe, rd.getVar('PN'))
+ # Check that expanding a value that includes a variable reference works
+ self.assertEqual(testrecipe, rd.getVar('BPN'))
+ # Now check that changing the referenced variable's value in a copy gives that
+ # value when expanding
+ localdata = bb.data.createCopy(rd)
+ localdata.setVar('PN', 'hello')
+ self.assertEqual('hello', localdata.getVar('BPN'))
+
+ @OETestID(1573)
+ def test_parse_recipe_initial_datastore(self):
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(config_only=False, quiet=2)
+ testrecipe = 'mdadm'
+ best = tinfoil.find_best_provider(testrecipe)
+ if not best:
+ self.fail('Unable to find recipe providing %s' % testrecipe)
+ dcopy = bb.data.createCopy(tinfoil.config_data)
+ dcopy.setVar('MYVARIABLE', 'somevalue')
+ rd = tinfoil.parse_recipe_file(best[3], config_data=dcopy)
+ # Check we can get variable values
+ self.assertEqual('somevalue', rd.getVar('MYVARIABLE'))
+
+ @OETestID(1574)
+ def test_list_recipes(self):
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(config_only=False, quiet=2)
+ # Check pkg_pn
+ checkpns = ['tar', 'automake', 'coreutils', 'm4-native', 'nativesdk-gcc']
+ pkg_pn = tinfoil.cooker.recipecaches[''].pkg_pn
+ for pn in checkpns:
+ self.assertIn(pn, pkg_pn)
+ # Check pkg_fn
+ checkfns = {'nativesdk-gcc': '^virtual:nativesdk:.*', 'coreutils': '.*/coreutils_.*.bb'}
+ for fn, pn in tinfoil.cooker.recipecaches[''].pkg_fn.items():
+ if pn in checkpns:
+ if pn in checkfns:
+ self.assertTrue(re.match(checkfns[pn], fn), 'Entry for %s: %s did not match %s' % (pn, fn, checkfns[pn]))
+ checkpns.remove(pn)
+ if checkpns:
+ self.fail('Unable to find pkg_fn entries for: %s' % ', '.join(checkpns))
+
+ @OETestID(1575)
+ def test_wait_event(self):
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(config_only=True)
+ # Need to drain events otherwise events that will be masked will still be in the queue
+ while tinfoil.wait_event(0.25):
+ pass
+ tinfoil.set_event_mask(['bb.event.FilesMatchingFound', 'bb.command.CommandCompleted'])
+ pattern = 'conf'
+ res = tinfoil.run_command('findFilesMatchingInDir', pattern, 'conf/machine')
+ self.assertTrue(res)
+
+ eventreceived = False
+ waitcount = 5
+ while waitcount > 0:
+ event = tinfoil.wait_event(1)
+ if event:
+ if isinstance(event, bb.command.CommandCompleted):
+ break
+ elif isinstance(event, bb.event.FilesMatchingFound):
+ self.assertEqual(pattern, event._pattern)
+ self.assertIn('qemuarm.conf', event._matches)
+ eventreceived = True
+ else:
+ self.fail('Unexpected event: %s' % event)
+
+ waitcount = waitcount - 1
+
+ self.assertNotEqual(waitcount, 0, 'Timed out waiting for CommandCompleted event from bitbake server')
+ self.assertTrue(eventreceived, 'Did not receive FilesMatchingFound event from bitbake server')
+
+ @OETestID(1576)
+ def test_setvariable_clean(self):
+ # First check that setVariable affects the datastore
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(config_only=True)
+ tinfoil.run_command('setVariable', 'TESTVAR', 'specialvalue')
+ self.assertEqual(tinfoil.config_data.getVar('TESTVAR'), 'specialvalue', 'Value set using setVariable is not reflected in client-side getVar()')
+
+ # Now check that the setVariable's effects are no longer present
+ # (this may legitimately break in future if we stop reinitialising
+ # the datastore, in which case we'll have to reconsider use of
+ # setVariable entirely)
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(config_only=True)
+ self.assertNotEqual(tinfoil.config_data.getVar('TESTVAR'), 'specialvalue', 'Value set using setVariable is still present!')
+
+ # Now check that setVar on the main datastore works (uses setVariable internally)
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(config_only=True)
+ tinfoil.config_data.setVar('TESTVAR', 'specialvalue')
+ value = tinfoil.run_command('getVariable', 'TESTVAR')
+ self.assertEqual(value, 'specialvalue', 'Value set using config_data.setVar() is not reflected in config_data.getVar()')
+
+ def test_datastore_operations(self):
+ with bb.tinfoil.Tinfoil() as tinfoil:
+ tinfoil.prepare(config_only=True)
+ # Test setVarFlag() / getVarFlag()
+ tinfoil.config_data.setVarFlag('TESTVAR', 'flagname', 'flagval')
+ value = tinfoil.config_data.getVarFlag('TESTVAR', 'flagname')
+ self.assertEqual(value, 'flagval', 'Value set using config_data.setVarFlag() is not reflected in config_data.getVarFlag()')
+ # Test delVarFlag()
+ tinfoil.config_data.setVarFlag('TESTVAR', 'otherflag', 'othervalue')
+ tinfoil.config_data.delVarFlag('TESTVAR', 'flagname')
+ value = tinfoil.config_data.getVarFlag('TESTVAR', 'flagname')
+ self.assertEqual(value, None, 'Varflag deleted using config_data.delVarFlag() is not reflected in config_data.getVarFlag()')
+ value = tinfoil.config_data.getVarFlag('TESTVAR', 'otherflag')
+ self.assertEqual(value, 'othervalue', 'Varflag deleted using config_data.delVarFlag() caused unrelated flag to be removed')
+ # Test delVar()
+ tinfoil.config_data.setVar('TESTVAR', 'varvalue')
+ value = tinfoil.config_data.getVar('TESTVAR')
+ self.assertEqual(value, 'varvalue', 'Value set using config_data.setVar() is not reflected in config_data.getVar()')
+ tinfoil.config_data.delVar('TESTVAR')
+ value = tinfoil.config_data.getVar('TESTVAR')
+ self.assertEqual(value, None, 'Variable deleted using config_data.delVar() appears to still have a value')
+ # Test renameVar()
+ tinfoil.config_data.setVar('TESTVAROLD', 'origvalue')
+ tinfoil.config_data.renameVar('TESTVAROLD', 'TESTVARNEW')
+ value = tinfoil.config_data.getVar('TESTVAROLD')
+ self.assertEqual(value, None, 'Variable renamed using config_data.renameVar() still seems to exist')
+ value = tinfoil.config_data.getVar('TESTVARNEW')
+ self.assertEqual(value, 'origvalue', 'Variable renamed using config_data.renameVar() does not appear with new name')
+ # Test overrides
+ tinfoil.config_data.setVar('TESTVAR', 'original')
+ tinfoil.config_data.setVar('TESTVAR_overrideone', 'one')
+ tinfoil.config_data.setVar('TESTVAR_overridetwo', 'two')
+ tinfoil.config_data.appendVar('OVERRIDES', ':overrideone')
+ value = tinfoil.config_data.getVar('TESTVAR')
+ self.assertEqual(value, 'one', 'Variable overrides not functioning correctly')
diff --git a/meta/lib/oeqa/selftest/cases/wic.py b/meta/lib/oeqa/selftest/cases/wic.py
new file mode 100644
index 0000000000..4040cf7246
--- /dev/null
+++ b/meta/lib/oeqa/selftest/cases/wic.py
@@ -0,0 +1,793 @@
+#!/usr/bin/env python
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# Copyright (c) 2015, Intel Corporation.
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# AUTHORS
+# Ed Bartosh <ed.bartosh@linux.intel.com>
+
+"""Test cases for wic."""
+
+import os
+import sys
+import unittest
+
+from glob import glob
+from shutil import rmtree
+from functools import wraps, lru_cache
+from tempfile import NamedTemporaryFile
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
+from oeqa.core.decorator.oeid import OETestID
+
+
+@lru_cache(maxsize=32)
+def get_host_arch(recipe):
+ """A cached call to get_bb_var('HOST_ARCH', <recipe>)"""
+ return get_bb_var('HOST_ARCH', recipe)
+
+
+def only_for_arch(archs, image='core-image-minimal'):
+ """Decorator for wrapping test cases that can be run only for specific target
+ architectures. A list of compatible architectures is passed in `archs`.
+ Current architecture will be determined by parsing bitbake output for
+ `image` recipe.
+ """
+ def wrapper(func):
+ @wraps(func)
+ def wrapped_f(*args, **kwargs):
+ arch = get_host_arch(image)
+ if archs and arch not in archs:
+ raise unittest.SkipTest("Testcase arch dependency not met: %s" % arch)
+ return func(*args, **kwargs)
+ wrapped_f.__name__ = func.__name__
+ return wrapped_f
+ return wrapper
+
+
+class Wic(OESelftestTestCase):
+ """Wic test class."""
+
+ resultdir = "/var/tmp/wic.oe-selftest/"
+ image_is_ready = False
+ native_sysroot = None
+ wicenv_cache = {}
+
+ def setUpLocal(self):
+ """This code is executed before each test method."""
+ super(Wic, self).setUpLocal()
+ if not self.native_sysroot:
+ Wic.native_sysroot = get_bb_var('STAGING_DIR_NATIVE', 'wic-tools')
+
+ # Do this here instead of in setUpClass as the base setUp does some
+ # clean up which can result in the native tools built earlier in
+ # setUpClass being unavailable.
+ if not Wic.image_is_ready:
+ if get_bb_var('USE_NLS') == 'yes':
+ bitbake('wic-tools')
+ else:
+ self.skipTest('wic-tools cannot be built due its (intltool|gettext)-native dependency and NLS disable')
+
+ bitbake('core-image-minimal')
+ Wic.image_is_ready = True
+
+ rmtree(self.resultdir, ignore_errors=True)
+
+ def tearDownLocal(self):
+ """Remove resultdir as it may contain images."""
+ rmtree(self.resultdir, ignore_errors=True)
+ super(Wic, self).tearDownLocal()
+
+ @OETestID(1552)
+ def test_version(self):
+ """Test wic --version"""
+ self.assertEqual(0, runCmd('wic --version').status)
+
+ @OETestID(1208)
+ def test_help(self):
+ """Test wic --help and wic -h"""
+ self.assertEqual(0, runCmd('wic --help').status)
+ self.assertEqual(0, runCmd('wic -h').status)
+
+ @OETestID(1209)
+ def test_createhelp(self):
+ """Test wic create --help"""
+ self.assertEqual(0, runCmd('wic create --help').status)
+
+ @OETestID(1210)
+ def test_listhelp(self):
+ """Test wic list --help"""
+ self.assertEqual(0, runCmd('wic list --help').status)
+
+ @OETestID(1553)
+ def test_help_create(self):
+ """Test wic help create"""
+ self.assertEqual(0, runCmd('wic help create').status)
+
+ @OETestID(1554)
+ def test_help_list(self):
+ """Test wic help list"""
+ self.assertEqual(0, runCmd('wic help list').status)
+
+ @OETestID(1215)
+ def test_help_overview(self):
+ """Test wic help overview"""
+ self.assertEqual(0, runCmd('wic help overview').status)
+
+ @OETestID(1216)
+ def test_help_plugins(self):
+ """Test wic help plugins"""
+ self.assertEqual(0, runCmd('wic help plugins').status)
+
+ @OETestID(1217)
+ def test_help_kickstart(self):
+ """Test wic help kickstart"""
+ self.assertEqual(0, runCmd('wic help kickstart').status)
+
+ @OETestID(1555)
+ def test_list_images(self):
+ """Test wic list images"""
+ self.assertEqual(0, runCmd('wic list images').status)
+
+ @OETestID(1556)
+ def test_list_source_plugins(self):
+ """Test wic list source-plugins"""
+ self.assertEqual(0, runCmd('wic list source-plugins').status)
+
+ @OETestID(1557)
+ def test_listed_images_help(self):
+ """Test wic listed images help"""
+ output = runCmd('wic list images').output
+ imagelist = [line.split()[0] for line in output.splitlines()]
+ for image in imagelist:
+ self.assertEqual(0, runCmd('wic list %s help' % image).status)
+
+ @OETestID(1213)
+ def test_unsupported_subcommand(self):
+ """Test unsupported subcommand"""
+ self.assertNotEqual(0, runCmd('wic unsupported', ignore_status=True).status)
+
+ @OETestID(1214)
+ def test_no_command(self):
+ """Test wic without command"""
+ self.assertEqual(1, runCmd('wic', ignore_status=True).status)
+
+ @OETestID(1211)
+ def test_build_image_name(self):
+ """Test wic create wictestdisk --image-name=core-image-minimal"""
+ cmd = "wic create wictestdisk --image-name=core-image-minimal -o %s" % self.resultdir
+ self.assertEqual(0, runCmd(cmd).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+ @OETestID(1157)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_gpt_image(self):
+ """Test creation of core-image-minimal with gpt table and UUID boot"""
+ cmd = "wic create directdisk-gpt --image-name core-image-minimal -o %s" % self.resultdir
+ self.assertEqual(0, runCmd(cmd).status)
+ self.assertEqual(1, len(glob(self.resultdir + "directdisk-*.direct")))
+
+ @OETestID(1346)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_iso_image(self):
+ """Test creation of hybrid iso image with legacy and EFI boot"""
+ config = 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
+ 'MACHINE_FEATURES_append = " efi"\n'
+ self.append_config(config)
+ bitbake('core-image-minimal')
+ self.remove_config(config)
+ cmd = "wic create mkhybridiso --image-name core-image-minimal -o %s" % self.resultdir
+ self.assertEqual(0, runCmd(cmd).status)
+ self.assertEqual(1, len(glob(self.resultdir + "HYBRID_ISO_IMG-*.direct")))
+ self.assertEqual(1, len(glob(self.resultdir + "HYBRID_ISO_IMG-*.iso")))
+
+ @OETestID(1348)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_qemux86_directdisk(self):
+ """Test creation of qemux-86-directdisk image"""
+ cmd = "wic create qemux86-directdisk -e core-image-minimal -o %s" % self.resultdir
+ self.assertEqual(0, runCmd(cmd).status)
+ self.assertEqual(1, len(glob(self.resultdir + "qemux86-directdisk-*direct")))
+
+ @OETestID(1350)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_mkefidisk(self):
+ """Test creation of mkefidisk image"""
+ cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
+ self.assertEqual(0, runCmd(cmd).status)
+ self.assertEqual(1, len(glob(self.resultdir + "mkefidisk-*direct")))
+
+ @OETestID(1385)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_bootloader_config(self):
+ """Test creation of directdisk-bootloader-config image"""
+ cmd = "wic create directdisk-bootloader-config -e core-image-minimal -o %s" % self.resultdir
+ self.assertEqual(0, runCmd(cmd).status)
+ self.assertEqual(1, len(glob(self.resultdir + "directdisk-bootloader-config-*direct")))
+
+ @OETestID(1560)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_systemd_bootdisk(self):
+ """Test creation of systemd-bootdisk image"""
+ config = 'MACHINE_FEATURES_append = " efi"\n'
+ self.append_config(config)
+ bitbake('core-image-minimal')
+ self.remove_config(config)
+ cmd = "wic create systemd-bootdisk -e core-image-minimal -o %s" % self.resultdir
+ self.assertEqual(0, runCmd(cmd).status)
+ self.assertEqual(1, len(glob(self.resultdir + "systemd-bootdisk-*direct")))
+
+ @OETestID(1561)
+ def test_sdimage_bootpart(self):
+ """Test creation of sdimage-bootpart image"""
+ cmd = "wic create sdimage-bootpart -e core-image-minimal -o %s" % self.resultdir
+ kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
+ self.write_config('IMAGE_BOOT_FILES = "%s"\n' % kimgtype)
+ self.assertEqual(0, runCmd(cmd).status)
+ self.assertEqual(1, len(glob(self.resultdir + "sdimage-bootpart-*direct")))
+
+ @OETestID(1562)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_default_output_dir(self):
+ """Test default output location"""
+ for fname in glob("directdisk-*.direct"):
+ os.remove(fname)
+ cmd = "wic create directdisk -e core-image-minimal"
+ self.assertEqual(0, runCmd(cmd).status)
+ self.assertEqual(1, len(glob("directdisk-*.direct")))
+
+ @OETestID(1212)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_build_artifacts(self):
+ """Test wic create directdisk providing all artifacts."""
+ bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
+ 'wic-tools')
+ bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
+ 'core-image-minimal'))
+ bbvars = {key.lower(): value for key, value in bb_vars.items()}
+ bbvars['resultdir'] = self.resultdir
+ status = runCmd("wic create directdisk "
+ "-b %(staging_datadir)s "
+ "-k %(deploy_dir_image)s "
+ "-n %(recipe_sysroot_native)s "
+ "-r %(image_rootfs)s "
+ "-o %(resultdir)s" % bbvars).status
+ self.assertEqual(0, status)
+ self.assertEqual(1, len(glob(self.resultdir + "directdisk-*.direct")))
+
+ @OETestID(1264)
+ def test_compress_gzip(self):
+ """Test compressing an image with gzip"""
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name core-image-minimal "
+ "-c gzip -o %s" % self.resultdir).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct.gz")))
+
+ @OETestID(1265)
+ def test_compress_bzip2(self):
+ """Test compressing an image with bzip2"""
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=core-image-minimal "
+ "-c bzip2 -o %s" % self.resultdir).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct.bz2")))
+
+ @OETestID(1266)
+ def test_compress_xz(self):
+ """Test compressing an image with xz"""
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=core-image-minimal "
+ "--compress-with=xz -o %s" % self.resultdir).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct.xz")))
+
+ @OETestID(1267)
+ def test_wrong_compressor(self):
+ """Test how wic breaks if wrong compressor is provided"""
+ self.assertEqual(2, runCmd("wic create wictestdisk "
+ "--image-name=core-image-minimal "
+ "-c wrong -o %s" % self.resultdir,
+ ignore_status=True).status)
+
+ @OETestID(1558)
+ def test_debug_short(self):
+ """Test -D option"""
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=core-image-minimal "
+ "-D -o %s" % self.resultdir).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+ def test_debug_long(self):
+ """Test --debug option"""
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=core-image-minimal "
+ "--debug -o %s" % self.resultdir).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+ @OETestID(1563)
+ def test_skip_build_check_short(self):
+ """Test -s option"""
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=core-image-minimal "
+ "-s -o %s" % self.resultdir).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+ def test_skip_build_check_long(self):
+ """Test --skip-build-check option"""
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=core-image-minimal "
+ "--skip-build-check "
+ "--outdir %s" % self.resultdir).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+ @OETestID(1564)
+ def test_build_rootfs_short(self):
+ """Test -f option"""
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=core-image-minimal "
+ "-f -o %s" % self.resultdir).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+ def test_build_rootfs_long(self):
+ """Test --build-rootfs option"""
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=core-image-minimal "
+ "--build-rootfs "
+ "--outdir %s" % self.resultdir).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
+
+ @OETestID(1268)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_rootfs_indirect_recipes(self):
+ """Test usage of rootfs plugin with rootfs recipes"""
+ status = runCmd("wic create directdisk-multi-rootfs "
+ "--image-name=core-image-minimal "
+ "--rootfs rootfs1=core-image-minimal "
+ "--rootfs rootfs2=core-image-minimal "
+ "--outdir %s" % self.resultdir).status
+ self.assertEqual(0, status)
+ self.assertEqual(1, len(glob(self.resultdir + "directdisk-multi-rootfs*.direct")))
+
+ @OETestID(1269)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_rootfs_artifacts(self):
+ """Test usage of rootfs plugin with rootfs paths"""
+ bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
+ 'wic-tools')
+ bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
+ 'core-image-minimal'))
+ bbvars = {key.lower(): value for key, value in bb_vars.items()}
+ bbvars['wks'] = "directdisk-multi-rootfs"
+ bbvars['resultdir'] = self.resultdir
+ status = runCmd("wic create %(wks)s "
+ "--bootimg-dir=%(staging_datadir)s "
+ "--kernel-dir=%(deploy_dir_image)s "
+ "--native-sysroot=%(recipe_sysroot_native)s "
+ "--rootfs-dir rootfs1=%(image_rootfs)s "
+ "--rootfs-dir rootfs2=%(image_rootfs)s "
+ "--outdir %(resultdir)s" % bbvars).status
+ self.assertEqual(0, status)
+ self.assertEqual(1, len(glob(self.resultdir + "%(wks)s-*.direct" % bbvars)))
+
+ def test_exclude_path(self):
+ """Test --exclude-path wks option."""
+
+ oldpath = os.environ['PATH']
+ os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
+
+ try:
+ wks_file = 'temp.wks'
+ with open(wks_file, 'w') as wks:
+ rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
+ wks.write("""
+part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr
+part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr
+part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr"""
+ % (rootfs_dir, rootfs_dir))
+ self.assertEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
+ % (wks_file, self.resultdir)).status)
+
+ os.remove(wks_file)
+ wicout = glob(self.resultdir + "%s-*direct" % 'temp')
+ self.assertEqual(1, len(wicout))
+
+ wicimg = wicout[0]
+
+ # verify partition size with wic
+ res = runCmd("parted -m %s unit b p 2>/dev/null" % wicimg)
+ self.assertEqual(0, res.status)
+
+ # parse parted output which looks like this:
+ # BYT;\n
+ # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
+ # 1:0.00MiB:200MiB:200MiB:ext4::;\n
+ partlns = res.output.splitlines()[2:]
+
+ self.assertEqual(3, len(partlns))
+
+ for part in [1, 2, 3]:
+ part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
+ partln = partlns[part-1].split(":")
+ self.assertEqual(7, len(partln))
+ start = int(partln[1].rstrip("B")) / 512
+ length = int(partln[3].rstrip("B")) / 512
+ self.assertEqual(0, runCmd("dd if=%s of=%s skip=%d count=%d" %
+ (wicimg, part_file, start, length)).status)
+
+ def extract_files(debugfs_output):
+ """
+ extract file names from the output of debugfs -R 'ls -p',
+ which looks like this:
+
+ /2/040755/0/0/.//\n
+ /2/040755/0/0/..//\n
+ /11/040700/0/0/lost+found^M//\n
+ /12/040755/1002/1002/run//\n
+ /13/040755/1002/1002/sys//\n
+ /14/040755/1002/1002/bin//\n
+ /80/040755/1002/1002/var//\n
+ /92/040755/1002/1002/tmp//\n
+ """
+ # NOTE the occasional ^M in file names
+ return [line.split('/')[5].strip() for line in \
+ debugfs_output.strip().split('/\n')]
+
+ # Test partition 1, should contain the normal root directories, except
+ # /usr.
+ res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
+ os.path.join(self.resultdir, "selftest_img.part1"))
+ self.assertEqual(0, res.status)
+ files = extract_files(res.output)
+ self.assertIn("etc", files)
+ self.assertNotIn("usr", files)
+
+ # Partition 2, should contain common directories for /usr, not root
+ # directories.
+ res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
+ os.path.join(self.resultdir, "selftest_img.part2"))
+ self.assertEqual(0, res.status)
+ files = extract_files(res.output)
+ self.assertNotIn("etc", files)
+ self.assertNotIn("usr", files)
+ self.assertIn("share", files)
+
+ # Partition 3, should contain the same as partition 2, including the bin
+ # directory, but not the files inside it.
+ res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
+ os.path.join(self.resultdir, "selftest_img.part3"))
+ self.assertEqual(0, res.status)
+ files = extract_files(res.output)
+ self.assertNotIn("etc", files)
+ self.assertNotIn("usr", files)
+ self.assertIn("share", files)
+ self.assertIn("bin", files)
+ res = runCmd("debugfs -R 'ls -p bin' %s 2>/dev/null" % \
+ os.path.join(self.resultdir, "selftest_img.part3"))
+ self.assertEqual(0, res.status)
+ files = extract_files(res.output)
+ self.assertIn(".", files)
+ self.assertIn("..", files)
+ self.assertEqual(2, len(files))
+
+ for part in [1, 2, 3]:
+ part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
+ os.remove(part_file)
+
+ finally:
+ os.environ['PATH'] = oldpath
+
+ def test_exclude_path_errors(self):
+ """Test --exclude-path wks option error handling."""
+ wks_file = 'temp.wks'
+
+ # Absolute argument.
+ with open(wks_file, 'w') as wks:
+ wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path /usr")
+ self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
+ % (wks_file, self.resultdir), ignore_status=True).status)
+ os.remove(wks_file)
+
+ # Argument pointing to parent directory.
+ with open(wks_file, 'w') as wks:
+ wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path ././..")
+ self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
+ % (wks_file, self.resultdir), ignore_status=True).status)
+ os.remove(wks_file)
+
+ @OETestID(1496)
+ def test_bmap_short(self):
+ """Test generation of .bmap file -m option"""
+ cmd = "wic create wictestdisk -e core-image-minimal -m -o %s" % self.resultdir
+ status = runCmd(cmd).status
+ self.assertEqual(0, status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct.bmap")))
+
+ def test_bmap_long(self):
+ """Test generation of .bmap file --bmap option"""
+ cmd = "wic create wictestdisk -e core-image-minimal --bmap -o %s" % self.resultdir
+ status = runCmd(cmd).status
+ self.assertEqual(0, status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct.bmap")))
+
+ def _get_image_env_path(self, image):
+ """Generate and obtain the path to <image>.env"""
+ if image not in self.wicenv_cache:
+ self.assertEqual(0, bitbake('%s -c do_rootfs_wicenv' % image).status)
+ bb_vars = get_bb_vars(['STAGING_DIR', 'MACHINE'], image)
+ stdir = bb_vars['STAGING_DIR']
+ machine = bb_vars['MACHINE']
+ self.wicenv_cache[image] = os.path.join(stdir, machine, 'imgdata')
+ return self.wicenv_cache[image]
+
+ @OETestID(1347)
+ def test_image_env(self):
+ """Test generation of <image>.env files."""
+ image = 'core-image-minimal'
+ imgdatadir = self._get_image_env_path(image)
+
+ bb_vars = get_bb_vars(['IMAGE_BASENAME', 'WICVARS'], image)
+ basename = bb_vars['IMAGE_BASENAME']
+ self.assertEqual(basename, image)
+ path = os.path.join(imgdatadir, basename) + '.env'
+ self.assertTrue(os.path.isfile(path))
+
+ wicvars = set(bb_vars['WICVARS'].split())
+ # filter out optional variables
+ wicvars = wicvars.difference(('DEPLOY_DIR_IMAGE', 'IMAGE_BOOT_FILES',
+ 'INITRD', 'INITRD_LIVE', 'ISODIR'))
+ with open(path) as envfile:
+ content = dict(line.split("=", 1) for line in envfile)
+ # test if variables used by wic present in the .env file
+ for var in wicvars:
+ self.assertTrue(var in content, "%s is not in .env file" % var)
+ self.assertTrue(content[var])
+
+ @OETestID(1559)
+ def test_image_vars_dir_short(self):
+ """Test image vars directory selection -v option"""
+ image = 'core-image-minimal'
+ imgenvdir = self._get_image_env_path(image)
+
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=%s -v %s -o %s"
+ % (image, imgenvdir, self.resultdir)).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
+
+ def test_image_vars_dir_long(self):
+ """Test image vars directory selection --vars option"""
+ image = 'core-image-minimal'
+ imgenvdir = self._get_image_env_path(image)
+ self.assertEqual(0, runCmd("wic create wictestdisk "
+ "--image-name=%s "
+ "--vars %s "
+ "--outdir %s"
+ % (image, imgenvdir, self.resultdir)).status)
+ self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
+
+ @OETestID(1351)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_wic_image_type(self):
+ """Test building wic images by bitbake"""
+ config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
+ 'MACHINE_FEATURES_append = " efi"\n'
+ self.append_config(config)
+ self.assertEqual(0, bitbake('wic-image-minimal').status)
+ self.remove_config(config)
+
+ bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'MACHINE'])
+ deploy_dir = bb_vars['DEPLOY_DIR_IMAGE']
+ machine = bb_vars['MACHINE']
+ prefix = os.path.join(deploy_dir, 'wic-image-minimal-%s.' % machine)
+ # check if we have result image and manifests symlinks
+ # pointing to existing files
+ for suffix in ('wic', 'manifest'):
+ path = prefix + suffix
+ self.assertTrue(os.path.islink(path))
+ self.assertTrue(os.path.isfile(os.path.realpath(path)))
+
+ @OETestID(1422)
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_qemu(self):
+ """Test wic-image-minimal under qemu"""
+ config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
+ 'MACHINE_FEATURES_append = " efi"\n'
+ self.append_config(config)
+ self.assertEqual(0, bitbake('wic-image-minimal').status)
+ self.remove_config(config)
+
+ with runqemu('wic-image-minimal', ssh=False) as qemu:
+ cmd = "mount |grep '^/dev/' | cut -f1,3 -d ' '"
+ status, output = qemu.run_serial(cmd)
+ self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
+ self.assertEqual(output, '/dev/root /\r\n/dev/sda3 /mnt')
+
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_qemu_efi(self):
+ """Test core-image-minimal efi image under qemu"""
+ config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "mkefidisk.wks"\n'
+ self.append_config(config)
+ self.assertEqual(0, bitbake('core-image-minimal ovmf').status)
+ self.remove_config(config)
+
+ with runqemu('core-image-minimal', ssh=False,
+ runqemuparams='ovmf', image_fstype='wic') as qemu:
+ cmd = "grep sda. /proc/partitions |wc -l"
+ status, output = qemu.run_serial(cmd)
+ self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
+ self.assertEqual(output, '3')
+
+ @staticmethod
+ def _make_fixed_size_wks(size):
+ """
+ Create a wks of an image with a single partition. Size of the partition is set
+ using --fixed-size flag. Returns a tuple: (path to wks file, wks image name)
+ """
+ with NamedTemporaryFile("w", suffix=".wks", delete=False) as tempf:
+ wkspath = tempf.name
+ tempf.write("part " \
+ "--source rootfs --ondisk hda --align 4 --fixed-size %d "
+ "--fstype=ext4\n" % size)
+ wksname = os.path.splitext(os.path.basename(wkspath))[0]
+
+ return wkspath, wksname
+
+ def test_fixed_size(self):
+ """
+ Test creation of a simple image with partition size controlled through
+ --fixed-size flag
+ """
+ wkspath, wksname = Wic._make_fixed_size_wks(200)
+
+ self.assertEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
+ % (wkspath, self.resultdir)).status)
+ os.remove(wkspath)
+ wicout = glob(self.resultdir + "%s-*direct" % wksname)
+ self.assertEqual(1, len(wicout))
+
+ wicimg = wicout[0]
+
+ # verify partition size with wic
+ res = runCmd("parted -m %s unit mib p 2>/dev/null" % wicimg,
+ ignore_status=True,
+ native_sysroot=self.native_sysroot)
+ self.assertEqual(0, res.status)
+
+ # parse parted output which looks like this:
+ # BYT;\n
+ # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
+ # 1:0.00MiB:200MiB:200MiB:ext4::;\n
+ partlns = res.output.splitlines()[2:]
+
+ self.assertEqual(1, len(partlns))
+ self.assertEqual("1:0.00MiB:200MiB:200MiB:ext4::;", partlns[0])
+
+ def test_fixed_size_error(self):
+ """
+ Test creation of a simple image with partition size controlled through
+ --fixed-size flag. The size of partition is intentionally set to 1MiB
+ in order to trigger an error in wic.
+ """
+ wkspath, wksname = Wic._make_fixed_size_wks(1)
+
+ self.assertEqual(1, runCmd("wic create %s -e core-image-minimal -o %s" \
+ % (wkspath, self.resultdir), ignore_status=True).status)
+ os.remove(wkspath)
+ wicout = glob(self.resultdir + "%s-*direct" % wksname)
+ self.assertEqual(0, len(wicout))
+
+ @only_for_arch(['i586', 'i686', 'x86_64'])
+ def test_rawcopy_plugin_qemu(self):
+ """Test rawcopy plugin in qemu"""
+ # build ext4 and wic images
+ for fstype in ("ext4", "wic"):
+ config = 'IMAGE_FSTYPES = "%s"\nWKS_FILE = "test_rawcopy_plugin.wks.in"\n' % fstype
+ self.append_config(config)
+ self.assertEqual(0, bitbake('core-image-minimal').status)
+ self.remove_config(config)
+
+ with runqemu('core-image-minimal', ssh=False, image_fstype='wic') as qemu:
+ cmd = "grep sda. /proc/partitions |wc -l"
+ status, output = qemu.run_serial(cmd)
+ self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
+ self.assertEqual(output, '2')
+
+ def test_rawcopy_plugin(self):
+ """Test rawcopy plugin"""
+ img = 'core-image-minimal'
+ machine = get_bb_var('MACHINE', img)
+ with NamedTemporaryFile("w", suffix=".wks") as wks:
+ wks.writelines(['part /boot --active --source bootimg-pcbios\n',
+ 'part / --source rawcopy --sourceparams="file=%s-%s.ext4" --use-uuid\n'\
+ % (img, machine),
+ 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
+ wks.flush()
+ cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
+ self.assertEqual(0, runCmd(cmd).status)
+ wksname = os.path.splitext(os.path.basename(wks.name))[0]
+ out = glob(self.resultdir + "%s-*direct" % wksname)
+ self.assertEqual(1, len(out))
+
+ def test_fs_types(self):
+ """Test filesystem types for empty and not empty partitions"""
+ img = 'core-image-minimal'
+ with NamedTemporaryFile("w", suffix=".wks") as wks:
+ wks.writelines(['part ext2 --fstype ext2 --source rootfs\n',
+ 'part btrfs --fstype btrfs --source rootfs --size 40M\n',
+ 'part squash --fstype squashfs --source rootfs\n',
+ 'part swap --fstype swap --size 1M\n',
+ 'part emptyvfat --fstype vfat --size 1M\n',
+ 'part emptymsdos --fstype msdos --size 1M\n',
+ 'part emptyext2 --fstype ext2 --size 1M\n',
+ 'part emptybtrfs --fstype btrfs --size 100M\n'])
+ wks.flush()
+ cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
+ self.assertEqual(0, runCmd(cmd).status)
+ wksname = os.path.splitext(os.path.basename(wks.name))[0]
+ out = glob(self.resultdir + "%s-*direct" % wksname)
+ self.assertEqual(1, len(out))
+
+ def test_kickstart_parser(self):
+ """Test wks parser options"""
+ with NamedTemporaryFile("w", suffix=".wks") as wks:
+ wks.writelines(['part / --fstype ext3 --source rootfs --system-id 0xFF '\
+ '--overhead-factor 1.2 --size 100k\n'])
+ wks.flush()
+ cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
+ self.assertEqual(0, runCmd(cmd).status)
+ wksname = os.path.splitext(os.path.basename(wks.name))[0]
+ out = glob(self.resultdir + "%s-*direct" % wksname)
+ self.assertEqual(1, len(out))
+
+ def test_image_bootpart_globbed(self):
+ """Test globbed sources with image-bootpart plugin"""
+ img = "core-image-minimal"
+ cmd = "wic create sdimage-bootpart -e %s -o %s" % (img, self.resultdir)
+ config = 'IMAGE_BOOT_FILES = "%s*"' % get_bb_var('KERNEL_IMAGETYPE', img)
+ self.append_config(config)
+ self.assertEqual(0, runCmd(cmd).status)
+ self.remove_config(config)
+ self.assertEqual(1, len(glob(self.resultdir + "sdimage-bootpart-*direct")))
+
+ def test_sparse_copy(self):
+ """Test sparse_copy with FIEMAP and SEEK_HOLE filemap APIs"""
+ libpath = os.path.join(get_bb_var('COREBASE'), 'scripts', 'lib', 'wic')
+ sys.path.insert(0, libpath)
+ from filemap import FilemapFiemap, FilemapSeek, sparse_copy, ErrorNotSupp
+ with NamedTemporaryFile("w", suffix=".wic-sparse") as sparse:
+ src_name = sparse.name
+ src_size = 1024 * 10
+ sparse.truncate(src_size)
+ # write one byte to the file
+ with open(src_name, 'r+b') as sfile:
+ sfile.seek(1024 * 4)
+ sfile.write(b'\x00')
+ dest = sparse.name + '.out'
+ # copy src file to dest using different filemap APIs
+ for api in (FilemapFiemap, FilemapSeek, None):
+ if os.path.exists(dest):
+ os.unlink(dest)
+ try:
+ sparse_copy(sparse.name, dest, api=api)
+ except ErrorNotSupp:
+ continue # skip unsupported API
+ dest_stat = os.stat(dest)
+ self.assertEqual(dest_stat.st_size, src_size)
+ # 8 blocks is 4K (physical sector size)
+ self.assertEqual(dest_stat.st_blocks, 8)
+ os.unlink(dest)