summaryrefslogtreecommitdiffstats
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-06 19:02:43 +0100
commit157c3be2ca93f076033f725ec1ee912df91f7488 (patch)
tree8ef896ff7adf78d63b34059cd5b017a4f0a3419a /meta/lib/oeqa/selftest/cases
parent10c512b60d1167122b5fe778b93838dca3def717 (diff)
downloadpoky-157c3be2ca93f076033f725ec1ee912df91f7488.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. (From OE-Core rev: ddbbefdd124604d10bd47dd0266b55a764fcc0ab) Signed-off-by: Leonardo Sandoval <leonardo.sandoval.gonzalez@linux.intel.com> Signed-off-by: Aníbal Limón <anibal.limon@linux.intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
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 @@
1import os
2import shutil
3
4import oeqa.utils.ftools as ftools
5from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer
6from oeqa.selftest.cases.sstate import SStateBase
7
8
9class RebuildFromSState(SStateBase):
10
11 @classmethod
12 def setUpClass(self):
13 super(RebuildFromSState, self).setUpClass()
14 self.builddir = os.path.join(os.environ.get('BUILDDIR'))
15
16 def get_dep_targets(self, primary_targets):
17 found_targets = []
18 bitbake("-g " + ' '.join(map(str, primary_targets)))
19 with open(os.path.join(self.builddir, 'pn-buildlist'), 'r') as pnfile:
20 found_targets = pnfile.read().splitlines()
21 return found_targets
22
23 def configure_builddir(self, builddir):
24 os.mkdir(builddir)
25 self.track_for_cleanup(builddir)
26 os.mkdir(os.path.join(builddir, 'conf'))
27 shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/local.conf'), os.path.join(builddir, 'conf/local.conf'))
28 config = {}
29 config['default_sstate_dir'] = "SSTATE_DIR ?= \"${TOPDIR}/sstate-cache\""
30 config['null_sstate_mirrors'] = "SSTATE_MIRRORS = \"\""
31 config['default_tmp_dir'] = "TMPDIR = \"${TOPDIR}/tmp\""
32 for key in config:
33 ftools.append_file(os.path.join(builddir, 'conf/selftest.inc'), config[key])
34 shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/bblayers.conf'), os.path.join(builddir, 'conf/bblayers.conf'))
35 try:
36 shutil.copyfile(os.path.join(os.environ.get('BUILDDIR'), 'conf/auto.conf'), os.path.join(builddir, 'conf/auto.conf'))
37 except:
38 pass
39
40 def hardlink_tree(self, src, dst):
41 os.mkdir(dst)
42 self.track_for_cleanup(dst)
43 for root, dirs, files in os.walk(src):
44 if root == src:
45 continue
46 os.mkdir(os.path.join(dst, root.split(src)[1][1:]))
47 for sstate_file in files:
48 os.link(os.path.join(root, sstate_file), os.path.join(dst, root.split(src)[1][1:], sstate_file))
49
50 def run_test_sstate_rebuild(self, primary_targets, relocate=False, rebuild_dependencies=False):
51 buildA = os.path.join(self.builddir, 'buildA')
52 if relocate:
53 buildB = os.path.join(self.builddir, 'buildB')
54 else:
55 buildB = buildA
56
57 if rebuild_dependencies:
58 rebuild_targets = self.get_dep_targets(primary_targets)
59 else:
60 rebuild_targets = primary_targets
61
62 self.configure_builddir(buildA)
63 runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildA)) + 'bitbake ' + ' '.join(map(str, primary_targets)), shell=True, executable='/bin/bash')
64 self.hardlink_tree(os.path.join(buildA, 'sstate-cache'), os.path.join(self.builddir, 'sstate-cache-buildA'))
65 shutil.rmtree(buildA)
66
67 failed_rebuild = []
68 failed_cleansstate = []
69 for target in rebuild_targets:
70 self.configure_builddir(buildB)
71 self.hardlink_tree(os.path.join(self.builddir, 'sstate-cache-buildA'), os.path.join(buildB, 'sstate-cache'))
72
73 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')
74 if not result_cleansstate.status == 0:
75 failed_cleansstate.append(target)
76 shutil.rmtree(buildB)
77 continue
78
79 result_build = runCmd((". %s/oe-init-build-env %s && " % (get_bb_var('COREBASE'), buildB)) + 'bitbake ' + target, ignore_status=True, shell=True, executable='/bin/bash')
80 if not result_build.status == 0:
81 failed_rebuild.append(target)
82
83 shutil.rmtree(buildB)
84
85 self.assertFalse(failed_rebuild, msg="The following recipes have failed to rebuild: %s" % ' '.join(map(str, failed_rebuild)))
86 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)))
87
88 def test_sstate_relocation(self):
89 self.run_test_sstate_rebuild(['core-image-sato-sdk'], relocate=True, rebuild_dependencies=True)
90
91 def test_sstate_rebuild(self):
92 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 @@
1import os
2import glob
3from oeqa.utils.commands import bitbake, get_bb_vars
4from oeqa.selftest.case import OESelftestTestCase
5from oeqa.core.decorator.oeid import OETestID
6
7class Archiver(OESelftestTestCase):
8
9 @OETestID(1345)
10 def test_archiver_allows_to_filter_on_recipe_name(self):
11 """
12 Summary: The archiver should offer the possibility to filter on the recipe. (#6929)
13 Expected: 1. Included recipe (busybox) should be included
14 2. Excluded recipe (zlib) should be excluded
15 Product: oe-core
16 Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
17 AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
18 """
19
20 include_recipe = 'busybox'
21 exclude_recipe = 'zlib'
22
23 features = 'INHERIT += "archiver"\n'
24 features += 'ARCHIVER_MODE[src] = "original"\n'
25 features += 'COPYLEFT_PN_INCLUDE = "%s"\n' % include_recipe
26 features += 'COPYLEFT_PN_EXCLUDE = "%s"\n' % exclude_recipe
27 self.write_config(features)
28
29 bitbake('-c clean %s %s' % (include_recipe, exclude_recipe))
30 bitbake("%s %s" % (include_recipe, exclude_recipe))
31
32 bb_vars = get_bb_vars(['DEPLOY_DIR_SRC', 'TARGET_SYS'])
33 src_path = os.path.join(bb_vars['DEPLOY_DIR_SRC'], bb_vars['TARGET_SYS'])
34
35 # Check that include_recipe was included
36 included_present = len(glob.glob(src_path + '/%s-*' % include_recipe))
37 self.assertTrue(included_present, 'Recipe %s was not included.' % include_recipe)
38
39 # Check that exclude_recipe was excluded
40 excluded_present = len(glob.glob(src_path + '/%s-*' % exclude_recipe))
41 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 @@
1import os
2import re
3
4import oeqa.utils.ftools as ftools
5from oeqa.utils.commands import runCmd, get_bb_var
6
7from oeqa.selftest.case import OESelftestTestCase
8from oeqa.core.decorator.oeid import OETestID
9
10class BitbakeLayers(OESelftestTestCase):
11
12 @OETestID(756)
13 def test_bitbakelayers_showcrossdepends(self):
14 result = runCmd('bitbake-layers show-cross-depends')
15 self.assertTrue('aspell' in result.output, msg = "No dependencies were shown. bitbake-layers show-cross-depends output: %s" % result.output)
16
17 @OETestID(83)
18 def test_bitbakelayers_showlayers(self):
19 result = runCmd('bitbake-layers show-layers')
20 self.assertTrue('meta-selftest' in result.output, msg = "No layers were shown. bitbake-layers show-layers output: %s" % result.output)
21
22 @OETestID(93)
23 def test_bitbakelayers_showappends(self):
24 recipe = "xcursor-transparent-theme"
25 bb_file = self.get_recipe_basename(recipe)
26 result = runCmd('bitbake-layers show-appends')
27 self.assertTrue(bb_file in result.output, msg="%s file was not recognised. bitbake-layers show-appends output: %s" % (bb_file, result.output))
28
29 @OETestID(90)
30 def test_bitbakelayers_showoverlayed(self):
31 result = runCmd('bitbake-layers show-overlayed')
32 self.assertTrue('aspell' in result.output, msg="aspell overlayed recipe was not recognised bitbake-layers show-overlayed %s" % result.output)
33
34 @OETestID(95)
35 def test_bitbakelayers_flatten(self):
36 recipe = "xcursor-transparent-theme"
37 recipe_path = "recipes-graphics/xcursor-transparent-theme"
38 recipe_file = self.get_recipe_basename(recipe)
39 testoutdir = os.path.join(self.builddir, 'test_bitbakelayers_flatten')
40 self.assertFalse(os.path.isdir(testoutdir), msg = "test_bitbakelayers_flatten should not exist at this point in time")
41 self.track_for_cleanup(testoutdir)
42 result = runCmd('bitbake-layers flatten %s' % testoutdir)
43 bb_file = os.path.join(testoutdir, recipe_path, recipe_file)
44 self.assertTrue(os.path.isfile(bb_file), msg = "Cannot find xcursor-transparent-theme_0.1.1.bb in the test_bitbakelayers_flatten local dir.")
45 contents = ftools.read_file(bb_file)
46 find_in_contents = re.search("##### bbappended from meta-selftest #####\n(.*\n)*include test_recipe.inc", contents)
47 self.assertTrue(find_in_contents, msg = "Flattening layers did not work. bitbake-layers flatten output: %s" % result.output)
48
49 @OETestID(1195)
50 def test_bitbakelayers_add_remove(self):
51 test_layer = os.path.join(get_bb_var('COREBASE'), 'meta-skeleton')
52 result = runCmd('bitbake-layers show-layers')
53 self.assertNotIn('meta-skeleton', result.output, "This test cannot run with meta-skeleton in bblayers.conf. bitbake-layers show-layers output: %s" % result.output)
54 result = runCmd('bitbake-layers add-layer %s' % test_layer)
55 result = runCmd('bitbake-layers show-layers')
56 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)
57 result = runCmd('bitbake-layers remove-layer %s' % test_layer)
58 result = runCmd('bitbake-layers show-layers')
59 self.assertNotIn('meta-skeleton', result.output, msg = "meta-skeleton should have been removed at this step. bitbake-layers show-layers output: %s" % result.output)
60 result = runCmd('bitbake-layers add-layer %s' % test_layer)
61 result = runCmd('bitbake-layers show-layers')
62 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)
63 result = runCmd('bitbake-layers remove-layer */meta-skeleton')
64 result = runCmd('bitbake-layers show-layers')
65 self.assertNotIn('meta-skeleton', result.output, msg = "meta-skeleton should have been removed at this step. bitbake-layers show-layers output: %s" % result.output)
66
67 @OETestID(1384)
68 def test_bitbakelayers_showrecipes(self):
69 result = runCmd('bitbake-layers show-recipes')
70 self.assertIn('aspell:', result.output)
71 self.assertIn('mtd-utils:', result.output)
72 self.assertIn('core-image-minimal:', result.output)
73 result = runCmd('bitbake-layers show-recipes mtd-utils')
74 self.assertIn('mtd-utils:', result.output)
75 self.assertNotIn('aspell:', result.output)
76 result = runCmd('bitbake-layers show-recipes -i image')
77 self.assertIn('core-image-minimal', result.output)
78 self.assertNotIn('mtd-utils:', result.output)
79 result = runCmd('bitbake-layers show-recipes -i cmake,pkgconfig')
80 self.assertIn('libproxy:', result.output)
81 self.assertNotIn('mtd-utils:', result.output) # doesn't inherit either
82 self.assertNotIn('wget:', result.output) # doesn't inherit cmake
83 self.assertNotIn('waffle:', result.output) # doesn't inherit pkgconfig
84 result = runCmd('bitbake-layers show-recipes -i nonexistentclass', ignore_status=True)
85 self.assertNotEqual(result.status, 0, 'bitbake-layers show-recipes -i nonexistentclass should have failed')
86 self.assertIn('ERROR:', result.output)
87
88 def get_recipe_basename(self, recipe):
89 recipe_file = ""
90 result = runCmd("bitbake-layers show-recipes -f %s" % recipe)
91 for line in result.output.splitlines():
92 if recipe in line:
93 recipe_file = line
94 break
95
96 self.assertTrue(os.path.isfile(recipe_file), msg = "Can't find recipe file for %s" % recipe)
97 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 @@
1import os
2import re
3
4import oeqa.utils.ftools as ftools
5from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
6
7from oeqa.selftest.case import OESelftestTestCase
8from oeqa.core.decorator.oeid import OETestID
9
10class BitbakeTests(OESelftestTestCase):
11
12 def getline(self, res, line):
13 for l in res.output.split('\n'):
14 if line in l:
15 return l
16
17 @OETestID(789)
18 def test_run_bitbake_from_dir_1(self):
19 os.chdir(os.path.join(self.builddir, 'conf'))
20 self.assertEqual(bitbake('-e').status, 0, msg = "bitbake couldn't run from \"conf\" dir")
21
22 @OETestID(790)
23 def test_run_bitbake_from_dir_2(self):
24 my_env = os.environ.copy()
25 my_env['BBPATH'] = my_env['BUILDDIR']
26 os.chdir(os.path.dirname(os.environ['BUILDDIR']))
27 self.assertEqual(bitbake('-e', env=my_env).status, 0, msg = "bitbake couldn't run from builddir")
28
29 @OETestID(806)
30 def test_event_handler(self):
31 self.write_config("INHERIT += \"test_events\"")
32 result = bitbake('m4-native')
33 find_build_started = re.search("NOTE: Test for bb\.event\.BuildStarted(\n.*)*NOTE: Executing RunQueue Tasks", result.output)
34 find_build_completed = re.search("Tasks Summary:.*(\n.*)*NOTE: Test for bb\.event\.BuildCompleted", result.output)
35 self.assertTrue(find_build_started, msg = "Match failed in:\n%s" % result.output)
36 self.assertTrue(find_build_completed, msg = "Match failed in:\n%s" % result.output)
37 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)
38
39 @OETestID(103)
40 def test_local_sstate(self):
41 bitbake('m4-native')
42 bitbake('m4-native -cclean')
43 result = bitbake('m4-native')
44 find_setscene = re.search("m4-native.*do_.*_setscene", result.output)
45 self.assertTrue(find_setscene, msg = "No \"m4-native.*do_.*_setscene\" message found during bitbake m4-native. bitbake output: %s" % result.output )
46
47 @OETestID(105)
48 def test_bitbake_invalid_recipe(self):
49 result = bitbake('-b asdf', ignore_status=True)
50 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)
51
52 @OETestID(107)
53 def test_bitbake_invalid_target(self):
54 result = bitbake('asdf', ignore_status=True)
55 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)
56
57 @OETestID(106)
58 def test_warnings_errors(self):
59 result = bitbake('-b asdf', ignore_status=True)
60 find_warnings = re.search("Summary: There w.{2,3}? [1-9][0-9]* WARNING messages* shown", result.output)
61 find_errors = re.search("Summary: There w.{2,3}? [1-9][0-9]* ERROR messages* shown", result.output)
62 self.assertTrue(find_warnings, msg="Did not find the mumber of warnings at the end of the build:\n" + result.output)
63 self.assertTrue(find_errors, msg="Did not find the mumber of errors at the end of the build:\n" + result.output)
64
65 @OETestID(108)
66 def test_invalid_patch(self):
67 # This patch already exists in SRC_URI so adding it again will cause the
68 # patch to fail.
69 self.write_recipeinc('man', 'SRC_URI += "file://man-1.5h1-make.patch"')
70 self.write_config("INHERIT_remove = \"report-error\"")
71 result = bitbake('man -c patch', ignore_status=True)
72 self.delete_recipeinc('man')
73 bitbake('-cclean man')
74 line = self.getline(result, "Function failed: patch_do_patch")
75 self.assertTrue(line and line.startswith("ERROR:"), msg = "Repeated patch application didn't fail. bitbake output: %s" % result.output)
76
77 @OETestID(1354)
78 def test_force_task_1(self):
79 # test 1 from bug 5875
80 test_recipe = 'zlib'
81 test_data = "Microsoft Made No Profit From Anyone's Zunes Yo"
82 bb_vars = get_bb_vars(['D', 'PKGDEST', 'mandir'], test_recipe)
83 image_dir = bb_vars['D']
84 pkgsplit_dir = bb_vars['PKGDEST']
85 man_dir = bb_vars['mandir']
86
87 bitbake('-c clean %s' % test_recipe)
88 bitbake('-c package -f %s' % test_recipe)
89 self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
90
91 man_file = os.path.join(image_dir + man_dir, 'man3/zlib.3')
92 ftools.append_file(man_file, test_data)
93 bitbake('-c package -f %s' % test_recipe)
94
95 man_split_file = os.path.join(pkgsplit_dir, 'zlib-doc' + man_dir, 'man3/zlib.3')
96 man_split_content = ftools.read_file(man_split_file)
97 self.assertIn(test_data, man_split_content, 'The man file has not changed in packages-split.')
98
99 ret = bitbake(test_recipe)
100 self.assertIn('task do_package_write_rpm:', ret.output, 'Task do_package_write_rpm did not re-executed.')
101
102 @OETestID(163)
103 def test_force_task_2(self):
104 # test 2 from bug 5875
105 test_recipe = 'zlib'
106
107 bitbake(test_recipe)
108 self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
109
110 result = bitbake('-C compile %s' % test_recipe)
111 look_for_tasks = ['do_compile:', 'do_install:', 'do_populate_sysroot:', 'do_package:']
112 for task in look_for_tasks:
113 self.assertIn(task, result.output, msg="Couldn't find %s task.")
114
115 @OETestID(167)
116 def test_bitbake_g(self):
117 result = bitbake('-g core-image-minimal')
118 for f in ['pn-buildlist', 'recipe-depends.dot', 'task-depends.dot']:
119 self.addCleanup(os.remove, f)
120 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)
121 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.")
122
123 @OETestID(899)
124 def test_image_manifest(self):
125 bitbake('core-image-minimal')
126 bb_vars = get_bb_vars(["DEPLOY_DIR_IMAGE", "IMAGE_LINK_NAME"], "core-image-minimal")
127 deploydir = bb_vars["DEPLOY_DIR_IMAGE"]
128 imagename = bb_vars["IMAGE_LINK_NAME"]
129 manifest = os.path.join(deploydir, imagename + ".manifest")
130 self.assertTrue(os.path.islink(manifest), msg="No manifest file created for image. It should have been created in %s" % manifest)
131
132 @OETestID(168)
133 def test_invalid_recipe_src_uri(self):
134 data = 'SRC_URI = "file://invalid"'
135 self.write_recipeinc('man', data)
136 self.write_config("""DL_DIR = \"${TOPDIR}/download-selftest\"
137SSTATE_DIR = \"${TOPDIR}/download-selftest\"
138INHERIT_remove = \"report-error\"
139""")
140 self.track_for_cleanup(os.path.join(self.builddir, "download-selftest"))
141
142 bitbake('-ccleanall man')
143 result = bitbake('-c fetch man', ignore_status=True)
144 bitbake('-ccleanall man')
145 self.delete_recipeinc('man')
146 self.assertEqual(result.status, 1, msg="Command succeded when it should have failed. bitbake output: %s" % result.output)
147 self.assertTrue('Fetcher failure: Unable to find file file://invalid anywhere. The paths that were searched were:' in result.output, msg = "\"invalid\" file \
148doesn't exist, yet no error message encountered. bitbake output: %s" % result.output)
149 line = self.getline(result, 'Fetcher failure for URL: \'file://invalid\'. Unable to fetch URL from any source.')
150 self.assertTrue(line and line.startswith("ERROR:"), msg = "\"invalid\" file \
151doesn't exist, yet fetcher didn't report any error. bitbake output: %s" % result.output)
152
153 @OETestID(171)
154 def test_rename_downloaded_file(self):
155 # TODO unique dldir instead of using cleanall
156 # TODO: need to set sstatedir?
157 self.write_config("""DL_DIR = \"${TOPDIR}/download-selftest\"
158SSTATE_DIR = \"${TOPDIR}/download-selftest\"
159""")
160 self.track_for_cleanup(os.path.join(self.builddir, "download-selftest"))
161
162 data = 'SRC_URI = "${GNU_MIRROR}/aspell/aspell-${PV}.tar.gz;downloadfilename=test-aspell.tar.gz"'
163 self.write_recipeinc('aspell', data)
164 result = bitbake('-f -c fetch aspell', ignore_status=True)
165 self.delete_recipeinc('aspell')
166 self.assertEqual(result.status, 0, msg = "Couldn't fetch aspell. %s" % result.output)
167 dl_dir = get_bb_var("DL_DIR")
168 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)
169 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)
170
171 @OETestID(1028)
172 def test_environment(self):
173 self.write_config("TEST_ENV=\"localconf\"")
174 result = runCmd('bitbake -e | grep TEST_ENV=')
175 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='")
176
177 @OETestID(1029)
178 def test_dry_run(self):
179 result = runCmd('bitbake -n m4-native')
180 self.assertEqual(0, result.status, "bitbake dry run didn't run as expected. %s" % result.output)
181
182 @OETestID(1030)
183 def test_just_parse(self):
184 result = runCmd('bitbake -p')
185 self.assertEqual(0, result.status, "errors encountered when parsing recipes. %s" % result.output)
186
187 @OETestID(1031)
188 def test_version(self):
189 result = runCmd('bitbake -s | grep wget')
190 find = re.search("wget *:([0-9a-zA-Z\.\-]+)", result.output)
191 self.assertTrue(find, "No version returned for searched recipe. bitbake output: %s" % result.output)
192
193 @OETestID(1032)
194 def test_prefile(self):
195 preconf = os.path.join(self.builddir, 'conf/prefile.conf')
196 self.track_for_cleanup(preconf)
197 ftools.write_file(preconf ,"TEST_PREFILE=\"prefile\"")
198 result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
199 self.assertTrue('prefile' in result.output, "Preconfigure file \"prefile.conf\"was not taken into consideration. ")
200 self.write_config("TEST_PREFILE=\"localconf\"")
201 result = runCmd('bitbake -r conf/prefile.conf -e | grep TEST_PREFILE=')
202 self.assertTrue('localconf' in result.output, "Preconfigure file \"prefile.conf\"was not taken into consideration.")
203
204 @OETestID(1033)
205 def test_postfile(self):
206 postconf = os.path.join(self.builddir, 'conf/postfile.conf')
207 self.track_for_cleanup(postconf)
208 ftools.write_file(postconf , "TEST_POSTFILE=\"postfile\"")
209 self.write_config("TEST_POSTFILE=\"localconf\"")
210 result = runCmd('bitbake -R conf/postfile.conf -e | grep TEST_POSTFILE=')
211 self.assertTrue('postfile' in result.output, "Postconfigure file \"postfile.conf\"was not taken into consideration.")
212
213 @OETestID(1034)
214 def test_checkuri(self):
215 result = runCmd('bitbake -c checkuri m4')
216 self.assertEqual(0, result.status, msg = "\"checkuri\" task was not executed. bitbake output: %s" % result.output)
217
218 @OETestID(1035)
219 def test_continue(self):
220 self.write_config("""DL_DIR = \"${TOPDIR}/download-selftest\"
221SSTATE_DIR = \"${TOPDIR}/download-selftest\"
222INHERIT_remove = \"report-error\"
223""")
224 self.track_for_cleanup(os.path.join(self.builddir, "download-selftest"))
225 self.write_recipeinc('man',"\ndo_fail_task () {\nexit 1 \n}\n\naddtask do_fail_task before do_fetch\n" )
226 runCmd('bitbake -c cleanall man xcursor-transparent-theme')
227 result = runCmd('bitbake -c unpack -k man xcursor-transparent-theme', ignore_status=True)
228 errorpos = result.output.find('ERROR: Function failed: do_fail_task')
229 manver = re.search("NOTE: recipe xcursor-transparent-theme-(.*?): task do_unpack: Started", result.output)
230 continuepos = result.output.find('NOTE: recipe xcursor-transparent-theme-%s: task do_unpack: Started' % manver.group(1))
231 self.assertLess(errorpos,continuepos, msg = "bitbake didn't pass do_fail_task. bitbake output: %s" % result.output)
232
233 @OETestID(1119)
234 def test_non_gplv3(self):
235 self.write_config('INCOMPATIBLE_LICENSE = "GPLv3"')
236 result = bitbake('selftest-ed', ignore_status=True)
237 self.assertEqual(result.status, 0, "Bitbake failed, exit code %s, output %s" % (result.status, result.output))
238 lic_dir = get_bb_var('LICENSE_DIRECTORY')
239 self.assertFalse(os.path.isfile(os.path.join(lic_dir, 'selftest-ed/generic_GPLv3')))
240 self.assertTrue(os.path.isfile(os.path.join(lic_dir, 'selftest-ed/generic_GPLv2')))
241
242 @OETestID(1422)
243 def test_setscene_only(self):
244 """ Bitbake option to restore from sstate only within a build (i.e. execute no real tasks, only setscene)"""
245 test_recipe = 'ed'
246
247 bitbake(test_recipe)
248 bitbake('-c clean %s' % test_recipe)
249 ret = bitbake('--setscene-only %s' % test_recipe)
250
251 tasks = re.findall(r'task\s+(do_\S+):', ret.output)
252
253 for task in tasks:
254 self.assertIn('_setscene', task, 'A task different from _setscene ran: %s.\n'
255 'Executed tasks were: %s' % (task, str(tasks)))
256
257 @OETestID(1425)
258 def test_bbappend_order(self):
259 """ Bitbake should bbappend to recipe in a predictable order """
260 test_recipe = 'ed'
261 bb_vars = get_bb_vars(['SUMMARY', 'PV'], test_recipe)
262 test_recipe_summary_before = bb_vars['SUMMARY']
263 test_recipe_pv = bb_vars['PV']
264 recipe_append_file = test_recipe + '_' + test_recipe_pv + '.bbappend'
265 expected_recipe_summary = test_recipe_summary_before
266
267 for i in range(5):
268 recipe_append_dir = test_recipe + '_test_' + str(i)
269 recipe_append_path = os.path.join(self.testlayer_path, 'recipes-test', recipe_append_dir, recipe_append_file)
270 os.mkdir(os.path.join(self.testlayer_path, 'recipes-test', recipe_append_dir))
271 feature = 'SUMMARY += "%s"\n' % i
272 ftools.write_file(recipe_append_path, feature)
273 expected_recipe_summary += ' %s' % i
274
275 self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, 'recipes-test',
276 test_recipe + '_test_*'))
277
278 test_recipe_summary_after = get_bb_var('SUMMARY', test_recipe)
279 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 @@
1import os
2import re
3import datetime
4
5from oeqa.selftest.case import OESelftestTestCase
6from oeqa.utils.commands import bitbake, get_bb_vars
7
8
9class BuildhistoryBase(OESelftestTestCase):
10
11 def config_buildhistory(self, tmp_bh_location=False):
12 bb_vars = get_bb_vars(['USER_CLASSES', 'INHERIT'])
13 if (not 'buildhistory' in bb_vars['USER_CLASSES']) and (not 'buildhistory' in bb_vars['INHERIT']):
14 add_buildhistory_config = 'INHERIT += "buildhistory"\nBUILDHISTORY_COMMIT = "1"'
15 self.append_config(add_buildhistory_config)
16
17 if tmp_bh_location:
18 # Using a temporary buildhistory location for testing
19 tmp_bh_dir = os.path.join(self.builddir, "tmp_buildhistory_%s" % datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
20 buildhistory_dir_config = "BUILDHISTORY_DIR = \"%s\"" % tmp_bh_dir
21 self.append_config(buildhistory_dir_config)
22 self.track_for_cleanup(tmp_bh_dir)
23
24 def run_buildhistory_operation(self, target, global_config='', target_config='', change_bh_location=False, expect_error=False, error_regex=''):
25 if change_bh_location:
26 tmp_bh_location = True
27 else:
28 tmp_bh_location = False
29 self.config_buildhistory(tmp_bh_location)
30
31 self.append_config(global_config)
32 self.append_recipeinc(target, target_config)
33 bitbake("-cclean %s" % target)
34 result = bitbake(target, ignore_status=True)
35 self.remove_config(global_config)
36 self.remove_recipeinc(target, target_config)
37
38 if expect_error:
39 self.assertEqual(result.status, 1, msg="Error expected for global config '%s' and target config '%s'" % (global_config, target_config))
40 search_for_error = re.search(error_regex, result.output)
41 self.assertTrue(search_for_error, msg="Could not find desired error in output: %s (%s)" % (error_regex, result.output))
42 else:
43 self.assertEqual(result.status, 0, msg="Command 'bitbake %s' has failed unexpectedly: %s" % (target, result.output))
44
45 # No tests should be added to the base class.
46 # 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 @@
1import os
2import re
3import glob as g
4import shutil
5import tempfile
6from oeqa.selftest.case import OESelftestTestCase
7from oeqa.selftest.cases.buildhistory import BuildhistoryBase
8from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
9import oeqa.utils.ftools as ftools
10from oeqa.core.decorator.oeid import OETestID
11
12class ImageOptionsTests(OESelftestTestCase):
13
14 @OETestID(761)
15 def test_incremental_image_generation(self):
16 image_pkgtype = get_bb_var("IMAGE_PKGTYPE")
17 if image_pkgtype != 'rpm':
18 self.skipTest('Not using RPM as main package format')
19 bitbake("-c clean core-image-minimal")
20 self.write_config('INC_RPM_IMAGE_GEN = "1"')
21 self.append_config('IMAGE_FEATURES += "ssh-server-openssh"')
22 bitbake("core-image-minimal")
23 log_data_file = os.path.join(get_bb_var("WORKDIR", "core-image-minimal"), "temp/log.do_rootfs")
24 log_data_created = ftools.read_file(log_data_file)
25 incremental_created = re.search(r"Installing\s*:\s*packagegroup-core-ssh-openssh", log_data_created)
26 self.remove_config('IMAGE_FEATURES += "ssh-server-openssh"')
27 self.assertTrue(incremental_created, msg = "Match failed in:\n%s" % log_data_created)
28 bitbake("core-image-minimal")
29 log_data_removed = ftools.read_file(log_data_file)
30 incremental_removed = re.search(r"Erasing\s*:\s*packagegroup-core-ssh-openssh", log_data_removed)
31 self.assertTrue(incremental_removed, msg = "Match failed in:\n%s" % log_data_removed)
32
33 @OETestID(286)
34 def test_ccache_tool(self):
35 bitbake("ccache-native")
36 bb_vars = get_bb_vars(['SYSROOT_DESTDIR', 'bindir'], 'ccache-native')
37 p = bb_vars['SYSROOT_DESTDIR'] + bb_vars['bindir'] + "/" + "ccache"
38 self.assertTrue(os.path.isfile(p), msg = "No ccache found (%s)" % p)
39 self.write_config('INHERIT += "ccache"')
40 self.add_command_to_tearDown('bitbake -c clean m4')
41 bitbake("m4 -f -c compile")
42 log_compile = os.path.join(get_bb_var("WORKDIR","m4"), "temp/log.do_compile")
43 res = runCmd("grep ccache %s" % log_compile, ignore_status=True)
44 self.assertEqual(0, res.status, msg="No match for ccache in m4 log.do_compile. For further details: %s" % log_compile)
45
46 @OETestID(1435)
47 def test_read_only_image(self):
48 distro_features = get_bb_var('DISTRO_FEATURES')
49 if not ('x11' in distro_features and 'opengl' in distro_features):
50 self.skipTest('core-image-sato requires x11 and opengl in distro features')
51 self.write_config('IMAGE_FEATURES += "read-only-rootfs"')
52 bitbake("core-image-sato")
53 # do_image will fail if there are any pending postinsts
54
55class DiskMonTest(OESelftestTestCase):
56
57 @OETestID(277)
58 def test_stoptask_behavior(self):
59 self.write_config('BB_DISKMON_DIRS = "STOPTASKS,${TMPDIR},100000G,100K"')
60 res = bitbake("m4", ignore_status = True)
61 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)
62 self.assertEqual(res.status, 1, msg = "bitbake reported exit code %s. It should have been 1. Bitbake output: %s" % (str(res.status), res.output))
63 self.write_config('BB_DISKMON_DIRS = "ABORT,${TMPDIR},100000G,100K"')
64 res = bitbake("m4", ignore_status = True)
65 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)
66 self.assertEqual(res.status, 1, msg = "bitbake reported exit code %s. It should have been 1. Bitbake output: %s" % (str(res.status), res.output))
67 self.write_config('BB_DISKMON_DIRS = "WARN,${TMPDIR},100000G,100K"')
68 res = bitbake("m4")
69 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)
70
71class SanityOptionsTest(OESelftestTestCase):
72 def getline(self, res, line):
73 for l in res.output.split('\n'):
74 if line in l:
75 return l
76
77 @OETestID(927)
78 def test_options_warnqa_errorqa_switch(self):
79
80 self.write_config("INHERIT_remove = \"report-error\"")
81 if "packages-list" not in get_bb_var("ERROR_QA"):
82 self.append_config("ERROR_QA_append = \" packages-list\"")
83
84 self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
85 self.add_command_to_tearDown('bitbake -c clean xcursor-transparent-theme')
86 res = bitbake("xcursor-transparent-theme -f -c package", ignore_status=True)
87 self.delete_recipeinc('xcursor-transparent-theme')
88 line = self.getline(res, "QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors.")
89 self.assertTrue(line and line.startswith("ERROR:"), msg=res.output)
90 self.assertEqual(res.status, 1, msg = "bitbake reported exit code %s. It should have been 1. Bitbake output: %s" % (str(res.status), res.output))
91 self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"')
92 self.append_config('ERROR_QA_remove = "packages-list"')
93 self.append_config('WARN_QA_append = " packages-list"')
94 res = bitbake("xcursor-transparent-theme -f -c package")
95 self.delete_recipeinc('xcursor-transparent-theme')
96 line = self.getline(res, "QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors.")
97 self.assertTrue(line and line.startswith("WARNING:"), msg=res.output)
98
99 @OETestID(278)
100 def test_sanity_unsafe_script_references(self):
101 self.write_config('WARN_QA_append = " unsafe-references-in-scripts"')
102
103 self.add_command_to_tearDown('bitbake -c clean gzip')
104 res = bitbake("gzip -f -c package_qa")
105 line = self.getline(res, "QA Issue: gzip")
106 self.assertFalse(line, "WARNING: QA Issue: gzip message is present in bitbake's output and shouldn't be: %s" % res.output)
107
108 self.append_config("""
109do_install_append_pn-gzip () {
110 echo "\n${bindir}/test" >> ${D}${bindir}/zcat
111}
112""")
113 res = bitbake("gzip -f -c package_qa")
114 line = self.getline(res, "QA Issue: gzip")
115 self.assertTrue(line and line.startswith("WARNING:"), "WARNING: QA Issue: gzip message is not present in bitbake's output: %s" % res.output)
116
117 @OETestID(1421)
118 def test_layer_without_git_dir(self):
119 """
120 Summary: Test that layer git revisions are displayed and do not fail without git repository
121 Expected: The build to be successful and without "fatal" errors
122 Product: oe-core
123 Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
124 AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
125 """
126
127 dirpath = tempfile.mkdtemp()
128
129 dummy_layer_name = 'meta-dummy'
130 dummy_layer_path = os.path.join(dirpath, dummy_layer_name)
131 dummy_layer_conf_dir = os.path.join(dummy_layer_path, 'conf')
132 os.makedirs(dummy_layer_conf_dir)
133 dummy_layer_conf_path = os.path.join(dummy_layer_conf_dir, 'layer.conf')
134
135 dummy_layer_content = 'BBPATH .= ":${LAYERDIR}"\n' \
136 'BBFILES += "${LAYERDIR}/recipes-*/*/*.bb ${LAYERDIR}/recipes-*/*/*.bbappend"\n' \
137 'BBFILE_COLLECTIONS += "%s"\n' \
138 'BBFILE_PATTERN_%s = "^${LAYERDIR}/"\n' \
139 'BBFILE_PRIORITY_%s = "6"\n' % (dummy_layer_name, dummy_layer_name, dummy_layer_name)
140
141 ftools.write_file(dummy_layer_conf_path, dummy_layer_content)
142
143 bblayers_conf = 'BBLAYERS += "%s"\n' % dummy_layer_path
144 self.write_bblayers_config(bblayers_conf)
145
146 test_recipe = 'ed'
147
148 ret = bitbake('-n %s' % test_recipe)
149
150 err = 'fatal: Not a git repository'
151
152 shutil.rmtree(dirpath)
153
154 self.assertNotIn(err, ret.output)
155
156
157class BuildhistoryTests(BuildhistoryBase):
158
159 @OETestID(293)
160 def test_buildhistory_basic(self):
161 self.run_buildhistory_operation('xcursor-transparent-theme')
162 self.assertTrue(os.path.isdir(get_bb_var('BUILDHISTORY_DIR')), "buildhistory dir was not created.")
163
164 @OETestID(294)
165 def test_buildhistory_buildtime_pr_backwards(self):
166 target = 'xcursor-transparent-theme'
167 error = "ERROR:.*QA Issue: Package version for package %s went backwards which would break package feeds from (.*-r1.* to .*-r0.*)" % target
168 self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
169 self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True, error_regex=error)
170
171class ArchiverTest(OESelftestTestCase):
172 @OETestID(926)
173 def test_arch_work_dir_and_export_source(self):
174 """
175 Test for archiving the work directory and exporting the source files.
176 """
177 self.write_config("INHERIT += \"archiver\"\nARCHIVER_MODE[src] = \"original\"\nARCHIVER_MODE[srpm] = \"1\"")
178 res = bitbake("xcursor-transparent-theme", ignore_status=True)
179 self.assertEqual(res.status, 0, "\nCouldn't build xcursortransparenttheme.\nbitbake output %s" % res.output)
180 deploy_dir_src = get_bb_var('DEPLOY_DIR_SRC')
181 pkgs_path = g.glob(str(deploy_dir_src) + "/allarch*/xcurs*")
182 src_file_glob = str(pkgs_path[0]) + "/xcursor*.src.rpm"
183 tar_file_glob = str(pkgs_path[0]) + "/xcursor*.tar.gz"
184 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 @@
1import os
2
3from oeqa.selftest.case import OESelftestTestCase
4from oeqa.utils.commands import bitbake, get_bb_vars, runCmd
5
6# This test builds an image with using the "container" IMAGE_FSTYPE, and
7# ensures that then files in the image are only the ones expected.
8#
9# The only package added to the image is container_image_testpkg, which
10# contains one file. However, due to some other things not cleaning up during
11# rootfs creation, there is some cruft. Ideally bugs will be filed and the
12# cruft removed, but for now we whitelist some known set.
13#
14# Also for performance reasons we're only checking the cruft when using ipk.
15# When using deb, and rpm it is a bit different and we could test all
16# of them, but this test is more to catch if other packages get added by
17# default other than what is in ROOTFS_BOOTSTRAP_INSTALL.
18#
19class ContainerImageTests(OESelftestTestCase):
20
21 # Verify that when specifying a IMAGE_TYPEDEP_ of the form "foo.bar" that
22 # the conversion type bar gets added as a dep as well
23 def test_expected_files(self):
24
25 def get_each_path_part(path):
26 if path:
27 part = [ '.' + path + '/' ]
28 result = get_each_path_part(path.rsplit('/', 1)[0])
29 if result:
30 return part + result
31 else:
32 return part
33 else:
34 return None
35
36 self.write_config("""PREFERRED_PROVIDER_virtual/kernel = "linux-dummy"
37IMAGE_FSTYPES = "container"
38PACKAGE_CLASSES = "package_ipk"
39IMAGE_FEATURES = ""
40""")
41
42 bbvars = get_bb_vars(['bindir', 'sysconfdir', 'localstatedir',
43 'DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'],
44 target='container-test-image')
45 expected_files = [
46 './',
47 '.{bindir}/theapp',
48 '.{sysconfdir}/default/',
49 '.{sysconfdir}/default/postinst',
50 '.{sysconfdir}/ld.so.cache',
51 '.{sysconfdir}/timestamp',
52 '.{sysconfdir}/version',
53 './run/',
54 '.{localstatedir}/cache/',
55 '.{localstatedir}/cache/ldconfig/',
56 '.{localstatedir}/cache/ldconfig/aux-cache',
57 '.{localstatedir}/cache/opkg/',
58 '.{localstatedir}/lib/',
59 '.{localstatedir}/lib/opkg/'
60 ]
61
62 expected_files = [ x.format(bindir=bbvars['bindir'],
63 sysconfdir=bbvars['sysconfdir'],
64 localstatedir=bbvars['localstatedir'])
65 for x in expected_files ]
66
67 # Since tar lists all directories individually, make sure each element
68 # from bindir, sysconfdir, etc is added
69 expected_files += get_each_path_part(bbvars['bindir'])
70 expected_files += get_each_path_part(bbvars['sysconfdir'])
71 expected_files += get_each_path_part(bbvars['localstatedir'])
72
73 expected_files = sorted(expected_files)
74
75 # Build the image of course
76 bitbake('container-test-image')
77
78 image = os.path.join(bbvars['DEPLOY_DIR_IMAGE'],
79 bbvars['IMAGE_LINK_NAME'] + '.tar.bz2')
80
81 # Ensure the files in the image are what we expect
82 result = runCmd("tar tf {} | sort".format(image), shell=True)
83 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 @@
1import os
2import re
3import shutil
4import tempfile
5import glob
6import fnmatch
7
8import oeqa.utils.ftools as ftools
9from oeqa.selftest.case import OESelftestTestCase
10from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer
11from oeqa.utils.commands import get_bb_vars, runqemu, get_test_layer
12from oeqa.core.decorator.oeid import OETestID
13
14class DevtoolBase(OESelftestTestCase):
15
16 def _test_recipe_contents(self, recipefile, checkvars, checkinherits):
17 with open(recipefile, 'r') as f:
18 invar = None
19 invalue = None
20 for line in f:
21 var = None
22 if invar:
23 value = line.strip().strip('"')
24 if value.endswith('\\'):
25 invalue += ' ' + value[:-1].strip()
26 continue
27 else:
28 invalue += ' ' + value.strip()
29 var = invar
30 value = invalue
31 invar = None
32 elif '=' in line:
33 splitline = line.split('=', 1)
34 var = splitline[0].rstrip()
35 value = splitline[1].strip().strip('"')
36 if value.endswith('\\'):
37 invalue = value[:-1].strip()
38 invar = var
39 continue
40 elif line.startswith('inherit '):
41 inherits = line.split()[1:]
42
43 if var and var in checkvars:
44 needvalue = checkvars.pop(var)
45 if needvalue is None:
46 self.fail('Variable %s should not appear in recipe, but value is being set to "%s"' % (var, value))
47 if isinstance(needvalue, set):
48 if var == 'LICENSE':
49 value = set(value.split(' & '))
50 else:
51 value = set(value.split())
52 self.assertEqual(value, needvalue, 'values for %s do not match' % var)
53
54
55 missingvars = {}
56 for var, value in checkvars.items():
57 if value is not None:
58 missingvars[var] = value
59 self.assertEqual(missingvars, {}, 'Some expected variables not found in recipe: %s' % checkvars)
60
61 for inherit in checkinherits:
62 self.assertIn(inherit, inherits, 'Missing inherit of %s' % inherit)
63
64 def _check_bbappend(self, testrecipe, recipefile, appenddir):
65 result = runCmd('bitbake-layers show-appends', cwd=self.builddir)
66 resultlines = result.output.splitlines()
67 inrecipe = False
68 bbappends = []
69 bbappendfile = None
70 for line in resultlines:
71 if inrecipe:
72 if line.startswith(' '):
73 bbappends.append(line.strip())
74 else:
75 break
76 elif line == '%s:' % os.path.basename(recipefile):
77 inrecipe = True
78 self.assertLessEqual(len(bbappends), 2, '%s recipe is being bbappended by another layer - bbappends found:\n %s' % (testrecipe, '\n '.join(bbappends)))
79 for bbappend in bbappends:
80 if bbappend.startswith(appenddir):
81 bbappendfile = bbappend
82 break
83 else:
84 self.fail('bbappend for recipe %s does not seem to be created in test layer' % testrecipe)
85 return bbappendfile
86
87 def _create_temp_layer(self, templayerdir, addlayer, templayername, priority=999, recipepathspec='recipes-*/*'):
88 create_temp_layer(templayerdir, templayername, priority, recipepathspec)
89 if addlayer:
90 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
91 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
92
93 def _process_ls_output(self, output):
94 """
95 Convert ls -l output to a format we can reasonably compare from one context
96 to another (e.g. from host to target)
97 """
98 filelist = []
99 for line in output.splitlines():
100 splitline = line.split()
101 if len(splitline) < 8:
102 self.fail('_process_ls_output: invalid output line: %s' % line)
103 # Remove trailing . on perms
104 splitline[0] = splitline[0].rstrip('.')
105 # Remove leading . on paths
106 splitline[-1] = splitline[-1].lstrip('.')
107 # Drop fields we don't want to compare
108 del splitline[7]
109 del splitline[6]
110 del splitline[5]
111 del splitline[4]
112 del splitline[1]
113 filelist.append(' '.join(splitline))
114 return filelist
115
116
117class DevtoolTests(DevtoolBase):
118
119 @classmethod
120 def setUpClass(cls):
121 super(DevtoolTests, cls).setUpClass()
122 bb_vars = get_bb_vars(['TOPDIR', 'SSTATE_DIR'])
123 cls.original_sstate = bb_vars['SSTATE_DIR']
124 cls.devtool_sstate = os.path.join(bb_vars['TOPDIR'], 'sstate_devtool')
125 cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate
126 cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n'
127 % cls.original_sstate)
128
129 @classmethod
130 def tearDownClass(cls):
131 cls.logger.debug('Deleting devtool sstate cache on %s' % cls.devtool_sstate)
132 runCmd('rm -rf %s' % cls.devtool_sstate)
133 super(DevtoolTests, cls).tearDownClass()
134
135 def setUp(self):
136 """Test case setup function"""
137 super(DevtoolTests, self).setUp()
138 self.workspacedir = os.path.join(self.builddir, 'workspace')
139 self.assertTrue(not os.path.exists(self.workspacedir),
140 'This test cannot be run with a workspace directory '
141 'under the build directory')
142 self.append_config(self.sstate_conf)
143
144 def _check_src_repo(self, repo_dir):
145 """Check srctree git repository"""
146 self.assertTrue(os.path.isdir(os.path.join(repo_dir, '.git')),
147 'git repository for external source tree not found')
148 result = runCmd('git status --porcelain', cwd=repo_dir)
149 self.assertEqual(result.output.strip(), "",
150 'Created git repo is not clean')
151 result = runCmd('git symbolic-ref HEAD', cwd=repo_dir)
152 self.assertEqual(result.output.strip(), "refs/heads/devtool",
153 'Wrong branch in git repo')
154
155 def _check_repo_status(self, repo_dir, expected_status):
156 """Check the worktree status of a repository"""
157 result = runCmd('git status . --porcelain',
158 cwd=repo_dir)
159 for line in result.output.splitlines():
160 for ind, (f_status, fn_re) in enumerate(expected_status):
161 if re.match(fn_re, line[3:]):
162 if f_status != line[:2]:
163 self.fail('Unexpected status in line: %s' % line)
164 expected_status.pop(ind)
165 break
166 else:
167 self.fail('Unexpected modified file in line: %s' % line)
168 if expected_status:
169 self.fail('Missing file changes: %s' % expected_status)
170
171 @OETestID(1158)
172 def test_create_workspace(self):
173 # Check preconditions
174 result = runCmd('bitbake-layers show-layers')
175 self.assertTrue('/workspace' not in result.output, 'This test cannot be run with a workspace layer in bblayers.conf')
176 # Try creating a workspace layer with a specific path
177 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
178 self.track_for_cleanup(tempdir)
179 result = runCmd('devtool create-workspace %s' % tempdir)
180 self.assertTrue(os.path.isfile(os.path.join(tempdir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
181 result = runCmd('bitbake-layers show-layers')
182 self.assertIn(tempdir, result.output)
183 # Try creating a workspace layer with the default path
184 self.track_for_cleanup(self.workspacedir)
185 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
186 result = runCmd('devtool create-workspace')
187 self.assertTrue(os.path.isfile(os.path.join(self.workspacedir, 'conf', 'layer.conf')), msg = "No workspace created. devtool output: %s " % result.output)
188 result = runCmd('bitbake-layers show-layers')
189 self.assertNotIn(tempdir, result.output)
190 self.assertIn(self.workspacedir, result.output)
191
192 @OETestID(1159)
193 def test_devtool_add(self):
194 # Fetch source
195 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
196 self.track_for_cleanup(tempdir)
197 url = 'http://www.ivarch.com/programs/sources/pv-1.5.3.tar.bz2'
198 result = runCmd('wget %s' % url, cwd=tempdir)
199 result = runCmd('tar xfv pv-1.5.3.tar.bz2', cwd=tempdir)
200 srcdir = os.path.join(tempdir, 'pv-1.5.3')
201 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
202 # Test devtool add
203 self.track_for_cleanup(self.workspacedir)
204 self.add_command_to_tearDown('bitbake -c cleansstate pv')
205 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
206 result = runCmd('devtool add pv %s' % srcdir)
207 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
208 # Test devtool status
209 result = runCmd('devtool status')
210 self.assertIn('pv', result.output)
211 self.assertIn(srcdir, result.output)
212 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
213 bitbake('pv -c cleansstate')
214 # Test devtool build
215 result = runCmd('devtool build pv')
216 bb_vars = get_bb_vars(['D', 'bindir'], 'pv')
217 installdir = bb_vars['D']
218 self.assertTrue(installdir, 'Could not query installdir variable')
219 bindir = bb_vars['bindir']
220 self.assertTrue(bindir, 'Could not query bindir variable')
221 if bindir[0] == '/':
222 bindir = bindir[1:]
223 self.assertTrue(os.path.isfile(os.path.join(installdir, bindir, 'pv')), 'pv binary not found in D')
224
225 @OETestID(1423)
226 def test_devtool_add_git_local(self):
227 # Fetch source from a remote URL, but do it outside of devtool
228 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
229 self.track_for_cleanup(tempdir)
230 pn = 'dbus-wait'
231 srcrev = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
232 # We choose an https:// git URL here to check rewriting the URL works
233 url = 'https://git.yoctoproject.org/git/dbus-wait'
234 # Force fetching to "noname" subdir so we verify we're picking up the name from autoconf
235 # instead of the directory name
236 result = runCmd('git clone %s noname' % url, cwd=tempdir)
237 srcdir = os.path.join(tempdir, 'noname')
238 result = runCmd('git reset --hard %s' % srcrev, cwd=srcdir)
239 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure.ac')), 'Unable to find configure script in source directory')
240 # Test devtool add
241 self.track_for_cleanup(self.workspacedir)
242 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
243 # Don't specify a name since we should be able to auto-detect it
244 result = runCmd('devtool add %s' % srcdir)
245 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
246 # Check the recipe name is correct
247 recipefile = get_bb_var('FILE', pn)
248 self.assertIn('%s_git.bb' % pn, recipefile, 'Recipe file incorrectly named')
249 self.assertIn(recipefile, result.output)
250 # Test devtool status
251 result = runCmd('devtool status')
252 self.assertIn(pn, result.output)
253 self.assertIn(srcdir, result.output)
254 self.assertIn(recipefile, result.output)
255 checkvars = {}
256 checkvars['LICENSE'] = 'GPLv2'
257 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'
258 checkvars['S'] = '${WORKDIR}/git'
259 checkvars['PV'] = '0.1+git${SRCPV}'
260 checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https'
261 checkvars['SRCREV'] = srcrev
262 checkvars['DEPENDS'] = set(['dbus'])
263 self._test_recipe_contents(recipefile, checkvars, [])
264
265 @OETestID(1162)
266 def test_devtool_add_library(self):
267 # Fetch source
268 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
269 self.track_for_cleanup(tempdir)
270 version = '1.1'
271 url = 'https://www.intra2net.com/en/developer/libftdi/download/libftdi1-%s.tar.bz2' % version
272 result = runCmd('wget %s' % url, cwd=tempdir)
273 result = runCmd('tar xfv libftdi1-%s.tar.bz2' % version, cwd=tempdir)
274 srcdir = os.path.join(tempdir, 'libftdi1-%s' % version)
275 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'CMakeLists.txt')), 'Unable to find CMakeLists.txt in source directory')
276 # Test devtool add (and use -V so we test that too)
277 self.track_for_cleanup(self.workspacedir)
278 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
279 result = runCmd('devtool add libftdi %s -V %s' % (srcdir, version))
280 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
281 # Test devtool status
282 result = runCmd('devtool status')
283 self.assertIn('libftdi', result.output)
284 self.assertIn(srcdir, result.output)
285 # Clean up anything in the workdir/sysroot/sstate cache (have to do this *after* devtool add since the recipe only exists then)
286 bitbake('libftdi -c cleansstate')
287 # libftdi's python/CMakeLists.txt is a bit broken, so let's just disable it
288 # There's also the matter of it installing cmake files to a path we don't
289 # normally cover, which triggers the installed-vs-shipped QA test we have
290 # within do_package
291 recipefile = '%s/recipes/libftdi/libftdi_%s.bb' % (self.workspacedir, version)
292 result = runCmd('recipetool setvar %s EXTRA_OECMAKE -- \'-DPYTHON_BINDINGS=OFF -DLIBFTDI_CMAKE_CONFIG_DIR=${datadir}/cmake/Modules\'' % recipefile)
293 with open(recipefile, 'a') as f:
294 f.write('\nFILES_${PN}-dev += "${datadir}/cmake/Modules"\n')
295 # We don't have the ability to pick up this dependency automatically yet...
296 f.write('\nDEPENDS += "libusb1"\n')
297 f.write('\nTESTLIBOUTPUT = "${COMPONENTS_DIR}/${TUNE_PKGARCH}/${PN}/${libdir}"\n')
298 # Test devtool build
299 result = runCmd('devtool build libftdi')
300 bb_vars = get_bb_vars(['TESTLIBOUTPUT', 'STAMP'], 'libftdi')
301 staging_libdir = bb_vars['TESTLIBOUTPUT']
302 self.assertTrue(staging_libdir, 'Could not query TESTLIBOUTPUT variable')
303 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)
304 # Test devtool reset
305 stampprefix = bb_vars['STAMP']
306 result = runCmd('devtool reset libftdi')
307 result = runCmd('devtool status')
308 self.assertNotIn('libftdi', result.output)
309 self.assertTrue(stampprefix, 'Unable to get STAMP value for recipe libftdi')
310 matches = glob.glob(stampprefix + '*')
311 self.assertFalse(matches, 'Stamp files exist for recipe libftdi that should have been cleaned')
312 self.assertFalse(os.path.isfile(os.path.join(staging_libdir, 'libftdi1.so.2.1.0')), 'libftdi binary still found in STAGING_LIBDIR after cleaning')
313
314 @OETestID(1160)
315 def test_devtool_add_fetch(self):
316 # Fetch source
317 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
318 self.track_for_cleanup(tempdir)
319 testver = '0.23'
320 url = 'https://pypi.python.org/packages/source/M/MarkupSafe/MarkupSafe-%s.tar.gz' % testver
321 testrecipe = 'python-markupsafe'
322 srcdir = os.path.join(tempdir, testrecipe)
323 # Test devtool add
324 self.track_for_cleanup(self.workspacedir)
325 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
326 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
327 result = runCmd('devtool add %s %s -f %s' % (testrecipe, srcdir, url))
328 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created. %s' % result.output)
329 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
330 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
331 # Test devtool status
332 result = runCmd('devtool status')
333 self.assertIn(testrecipe, result.output)
334 self.assertIn(srcdir, result.output)
335 # Check recipe
336 recipefile = get_bb_var('FILE', testrecipe)
337 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
338 checkvars = {}
339 checkvars['S'] = '${WORKDIR}/MarkupSafe-${PV}'
340 checkvars['SRC_URI'] = url.replace(testver, '${PV}')
341 self._test_recipe_contents(recipefile, checkvars, [])
342 # Try with version specified
343 result = runCmd('devtool reset -n %s' % testrecipe)
344 shutil.rmtree(srcdir)
345 fakever = '1.9'
346 result = runCmd('devtool add %s %s -f %s -V %s' % (testrecipe, srcdir, url, fakever))
347 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'setup.py')), 'Unable to find setup.py in source directory')
348 # Test devtool status
349 result = runCmd('devtool status')
350 self.assertIn(testrecipe, result.output)
351 self.assertIn(srcdir, result.output)
352 # Check recipe
353 recipefile = get_bb_var('FILE', testrecipe)
354 self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named')
355 checkvars = {}
356 checkvars['S'] = '${WORKDIR}/MarkupSafe-%s' % testver
357 checkvars['SRC_URI'] = url
358 self._test_recipe_contents(recipefile, checkvars, [])
359
360 @OETestID(1161)
361 def test_devtool_add_fetch_git(self):
362 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
363 self.track_for_cleanup(tempdir)
364 url = 'gitsm://git.yoctoproject.org/mraa'
365 checkrev = 'ae127b19a50aa54255e4330ccfdd9a5d058e581d'
366 testrecipe = 'mraa'
367 srcdir = os.path.join(tempdir, testrecipe)
368 # Test devtool add
369 self.track_for_cleanup(self.workspacedir)
370 self.add_command_to_tearDown('bitbake -c cleansstate %s' % testrecipe)
371 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
372 result = runCmd('devtool add %s %s -a -f %s' % (testrecipe, srcdir, url))
373 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created: %s' % result.output)
374 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
375 # Test devtool status
376 result = runCmd('devtool status')
377 self.assertIn(testrecipe, result.output)
378 self.assertIn(srcdir, result.output)
379 # Check recipe
380 recipefile = get_bb_var('FILE', testrecipe)
381 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
382 checkvars = {}
383 checkvars['S'] = '${WORKDIR}/git'
384 checkvars['PV'] = '1.0+git${SRCPV}'
385 checkvars['SRC_URI'] = url
386 checkvars['SRCREV'] = '${AUTOREV}'
387 self._test_recipe_contents(recipefile, checkvars, [])
388 # Try with revision and version specified
389 result = runCmd('devtool reset -n %s' % testrecipe)
390 shutil.rmtree(srcdir)
391 url_rev = '%s;rev=%s' % (url, checkrev)
392 result = runCmd('devtool add %s %s -f "%s" -V 1.5' % (testrecipe, srcdir, url_rev))
393 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'imraa', 'imraa.c')), 'Unable to find imraa/imraa.c in source directory')
394 # Test devtool status
395 result = runCmd('devtool status')
396 self.assertIn(testrecipe, result.output)
397 self.assertIn(srcdir, result.output)
398 # Check recipe
399 recipefile = get_bb_var('FILE', testrecipe)
400 self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named')
401 checkvars = {}
402 checkvars['S'] = '${WORKDIR}/git'
403 checkvars['PV'] = '1.5+git${SRCPV}'
404 checkvars['SRC_URI'] = url
405 checkvars['SRCREV'] = checkrev
406 self._test_recipe_contents(recipefile, checkvars, [])
407
408 @OETestID(1391)
409 def test_devtool_add_fetch_simple(self):
410 # Fetch source from a remote URL, auto-detecting name
411 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
412 self.track_for_cleanup(tempdir)
413 testver = '1.6.0'
414 url = 'http://www.ivarch.com/programs/sources/pv-%s.tar.bz2' % testver
415 testrecipe = 'pv'
416 srcdir = os.path.join(self.workspacedir, 'sources', testrecipe)
417 # Test devtool add
418 self.track_for_cleanup(self.workspacedir)
419 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
420 result = runCmd('devtool add %s' % url)
421 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created. %s' % result.output)
422 self.assertTrue(os.path.isfile(os.path.join(srcdir, 'configure')), 'Unable to find configure script in source directory')
423 self.assertTrue(os.path.isdir(os.path.join(srcdir, '.git')), 'git repository for external source tree was not created')
424 # Test devtool status
425 result = runCmd('devtool status')
426 self.assertIn(testrecipe, result.output)
427 self.assertIn(srcdir, result.output)
428 # Check recipe
429 recipefile = get_bb_var('FILE', testrecipe)
430 self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named')
431 checkvars = {}
432 checkvars['S'] = None
433 checkvars['SRC_URI'] = url.replace(testver, '${PV}')
434 self._test_recipe_contents(recipefile, checkvars, [])
435
436 @OETestID(1164)
437 def test_devtool_modify(self):
438 import oe.path
439
440 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
441 self.track_for_cleanup(tempdir)
442 self.track_for_cleanup(self.workspacedir)
443 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
444 self.add_command_to_tearDown('bitbake -c clean mdadm')
445 result = runCmd('devtool modify mdadm -x %s' % tempdir)
446 self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
447 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
448 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'mdadm_*.bbappend'))
449 self.assertTrue(matches, 'bbappend not created %s' % result.output)
450
451 # Test devtool status
452 result = runCmd('devtool status')
453 self.assertIn('mdadm', result.output)
454 self.assertIn(tempdir, result.output)
455 self._check_src_repo(tempdir)
456
457 bitbake('mdadm -C unpack')
458
459 def check_line(checkfile, expected, message, present=True):
460 # Check for $expected, on a line on its own, in checkfile.
461 with open(checkfile, 'r') as f:
462 if present:
463 self.assertIn(expected + '\n', f, message)
464 else:
465 self.assertNotIn(expected + '\n', f, message)
466
467 modfile = os.path.join(tempdir, 'mdadm.8.in')
468 bb_vars = get_bb_vars(['PKGD', 'mandir'], 'mdadm')
469 pkgd = bb_vars['PKGD']
470 self.assertTrue(pkgd, 'Could not query PKGD variable')
471 mandir = bb_vars['mandir']
472 self.assertTrue(mandir, 'Could not query mandir variable')
473 manfile = oe.path.join(pkgd, mandir, 'man8', 'mdadm.8')
474
475 check_line(modfile, 'Linux Software RAID', 'Could not find initial string')
476 check_line(modfile, 'antique pin sardine', 'Unexpectedly found replacement string', present=False)
477
478 result = runCmd("sed -i 's!^Linux Software RAID$!antique pin sardine!' %s" % modfile)
479 check_line(modfile, 'antique pin sardine', 'mdadm.8.in file not modified (sed failed)')
480
481 bitbake('mdadm -c package')
482 check_line(manfile, 'antique pin sardine', 'man file not modified. man searched file path: %s' % manfile)
483
484 result = runCmd('git checkout -- %s' % modfile, cwd=tempdir)
485 check_line(modfile, 'Linux Software RAID', 'man .in file not restored (git failed)')
486
487 bitbake('mdadm -c package')
488 check_line(manfile, 'Linux Software RAID', 'man file not updated. man searched file path: %s' % manfile)
489
490 result = runCmd('devtool reset mdadm')
491 result = runCmd('devtool status')
492 self.assertNotIn('mdadm', result.output)
493
494 def test_devtool_buildclean(self):
495 def assertFile(path, *paths):
496 f = os.path.join(path, *paths)
497 self.assertTrue(os.path.exists(f), "%r does not exist" % f)
498 def assertNoFile(path, *paths):
499 f = os.path.join(path, *paths)
500 self.assertFalse(os.path.exists(os.path.join(f)), "%r exists" % f)
501
502 # Clean up anything in the workdir/sysroot/sstate cache
503 bitbake('mdadm m4 -c cleansstate')
504 # Try modifying a recipe
505 tempdir_mdadm = tempfile.mkdtemp(prefix='devtoolqa')
506 tempdir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
507 builddir_m4 = tempfile.mkdtemp(prefix='devtoolqa')
508 self.track_for_cleanup(tempdir_mdadm)
509 self.track_for_cleanup(tempdir_m4)
510 self.track_for_cleanup(builddir_m4)
511 self.track_for_cleanup(self.workspacedir)
512 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
513 self.add_command_to_tearDown('bitbake -c clean mdadm m4')
514 self.write_recipeinc('m4', 'EXTERNALSRC_BUILD = "%s"\ndo_clean() {\n\t:\n}\n' % builddir_m4)
515 try:
516 runCmd('devtool modify mdadm -x %s' % tempdir_mdadm)
517 runCmd('devtool modify m4 -x %s' % tempdir_m4)
518 assertNoFile(tempdir_mdadm, 'mdadm')
519 assertNoFile(builddir_m4, 'src/m4')
520 result = bitbake('m4 -e')
521 result = bitbake('mdadm m4 -c compile')
522 self.assertEqual(result.status, 0)
523 assertFile(tempdir_mdadm, 'mdadm')
524 assertFile(builddir_m4, 'src/m4')
525 # Check that buildclean task exists and does call make clean
526 bitbake('mdadm m4 -c buildclean')
527 assertNoFile(tempdir_mdadm, 'mdadm')
528 assertNoFile(builddir_m4, 'src/m4')
529 bitbake('mdadm m4 -c compile')
530 assertFile(tempdir_mdadm, 'mdadm')
531 assertFile(builddir_m4, 'src/m4')
532 bitbake('mdadm m4 -c clean')
533 # Check that buildclean task is run before clean for B == S
534 assertNoFile(tempdir_mdadm, 'mdadm')
535 # Check that buildclean task is not run before clean for B != S
536 assertFile(builddir_m4, 'src/m4')
537 finally:
538 self.delete_recipeinc('m4')
539
540 @OETestID(1166)
541 def test_devtool_modify_invalid(self):
542 # Try modifying some recipes
543 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
544 self.track_for_cleanup(tempdir)
545 self.track_for_cleanup(self.workspacedir)
546 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
547
548 testrecipes = 'perf kernel-devsrc package-index core-image-minimal meta-toolchain packagegroup-core-sdk meta-ide-support'.split()
549 # Find actual name of gcc-source since it now includes the version - crude, but good enough for this purpose
550 result = runCmd('bitbake-layers show-recipes gcc-source*')
551 for line in result.output.splitlines():
552 # just match those lines that contain a real target
553 m = re.match('(?P<recipe>^[a-zA-Z0-9.-]+)(?P<colon>:$)', line)
554 if m:
555 testrecipes.append(m.group('recipe'))
556 for testrecipe in testrecipes:
557 # Check it's a valid recipe
558 bitbake('%s -e' % testrecipe)
559 # devtool extract should fail
560 result = runCmd('devtool extract %s %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
561 self.assertNotEqual(result.status, 0, 'devtool extract on %s should have failed. devtool output: %s' % (testrecipe, result.output))
562 self.assertNotIn('Fetching ', result.output, 'devtool extract on %s should have errored out before trying to fetch' % testrecipe)
563 self.assertIn('ERROR: ', result.output, 'devtool extract on %s should have given an ERROR' % testrecipe)
564 # devtool modify should fail
565 result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)), ignore_status=True)
566 self.assertNotEqual(result.status, 0, 'devtool modify on %s should have failed. devtool output: %s' % (testrecipe, result.output))
567 self.assertIn('ERROR: ', result.output, 'devtool modify on %s should have given an ERROR' % testrecipe)
568
569 @OETestID(1365)
570 def test_devtool_modify_native(self):
571 # Check preconditions
572 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
573 # Try modifying some recipes
574 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
575 self.track_for_cleanup(tempdir)
576 self.track_for_cleanup(self.workspacedir)
577 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
578
579 bbclassextended = False
580 inheritnative = False
581 testrecipes = 'mtools-native apt-native desktop-file-utils-native'.split()
582 for testrecipe in testrecipes:
583 checkextend = 'native' in (get_bb_var('BBCLASSEXTEND', testrecipe) or '').split()
584 if not bbclassextended:
585 bbclassextended = checkextend
586 if not inheritnative:
587 inheritnative = not checkextend
588 result = runCmd('devtool modify %s -x %s' % (testrecipe, os.path.join(tempdir, testrecipe)))
589 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool modify output: %s' % result.output)
590 result = runCmd('devtool build %s' % testrecipe)
591 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool build output: %s' % result.output)
592 result = runCmd('devtool reset %s' % testrecipe)
593 self.assertNotIn('ERROR: ', result.output, 'ERROR in devtool reset output: %s' % result.output)
594
595 self.assertTrue(bbclassextended, 'None of these recipes are BBCLASSEXTENDed to native - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
596 self.assertTrue(inheritnative, 'None of these recipes do "inherit native" - need to adjust testrecipes list: %s' % ', '.join(testrecipes))
597
598
599 @OETestID(1165)
600 def test_devtool_modify_git(self):
601 # Check preconditions
602 testrecipe = 'mkelfimage'
603 src_uri = get_bb_var('SRC_URI', testrecipe)
604 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
605 # Clean up anything in the workdir/sysroot/sstate cache
606 bitbake('%s -c cleansstate' % testrecipe)
607 # Try modifying a recipe
608 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
609 self.track_for_cleanup(tempdir)
610 self.track_for_cleanup(self.workspacedir)
611 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
612 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
613 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
614 self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
615 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created. devtool output: %s' % result.output)
616 matches = glob.glob(os.path.join(self.workspacedir, 'appends', 'mkelfimage_*.bbappend'))
617 self.assertTrue(matches, 'bbappend not created')
618 # Test devtool status
619 result = runCmd('devtool status')
620 self.assertIn(testrecipe, result.output)
621 self.assertIn(tempdir, result.output)
622 # Check git repo
623 self._check_src_repo(tempdir)
624 # Try building
625 bitbake(testrecipe)
626
627 @OETestID(1167)
628 def test_devtool_modify_localfiles(self):
629 # Check preconditions
630 testrecipe = 'lighttpd'
631 src_uri = (get_bb_var('SRC_URI', testrecipe) or '').split()
632 foundlocal = False
633 for item in src_uri:
634 if item.startswith('file://') and '.patch' not in item:
635 foundlocal = True
636 break
637 self.assertTrue(foundlocal, 'This test expects the %s recipe to fetch local files and it seems that it no longer does' % testrecipe)
638 # Clean up anything in the workdir/sysroot/sstate cache
639 bitbake('%s -c cleansstate' % testrecipe)
640 # Try modifying a recipe
641 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
642 self.track_for_cleanup(tempdir)
643 self.track_for_cleanup(self.workspacedir)
644 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
645 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
646 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
647 self.assertTrue(os.path.exists(os.path.join(tempdir, 'configure.ac')), 'Extracted source could not be found')
648 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
649 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe))
650 self.assertTrue(matches, 'bbappend not created')
651 # Test devtool status
652 result = runCmd('devtool status')
653 self.assertIn(testrecipe, result.output)
654 self.assertIn(tempdir, result.output)
655 # Try building
656 bitbake(testrecipe)
657
658 @OETestID(1378)
659 def test_devtool_modify_virtual(self):
660 # Try modifying a virtual recipe
661 virtrecipe = 'virtual/make'
662 realrecipe = 'make'
663 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
664 self.track_for_cleanup(tempdir)
665 self.track_for_cleanup(self.workspacedir)
666 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
667 result = runCmd('devtool modify %s -x %s' % (virtrecipe, tempdir))
668 self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile.am')), 'Extracted source could not be found')
669 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'conf', 'layer.conf')), 'Workspace directory not created')
670 matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % realrecipe))
671 self.assertTrue(matches, 'bbappend not created %s' % result.output)
672 # Test devtool status
673 result = runCmd('devtool status')
674 self.assertNotIn(virtrecipe, result.output)
675 self.assertIn(realrecipe, result.output)
676 # Check git repo
677 self._check_src_repo(tempdir)
678 # This is probably sufficient
679
680
681 @OETestID(1169)
682 def test_devtool_update_recipe(self):
683 # Check preconditions
684 testrecipe = 'minicom'
685 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
686 recipefile = bb_vars['FILE']
687 src_uri = bb_vars['SRC_URI']
688 self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
689 self._check_repo_status(os.path.dirname(recipefile), [])
690 # First, modify a recipe
691 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
692 self.track_for_cleanup(tempdir)
693 self.track_for_cleanup(self.workspacedir)
694 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
695 # (don't bother with cleaning the recipe on teardown, we won't be building it)
696 # We don't use -x here so that we test the behaviour of devtool modify without it
697 result = runCmd('devtool modify %s %s' % (testrecipe, tempdir))
698 # Check git repo
699 self._check_src_repo(tempdir)
700 # Add a couple of commits
701 # FIXME: this only tests adding, need to also test update and remove
702 result = runCmd('echo "Additional line" >> README', cwd=tempdir)
703 result = runCmd('git commit -a -m "Change the README"', cwd=tempdir)
704 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
705 result = runCmd('git add devtool-new-file', cwd=tempdir)
706 result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
707 self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
708 result = runCmd('devtool update-recipe %s' % testrecipe)
709 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
710 ('??', '.*/0001-Change-the-README.patch$'),
711 ('??', '.*/0002-Add-a-new-file.patch$')]
712 self._check_repo_status(os.path.dirname(recipefile), expected_status)
713
714 @OETestID(1172)
715 def test_devtool_update_recipe_git(self):
716 # Check preconditions
717 testrecipe = 'mtd-utils'
718 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
719 recipefile = bb_vars['FILE']
720 src_uri = bb_vars['SRC_URI']
721 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
722 patches = []
723 for entry in src_uri.split():
724 if entry.startswith('file://') and entry.endswith('.patch'):
725 patches.append(entry[7:].split(';')[0])
726 self.assertGreater(len(patches), 0, 'The %s recipe does not appear to contain any patches, so this test will not be effective' % testrecipe)
727 self._check_repo_status(os.path.dirname(recipefile), [])
728 # First, modify a recipe
729 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
730 self.track_for_cleanup(tempdir)
731 self.track_for_cleanup(self.workspacedir)
732 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
733 # (don't bother with cleaning the recipe on teardown, we won't be building it)
734 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
735 # Check git repo
736 self._check_src_repo(tempdir)
737 # Add a couple of commits
738 # FIXME: this only tests adding, need to also test update and remove
739 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempdir)
740 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempdir)
741 result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir)
742 result = runCmd('git add devtool-new-file', cwd=tempdir)
743 result = runCmd('git commit -m "Add a new file"', cwd=tempdir)
744 self.add_command_to_tearDown('cd %s; rm -rf %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
745 result = runCmd('devtool update-recipe -m srcrev %s' % testrecipe)
746 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile))] + \
747 [(' D', '.*/%s$' % patch) for patch in patches]
748 self._check_repo_status(os.path.dirname(recipefile), expected_status)
749
750 result = runCmd('git diff %s' % os.path.basename(recipefile), cwd=os.path.dirname(recipefile))
751 addlines = ['SRCREV = ".*"', 'SRC_URI = "git://git.infradead.org/mtd-utils.git"']
752 srcurilines = src_uri.split()
753 srcurilines[0] = 'SRC_URI = "' + srcurilines[0]
754 srcurilines.append('"')
755 removelines = ['SRCREV = ".*"'] + srcurilines
756 for line in result.output.splitlines():
757 if line.startswith('+++') or line.startswith('---'):
758 continue
759 elif line.startswith('+'):
760 matched = False
761 for item in addlines:
762 if re.match(item, line[1:].strip()):
763 matched = True
764 break
765 self.assertTrue(matched, 'Unexpected diff add line: %s' % line)
766 elif line.startswith('-'):
767 matched = False
768 for item in removelines:
769 if re.match(item, line[1:].strip()):
770 matched = True
771 break
772 self.assertTrue(matched, 'Unexpected diff remove line: %s' % line)
773 # Now try with auto mode
774 runCmd('cd %s; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, os.path.basename(recipefile)))
775 result = runCmd('devtool update-recipe %s' % testrecipe)
776 result = runCmd('git rev-parse --show-toplevel', cwd=os.path.dirname(recipefile))
777 topleveldir = result.output.strip()
778 relpatchpath = os.path.join(os.path.relpath(os.path.dirname(recipefile), topleveldir), testrecipe)
779 expected_status = [(' M', os.path.relpath(recipefile, topleveldir)),
780 ('??', '%s/0001-Change-the-Makefile.patch' % relpatchpath),
781 ('??', '%s/0002-Add-a-new-file.patch' % relpatchpath)]
782 self._check_repo_status(os.path.dirname(recipefile), expected_status)
783
784 @OETestID(1170)
785 def test_devtool_update_recipe_append(self):
786 # Check preconditions
787 testrecipe = 'mdadm'
788 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
789 recipefile = bb_vars['FILE']
790 src_uri = bb_vars['SRC_URI']
791 self.assertNotIn('git://', src_uri, 'This test expects the %s recipe to NOT be a git recipe' % testrecipe)
792 self._check_repo_status(os.path.dirname(recipefile), [])
793 # First, modify a recipe
794 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
795 tempsrcdir = os.path.join(tempdir, 'source')
796 templayerdir = os.path.join(tempdir, 'layer')
797 self.track_for_cleanup(tempdir)
798 self.track_for_cleanup(self.workspacedir)
799 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
800 # (don't bother with cleaning the recipe on teardown, we won't be building it)
801 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
802 # Check git repo
803 self._check_src_repo(tempsrcdir)
804 # Add a commit
805 result = runCmd("sed 's!\\(#define VERSION\\W*\"[^\"]*\\)\"!\\1-custom\"!' -i ReadMe.c", cwd=tempsrcdir)
806 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
807 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
808 # Create a temporary layer and add it to bblayers.conf
809 self._create_temp_layer(templayerdir, True, 'selftestupdaterecipe')
810 # Create the bbappend
811 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
812 self.assertNotIn('WARNING:', result.output)
813 # Check recipe is still clean
814 self._check_repo_status(os.path.dirname(recipefile), [])
815 # Check bbappend was created
816 splitpath = os.path.dirname(recipefile).split(os.sep)
817 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
818 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
819 patchfile = os.path.join(appenddir, testrecipe, '0001-Add-our-custom-version.patch')
820 self.assertTrue(os.path.exists(patchfile), 'Patch file not created')
821
822 # Check bbappend contents
823 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
824 '\n',
825 'SRC_URI += "file://0001-Add-our-custom-version.patch"\n',
826 '\n']
827 with open(bbappendfile, 'r') as f:
828 self.assertEqual(expectedlines, f.readlines())
829
830 # Check we can run it again and bbappend isn't modified
831 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
832 with open(bbappendfile, 'r') as f:
833 self.assertEqual(expectedlines, f.readlines())
834 # Drop new commit and check patch gets deleted
835 result = runCmd('git reset HEAD^', cwd=tempsrcdir)
836 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
837 self.assertFalse(os.path.exists(patchfile), 'Patch file not deleted')
838 expectedlines2 = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
839 '\n']
840 with open(bbappendfile, 'r') as f:
841 self.assertEqual(expectedlines2, f.readlines())
842 # Put commit back and check we can run it if layer isn't in bblayers.conf
843 os.remove(bbappendfile)
844 result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir)
845 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
846 result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir))
847 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
848 self.assertTrue(os.path.exists(patchfile), 'Patch file not created (with disabled layer)')
849 with open(bbappendfile, 'r') as f:
850 self.assertEqual(expectedlines, f.readlines())
851 # Deleting isn't expected to work under these circumstances
852
853 @OETestID(1171)
854 def test_devtool_update_recipe_append_git(self):
855 # Check preconditions
856 testrecipe = 'mtd-utils'
857 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
858 recipefile = bb_vars['FILE']
859 src_uri = bb_vars['SRC_URI']
860 self.assertIn('git://', src_uri, 'This test expects the %s recipe to be a git recipe' % testrecipe)
861 for entry in src_uri.split():
862 if entry.startswith('git://'):
863 git_uri = entry
864 break
865 self._check_repo_status(os.path.dirname(recipefile), [])
866 # First, modify a recipe
867 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
868 tempsrcdir = os.path.join(tempdir, 'source')
869 templayerdir = os.path.join(tempdir, 'layer')
870 self.track_for_cleanup(tempdir)
871 self.track_for_cleanup(self.workspacedir)
872 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
873 # (don't bother with cleaning the recipe on teardown, we won't be building it)
874 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempsrcdir))
875 # Check git repo
876 self._check_src_repo(tempsrcdir)
877 # Add a commit
878 result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir)
879 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
880 self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe))
881 # Create a temporary layer
882 os.makedirs(os.path.join(templayerdir, 'conf'))
883 with open(os.path.join(templayerdir, 'conf', 'layer.conf'), 'w') as f:
884 f.write('BBPATH .= ":${LAYERDIR}"\n')
885 f.write('BBFILES += "${LAYERDIR}/recipes-*/*/*.bbappend"\n')
886 f.write('BBFILE_COLLECTIONS += "oeselftesttemplayer"\n')
887 f.write('BBFILE_PATTERN_oeselftesttemplayer = "^${LAYERDIR}/"\n')
888 f.write('BBFILE_PRIORITY_oeselftesttemplayer = "999"\n')
889 f.write('BBFILE_PATTERN_IGNORE_EMPTY_oeselftesttemplayer = "1"\n')
890 self.add_command_to_tearDown('bitbake-layers remove-layer %s || true' % templayerdir)
891 result = runCmd('bitbake-layers add-layer %s' % templayerdir, cwd=self.builddir)
892 # Create the bbappend
893 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
894 self.assertNotIn('WARNING:', result.output)
895 # Check recipe is still clean
896 self._check_repo_status(os.path.dirname(recipefile), [])
897 # Check bbappend was created
898 splitpath = os.path.dirname(recipefile).split(os.sep)
899 appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1])
900 bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir)
901 self.assertFalse(os.path.exists(os.path.join(appenddir, testrecipe)), 'Patch directory should not be created')
902
903 # Check bbappend contents
904 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
905 expectedlines = set(['SRCREV = "%s"\n' % result.output,
906 '\n',
907 'SRC_URI = "%s"\n' % git_uri,
908 '\n'])
909 with open(bbappendfile, 'r') as f:
910 self.assertEqual(expectedlines, set(f.readlines()))
911
912 # Check we can run it again and bbappend isn't modified
913 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
914 with open(bbappendfile, 'r') as f:
915 self.assertEqual(expectedlines, set(f.readlines()))
916 # Drop new commit and check SRCREV changes
917 result = runCmd('git reset HEAD^', cwd=tempsrcdir)
918 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
919 self.assertFalse(os.path.exists(os.path.join(appenddir, testrecipe)), 'Patch directory should not be created')
920 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
921 expectedlines = set(['SRCREV = "%s"\n' % result.output,
922 '\n',
923 'SRC_URI = "%s"\n' % git_uri,
924 '\n'])
925 with open(bbappendfile, 'r') as f:
926 self.assertEqual(expectedlines, set(f.readlines()))
927 # Put commit back and check we can run it if layer isn't in bblayers.conf
928 os.remove(bbappendfile)
929 result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir)
930 result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir)
931 result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir))
932 self.assertIn('WARNING: Specified layer is not currently enabled in bblayers.conf', result.output)
933 self.assertFalse(os.path.exists(os.path.join(appenddir, testrecipe)), 'Patch directory should not be created')
934 result = runCmd('git rev-parse HEAD', cwd=tempsrcdir)
935 expectedlines = set(['SRCREV = "%s"\n' % result.output,
936 '\n',
937 'SRC_URI = "%s"\n' % git_uri,
938 '\n'])
939 with open(bbappendfile, 'r') as f:
940 self.assertEqual(expectedlines, set(f.readlines()))
941 # Deleting isn't expected to work under these circumstances
942
943 @OETestID(1370)
944 def test_devtool_update_recipe_local_files(self):
945 """Check that local source files are copied over instead of patched"""
946 testrecipe = 'makedevs'
947 recipefile = get_bb_var('FILE', testrecipe)
948 # Setup srctree for modifying the recipe
949 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
950 self.track_for_cleanup(tempdir)
951 self.track_for_cleanup(self.workspacedir)
952 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
953 # (don't bother with cleaning the recipe on teardown, we won't be
954 # building it)
955 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
956 # Check git repo
957 self._check_src_repo(tempdir)
958 # Try building just to ensure we haven't broken that
959 bitbake("%s" % testrecipe)
960 # Edit / commit local source
961 runCmd('echo "/* Foobar */" >> oe-local-files/makedevs.c', cwd=tempdir)
962 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir)
963 runCmd('echo "Bar" > new-file', cwd=tempdir)
964 runCmd('git add new-file', cwd=tempdir)
965 runCmd('git commit -m "Add new file"', cwd=tempdir)
966 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
967 os.path.dirname(recipefile))
968 runCmd('devtool update-recipe %s' % testrecipe)
969 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
970 (' M', '.*/makedevs/makedevs.c$'),
971 ('??', '.*/makedevs/new-local$'),
972 ('??', '.*/makedevs/0001-Add-new-file.patch$')]
973 self._check_repo_status(os.path.dirname(recipefile), expected_status)
974
975 @OETestID(1371)
976 def test_devtool_update_recipe_local_files_2(self):
977 """Check local source files support when oe-local-files is in Git"""
978 testrecipe = 'lzo'
979 recipefile = get_bb_var('FILE', testrecipe)
980 # Setup srctree for modifying the recipe
981 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
982 self.track_for_cleanup(tempdir)
983 self.track_for_cleanup(self.workspacedir)
984 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
985 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
986 # Check git repo
987 self._check_src_repo(tempdir)
988 # Add oe-local-files to Git
989 runCmd('rm oe-local-files/.gitignore', cwd=tempdir)
990 runCmd('git add oe-local-files', cwd=tempdir)
991 runCmd('git commit -m "Add local sources"', cwd=tempdir)
992 # Edit / commit local sources
993 runCmd('echo "# Foobar" >> oe-local-files/acinclude.m4', cwd=tempdir)
994 runCmd('git commit -am "Edit existing file"', cwd=tempdir)
995 runCmd('git rm oe-local-files/run-ptest', cwd=tempdir)
996 runCmd('git commit -m"Remove file"', cwd=tempdir)
997 runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir)
998 runCmd('git add oe-local-files/new-local', cwd=tempdir)
999 runCmd('git commit -m "Add new local file"', cwd=tempdir)
1000 runCmd('echo "Gar" > new-file', cwd=tempdir)
1001 runCmd('git add new-file', cwd=tempdir)
1002 runCmd('git commit -m "Add new file"', cwd=tempdir)
1003 self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' %
1004 os.path.dirname(recipefile))
1005 # Checkout unmodified file to working copy -> devtool should still pick
1006 # the modified version from HEAD
1007 runCmd('git checkout HEAD^ -- oe-local-files/acinclude.m4', cwd=tempdir)
1008 runCmd('devtool update-recipe %s' % testrecipe)
1009 expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)),
1010 (' M', '.*/acinclude.m4$'),
1011 (' D', '.*/run-ptest$'),
1012 ('??', '.*/new-local$'),
1013 ('??', '.*/0001-Add-new-file.patch$')]
1014 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1015
1016 def test_devtool_update_recipe_local_files_3(self):
1017 # First, modify the recipe
1018 testrecipe = 'devtool-test-localonly'
1019 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1020 recipefile = bb_vars['FILE']
1021 src_uri = bb_vars['SRC_URI']
1022 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1023 self.track_for_cleanup(tempdir)
1024 self.track_for_cleanup(self.workspacedir)
1025 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1026 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1027 result = runCmd('devtool modify %s' % testrecipe)
1028 # Modify one file
1029 runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe, 'oe-local-files'))
1030 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1031 result = runCmd('devtool update-recipe %s' % testrecipe)
1032 expected_status = [(' M', '.*/%s/file2$' % testrecipe)]
1033 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1034
1035 def test_devtool_update_recipe_local_patch_gz(self):
1036 # First, modify the recipe
1037 testrecipe = 'devtool-test-patch-gz'
1038 if get_bb_var('DISTRO') == 'poky-tiny':
1039 self.skipTest("The DISTRO 'poky-tiny' does not provide the dependencies needed by %s" % testrecipe)
1040 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1041 recipefile = bb_vars['FILE']
1042 src_uri = bb_vars['SRC_URI']
1043 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1044 self.track_for_cleanup(tempdir)
1045 self.track_for_cleanup(self.workspacedir)
1046 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1047 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1048 result = runCmd('devtool modify %s' % testrecipe)
1049 # Modify one file
1050 srctree = os.path.join(self.workspacedir, 'sources', testrecipe)
1051 runCmd('echo "Another line" >> README', cwd=srctree)
1052 runCmd('git commit -a --amend --no-edit', cwd=srctree)
1053 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1054 result = runCmd('devtool update-recipe %s' % testrecipe)
1055 expected_status = [(' M', '.*/%s/readme.patch.gz$' % testrecipe)]
1056 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1057 patch_gz = os.path.join(os.path.dirname(recipefile), testrecipe, 'readme.patch.gz')
1058 result = runCmd('file %s' % patch_gz)
1059 if 'gzip compressed data' not in result.output:
1060 self.fail('New patch file is not gzipped - file reports:\n%s' % result.output)
1061
1062 def test_devtool_update_recipe_local_files_subdir(self):
1063 # Try devtool extract on a recipe that has a file with subdir= set in
1064 # SRC_URI such that it overwrites a file that was in an archive that
1065 # was also in SRC_URI
1066 # First, modify the recipe
1067 testrecipe = 'devtool-test-subdir'
1068 bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe)
1069 recipefile = bb_vars['FILE']
1070 src_uri = bb_vars['SRC_URI']
1071 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1072 self.track_for_cleanup(tempdir)
1073 self.track_for_cleanup(self.workspacedir)
1074 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1075 # (don't bother with cleaning the recipe on teardown, we won't be building it)
1076 result = runCmd('devtool modify %s' % testrecipe)
1077 testfile = os.path.join(self.workspacedir, 'sources', testrecipe, 'testfile')
1078 self.assertTrue(os.path.exists(testfile), 'Extracted source could not be found')
1079 with open(testfile, 'r') as f:
1080 contents = f.read().rstrip()
1081 self.assertEqual(contents, 'Modified version', 'File has apparently not been overwritten as it should have been')
1082 # Test devtool update-recipe without modifying any files
1083 self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)))
1084 result = runCmd('devtool update-recipe %s' % testrecipe)
1085 expected_status = []
1086 self._check_repo_status(os.path.dirname(recipefile), expected_status)
1087
1088 @OETestID(1163)
1089 def test_devtool_extract(self):
1090 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1091 # Try devtool extract
1092 self.track_for_cleanup(tempdir)
1093 self.append_config('PREFERRED_PROVIDER_virtual/make = "remake"')
1094 result = runCmd('devtool extract remake %s' % tempdir)
1095 self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile.am')), 'Extracted source could not be found')
1096 # devtool extract shouldn't create the workspace
1097 self.assertFalse(os.path.exists(self.workspacedir))
1098 self._check_src_repo(tempdir)
1099
1100 @OETestID(1379)
1101 def test_devtool_extract_virtual(self):
1102 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1103 # Try devtool extract
1104 self.track_for_cleanup(tempdir)
1105 result = runCmd('devtool extract virtual/make %s' % tempdir)
1106 self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile.am')), 'Extracted source could not be found')
1107 # devtool extract shouldn't create the workspace
1108 self.assertFalse(os.path.exists(self.workspacedir))
1109 self._check_src_repo(tempdir)
1110
1111 @OETestID(1168)
1112 def test_devtool_reset_all(self):
1113 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1114 self.track_for_cleanup(tempdir)
1115 self.track_for_cleanup(self.workspacedir)
1116 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1117 testrecipe1 = 'mdadm'
1118 testrecipe2 = 'cronie'
1119 result = runCmd('devtool modify -x %s %s' % (testrecipe1, os.path.join(tempdir, testrecipe1)))
1120 result = runCmd('devtool modify -x %s %s' % (testrecipe2, os.path.join(tempdir, testrecipe2)))
1121 result = runCmd('devtool build %s' % testrecipe1)
1122 result = runCmd('devtool build %s' % testrecipe2)
1123 stampprefix1 = get_bb_var('STAMP', testrecipe1)
1124 self.assertTrue(stampprefix1, 'Unable to get STAMP value for recipe %s' % testrecipe1)
1125 stampprefix2 = get_bb_var('STAMP', testrecipe2)
1126 self.assertTrue(stampprefix2, 'Unable to get STAMP value for recipe %s' % testrecipe2)
1127 result = runCmd('devtool reset -a')
1128 self.assertIn(testrecipe1, result.output)
1129 self.assertIn(testrecipe2, result.output)
1130 result = runCmd('devtool status')
1131 self.assertNotIn(testrecipe1, result.output)
1132 self.assertNotIn(testrecipe2, result.output)
1133 matches1 = glob.glob(stampprefix1 + '*')
1134 self.assertFalse(matches1, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe1)
1135 matches2 = glob.glob(stampprefix2 + '*')
1136 self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2)
1137
1138 @OETestID(1272)
1139 def test_devtool_deploy_target(self):
1140 # NOTE: Whilst this test would seemingly be better placed as a runtime test,
1141 # unfortunately the runtime tests run under bitbake and you can't run
1142 # devtool within bitbake (since devtool needs to run bitbake itself).
1143 # Additionally we are testing build-time functionality as well, so
1144 # really this has to be done as an oe-selftest test.
1145 #
1146 # Check preconditions
1147 machine = get_bb_var('MACHINE')
1148 if not machine.startswith('qemu'):
1149 self.skipTest('This test only works with qemu machines')
1150 if not os.path.exists('/etc/runqemu-nosudo'):
1151 self.skipTest('You must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
1152 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ip tuntap show', ignore_status=True)
1153 if result.status != 0:
1154 result = runCmd('PATH="$PATH:/sbin:/usr/sbin" ifconfig -a', ignore_status=True)
1155 if result.status != 0:
1156 self.skipTest('Failed to determine if tap devices exist with ifconfig or ip: %s' % result.output)
1157 for line in result.output.splitlines():
1158 if line.startswith('tap'):
1159 break
1160 else:
1161 self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test')
1162 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1163 # Definitions
1164 testrecipe = 'mdadm'
1165 testfile = '/sbin/mdadm'
1166 testimage = 'oe-selftest-image'
1167 testcommand = '/sbin/mdadm --help'
1168 # Build an image to run
1169 bitbake("%s qemu-native qemu-helper-native" % testimage)
1170 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
1171 self.add_command_to_tearDown('bitbake -c clean %s' % testimage)
1172 self.add_command_to_tearDown('rm -f %s/%s*' % (deploy_dir_image, testimage))
1173 # Clean recipe so the first deploy will fail
1174 bitbake("%s -c clean" % testrecipe)
1175 # Try devtool modify
1176 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1177 self.track_for_cleanup(tempdir)
1178 self.track_for_cleanup(self.workspacedir)
1179 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1180 self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe)
1181 result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir))
1182 # Test that deploy-target at this point fails (properly)
1183 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe, ignore_status=True)
1184 self.assertNotEqual(result.output, 0, 'devtool deploy-target should have failed, output: %s' % result.output)
1185 self.assertNotIn(result.output, 'Traceback', 'devtool deploy-target should have failed with a proper error not a traceback, output: %s' % result.output)
1186 result = runCmd('devtool build %s' % testrecipe)
1187 # First try a dry-run of deploy-target
1188 result = runCmd('devtool deploy-target -n %s root@localhost' % testrecipe)
1189 self.assertIn(' %s' % testfile, result.output)
1190 # Boot the image
1191 with runqemu(testimage) as qemu:
1192 # Now really test deploy-target
1193 result = runCmd('devtool deploy-target -c %s root@%s' % (testrecipe, qemu.ip))
1194 # Run a test command to see if it was installed properly
1195 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
1196 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand))
1197 # Check if it deployed all of the files with the right ownership/perms
1198 # First look on the host - need to do this under pseudo to get the correct ownership/perms
1199 bb_vars = get_bb_vars(['D', 'FAKEROOTENV', 'FAKEROOTCMD'], testrecipe)
1200 installdir = bb_vars['D']
1201 fakerootenv = bb_vars['FAKEROOTENV']
1202 fakerootcmd = bb_vars['FAKEROOTCMD']
1203 result = runCmd('%s %s find . -type f -exec ls -l {} \;' % (fakerootenv, fakerootcmd), cwd=installdir)
1204 filelist1 = self._process_ls_output(result.output)
1205
1206 # Now look on the target
1207 tempdir2 = tempfile.mkdtemp(prefix='devtoolqa')
1208 self.track_for_cleanup(tempdir2)
1209 tmpfilelist = os.path.join(tempdir2, 'files.txt')
1210 with open(tmpfilelist, 'w') as f:
1211 for line in filelist1:
1212 splitline = line.split()
1213 f.write(splitline[-1] + '\n')
1214 result = runCmd('cat %s | ssh -q %s root@%s \'xargs ls -l\'' % (tmpfilelist, sshargs, qemu.ip))
1215 filelist2 = self._process_ls_output(result.output)
1216 filelist1.sort(key=lambda item: item.split()[-1])
1217 filelist2.sort(key=lambda item: item.split()[-1])
1218 self.assertEqual(filelist1, filelist2)
1219 # Test undeploy-target
1220 result = runCmd('devtool undeploy-target -c %s root@%s' % (testrecipe, qemu.ip))
1221 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True)
1222 self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have')
1223
1224 @OETestID(1366)
1225 def test_devtool_build_image(self):
1226 """Test devtool build-image plugin"""
1227 # Check preconditions
1228 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1229 image = 'core-image-minimal'
1230 self.track_for_cleanup(self.workspacedir)
1231 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1232 self.add_command_to_tearDown('bitbake -c clean %s' % image)
1233 bitbake('%s -c clean' % image)
1234 # Add target and native recipes to workspace
1235 recipes = ['mdadm', 'parted-native']
1236 for recipe in recipes:
1237 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1238 self.track_for_cleanup(tempdir)
1239 self.add_command_to_tearDown('bitbake -c clean %s' % recipe)
1240 runCmd('devtool modify %s -x %s' % (recipe, tempdir))
1241 # Try to build image
1242 result = runCmd('devtool build-image %s' % image)
1243 self.assertNotEqual(result, 0, 'devtool build-image failed')
1244 # Check if image contains expected packages
1245 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
1246 image_link_name = get_bb_var('IMAGE_LINK_NAME', image)
1247 reqpkgs = [item for item in recipes if not item.endswith('-native')]
1248 with open(os.path.join(deploy_dir_image, image_link_name + '.manifest'), 'r') as f:
1249 for line in f:
1250 splitval = line.split()
1251 if splitval:
1252 pkg = splitval[0]
1253 if pkg in reqpkgs:
1254 reqpkgs.remove(pkg)
1255 if reqpkgs:
1256 self.fail('The following packages were not present in the image as expected: %s' % ', '.join(reqpkgs))
1257
1258 @OETestID(1367)
1259 def test_devtool_upgrade(self):
1260 # Check preconditions
1261 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1262 self.track_for_cleanup(self.workspacedir)
1263 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1264 # Check parameters
1265 result = runCmd('devtool upgrade -h')
1266 for param in 'recipename srctree --version -V --branch -b --keep-temp --no-patch'.split():
1267 self.assertIn(param, result.output)
1268 # For the moment, we are using a real recipe.
1269 recipe = 'devtool-upgrade-test1'
1270 version = '1.6.0'
1271 oldrecipefile = get_bb_var('FILE', recipe)
1272 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1273 self.track_for_cleanup(tempdir)
1274 # Check that recipe is not already under devtool control
1275 result = runCmd('devtool status')
1276 self.assertNotIn(recipe, result.output)
1277 # Check upgrade. Code does not check if new PV is older or newer that current PV, so, it may be that
1278 # we are downgrading instead of upgrading.
1279 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, version))
1280 # Check if srctree at least is populated
1281 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, version))
1282 # Check new recipe subdirectory is present
1283 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe, '%s-%s' % (recipe, version))), 'Recipe folder should exist')
1284 # Check new recipe file is present
1285 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, '%s_%s.bb' % (recipe, version))
1286 self.assertTrue(os.path.exists(newrecipefile), 'Recipe file should exist after upgrade')
1287 # Check devtool status and make sure recipe is present
1288 result = runCmd('devtool status')
1289 self.assertIn(recipe, result.output)
1290 self.assertIn(tempdir, result.output)
1291 # Check recipe got changed as expected
1292 with open(oldrecipefile + '.upgraded', 'r') as f:
1293 desiredlines = f.readlines()
1294 with open(newrecipefile, 'r') as f:
1295 newlines = f.readlines()
1296 self.assertEqual(desiredlines, newlines)
1297 # Check devtool reset recipe
1298 result = runCmd('devtool reset %s -n' % recipe)
1299 result = runCmd('devtool status')
1300 self.assertNotIn(recipe, result.output)
1301 self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after resetting')
1302
1303 @OETestID(1433)
1304 def test_devtool_upgrade_git(self):
1305 # Check preconditions
1306 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1307 self.track_for_cleanup(self.workspacedir)
1308 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1309 recipe = 'devtool-upgrade-test2'
1310 commit = '6cc6077a36fe2648a5f993fe7c16c9632f946517'
1311 oldrecipefile = get_bb_var('FILE', recipe)
1312 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1313 self.track_for_cleanup(tempdir)
1314 # Check that recipe is not already under devtool control
1315 result = runCmd('devtool status')
1316 self.assertNotIn(recipe, result.output)
1317 # Check upgrade
1318 result = runCmd('devtool upgrade %s %s -S %s' % (recipe, tempdir, commit))
1319 # Check if srctree at least is populated
1320 self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit))
1321 # Check new recipe file is present
1322 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile))
1323 self.assertTrue(os.path.exists(newrecipefile), 'Recipe file should exist after upgrade')
1324 # Check devtool status and make sure recipe is present
1325 result = runCmd('devtool status')
1326 self.assertIn(recipe, result.output)
1327 self.assertIn(tempdir, result.output)
1328 # Check recipe got changed as expected
1329 with open(oldrecipefile + '.upgraded', 'r') as f:
1330 desiredlines = f.readlines()
1331 with open(newrecipefile, 'r') as f:
1332 newlines = f.readlines()
1333 self.assertEqual(desiredlines, newlines)
1334 # Check devtool reset recipe
1335 result = runCmd('devtool reset %s -n' % recipe)
1336 result = runCmd('devtool status')
1337 self.assertNotIn(recipe, result.output)
1338 self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after resetting')
1339
1340 @OETestID(1352)
1341 def test_devtool_layer_plugins(self):
1342 """Test that devtool can use plugins from other layers.
1343
1344 This test executes the selftest-reverse command from meta-selftest."""
1345
1346 self.track_for_cleanup(self.workspacedir)
1347 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1348
1349 s = "Microsoft Made No Profit From Anyone's Zunes Yo"
1350 result = runCmd("devtool --quiet selftest-reverse \"%s\"" % s)
1351 self.assertEqual(result.output, s[::-1])
1352
1353 def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
1354 dstdir = basedstdir
1355 self.assertTrue(os.path.exists(dstdir))
1356 for p in paths:
1357 dstdir = os.path.join(dstdir, p)
1358 if not os.path.exists(dstdir):
1359 os.makedirs(dstdir)
1360 self.track_for_cleanup(dstdir)
1361 dstfile = os.path.join(dstdir, os.path.basename(srcfile))
1362 if srcfile != dstfile:
1363 shutil.copy(srcfile, dstfile)
1364 self.track_for_cleanup(dstfile)
1365
1366 def test_devtool_load_plugin(self):
1367 """Test that devtool loads only the first found plugin in BBPATH."""
1368
1369 self.track_for_cleanup(self.workspacedir)
1370 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1371
1372 devtool = runCmd("which devtool")
1373 fromname = runCmd("devtool --quiet pluginfile")
1374 srcfile = fromname.output
1375 bbpath = get_bb_var('BBPATH')
1376 searchpath = bbpath.split(':') + [os.path.dirname(devtool.output)]
1377 plugincontent = []
1378 with open(srcfile) as fh:
1379 plugincontent = fh.readlines()
1380 try:
1381 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
1382 for path in searchpath:
1383 self._copy_file_with_cleanup(srcfile, path, 'lib', 'devtool')
1384 result = runCmd("devtool --quiet count")
1385 self.assertEqual(result.output, '1')
1386 result = runCmd("devtool --quiet multiloaded")
1387 self.assertEqual(result.output, "no")
1388 for path in searchpath:
1389 result = runCmd("devtool --quiet bbdir")
1390 self.assertEqual(result.output, path)
1391 os.unlink(os.path.join(result.output, 'lib', 'devtool', 'bbpath.py'))
1392 finally:
1393 with open(srcfile, 'w') as fh:
1394 fh.writelines(plugincontent)
1395
1396 def _setup_test_devtool_finish_upgrade(self):
1397 # Check preconditions
1398 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1399 self.track_for_cleanup(self.workspacedir)
1400 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1401 # Use a "real" recipe from meta-selftest
1402 recipe = 'devtool-upgrade-test1'
1403 oldversion = '1.5.3'
1404 newversion = '1.6.0'
1405 oldrecipefile = get_bb_var('FILE', recipe)
1406 recipedir = os.path.dirname(oldrecipefile)
1407 result = runCmd('git status --porcelain .', cwd=recipedir)
1408 if result.output.strip():
1409 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
1410 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1411 self.track_for_cleanup(tempdir)
1412 # Check that recipe is not already under devtool control
1413 result = runCmd('devtool status')
1414 self.assertNotIn(recipe, result.output)
1415 # Do the upgrade
1416 result = runCmd('devtool upgrade %s %s -V %s' % (recipe, tempdir, newversion))
1417 # Check devtool status and make sure recipe is present
1418 result = runCmd('devtool status')
1419 self.assertIn(recipe, result.output)
1420 self.assertIn(tempdir, result.output)
1421 # Make a change to the source
1422 result = runCmd('sed -i \'/^#include "pv.h"/a \\/* Here is a new comment *\\/\' src/pv/number.c', cwd=tempdir)
1423 result = runCmd('git status --porcelain', cwd=tempdir)
1424 self.assertIn('M src/pv/number.c', result.output)
1425 result = runCmd('git commit src/pv/number.c -m "Add a comment to the code"', cwd=tempdir)
1426 # Check if patch is there
1427 recipedir = os.path.dirname(oldrecipefile)
1428 olddir = os.path.join(recipedir, recipe + '-' + oldversion)
1429 patchfn = '0001-Add-a-note-line-to-the-quick-reference.patch'
1430 self.assertTrue(os.path.exists(os.path.join(olddir, patchfn)), 'Original patch file does not exist')
1431 return recipe, oldrecipefile, recipedir, olddir, newversion, patchfn
1432
1433 def test_devtool_finish_upgrade_origlayer(self):
1434 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn = self._setup_test_devtool_finish_upgrade()
1435 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
1436 self.assertIn('/meta-selftest/', recipedir)
1437 # Try finish to the original layer
1438 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1439 result = runCmd('devtool finish %s meta-selftest' % recipe)
1440 result = runCmd('devtool status')
1441 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1442 self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
1443 self.assertFalse(os.path.exists(oldrecipefile), 'Old recipe file should have been deleted but wasn\'t')
1444 self.assertFalse(os.path.exists(os.path.join(olddir, patchfn)), 'Old patch file should have been deleted but wasn\'t')
1445 newrecipefile = os.path.join(recipedir, '%s_%s.bb' % (recipe, newversion))
1446 newdir = os.path.join(recipedir, recipe + '-' + newversion)
1447 self.assertTrue(os.path.exists(newrecipefile), 'New recipe file should have been copied into existing layer but wasn\'t')
1448 self.assertTrue(os.path.exists(os.path.join(newdir, patchfn)), 'Patch file should have been copied into new directory but wasn\'t')
1449 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')
1450
1451 def test_devtool_finish_upgrade_otherlayer(self):
1452 recipe, oldrecipefile, recipedir, olddir, newversion, patchfn = self._setup_test_devtool_finish_upgrade()
1453 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
1454 self.assertIn('/meta-selftest/', recipedir)
1455 # Try finish to a different layer - should create a bbappend
1456 # This cleanup isn't strictly necessary but do it anyway just in case it goes wrong and writes to here
1457 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1458 oe_core_dir = os.path.join(get_bb_var('COREBASE'), 'meta')
1459 newrecipedir = os.path.join(oe_core_dir, 'recipes-test', 'devtool')
1460 newrecipefile = os.path.join(newrecipedir, '%s_%s.bb' % (recipe, newversion))
1461 self.track_for_cleanup(newrecipedir)
1462 result = runCmd('devtool finish %s oe-core' % recipe)
1463 result = runCmd('devtool status')
1464 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1465 self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
1466 self.assertTrue(os.path.exists(oldrecipefile), 'Old recipe file should not have been deleted')
1467 self.assertTrue(os.path.exists(os.path.join(olddir, patchfn)), 'Old patch file should not have been deleted')
1468 newdir = os.path.join(newrecipedir, recipe + '-' + newversion)
1469 self.assertTrue(os.path.exists(newrecipefile), 'New recipe file should have been copied into existing layer but wasn\'t')
1470 self.assertTrue(os.path.exists(os.path.join(newdir, patchfn)), 'Patch file should have been copied into new directory but wasn\'t')
1471 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')
1472
1473 def _setup_test_devtool_finish_modify(self):
1474 # Check preconditions
1475 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1476 # Try modifying a recipe
1477 self.track_for_cleanup(self.workspacedir)
1478 recipe = 'mdadm'
1479 oldrecipefile = get_bb_var('FILE', recipe)
1480 recipedir = os.path.dirname(oldrecipefile)
1481 result = runCmd('git status --porcelain .', cwd=recipedir)
1482 if result.output.strip():
1483 self.fail('Recipe directory for %s contains uncommitted changes' % recipe)
1484 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1485 self.track_for_cleanup(tempdir)
1486 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1487 result = runCmd('devtool modify %s %s' % (recipe, tempdir))
1488 self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')), 'Extracted source could not be found')
1489 # Test devtool status
1490 result = runCmd('devtool status')
1491 self.assertIn(recipe, result.output)
1492 self.assertIn(tempdir, result.output)
1493 # Make a change to the source
1494 result = runCmd('sed -i \'/^#include "mdadm.h"/a \\/* Here is a new comment *\\/\' maps.c', cwd=tempdir)
1495 result = runCmd('git status --porcelain', cwd=tempdir)
1496 self.assertIn('M maps.c', result.output)
1497 result = runCmd('git commit maps.c -m "Add a comment to the code"', cwd=tempdir)
1498 for entry in os.listdir(recipedir):
1499 filesdir = os.path.join(recipedir, entry)
1500 if os.path.isdir(filesdir):
1501 break
1502 else:
1503 self.fail('Unable to find recipe files directory for %s' % recipe)
1504 return recipe, oldrecipefile, recipedir, filesdir
1505
1506 def test_devtool_finish_modify_origlayer(self):
1507 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
1508 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
1509 self.assertIn('/meta/', recipedir)
1510 # Try finish to the original layer
1511 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1512 result = runCmd('devtool finish %s meta' % recipe)
1513 result = runCmd('devtool status')
1514 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1515 self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
1516 expected_status = [(' M', '.*/%s$' % os.path.basename(oldrecipefile)),
1517 ('??', '.*/.*-Add-a-comment-to-the-code.patch$')]
1518 self._check_repo_status(recipedir, expected_status)
1519
1520 def test_devtool_finish_modify_otherlayer(self):
1521 recipe, oldrecipefile, recipedir, filesdir = self._setup_test_devtool_finish_modify()
1522 # Ensure the recipe is where we think it should be (so that cleanup doesn't trash things)
1523 self.assertIn('/meta/', recipedir)
1524 relpth = os.path.relpath(recipedir, os.path.join(get_bb_var('COREBASE'), 'meta'))
1525 appenddir = os.path.join(get_test_layer(), relpth)
1526 self.track_for_cleanup(appenddir)
1527 # Try finish to the original layer
1528 self.add_command_to_tearDown('rm -rf %s ; cd %s ; git checkout %s' % (recipedir, os.path.dirname(recipedir), recipedir))
1529 result = runCmd('devtool finish %s meta-selftest' % recipe)
1530 result = runCmd('devtool status')
1531 self.assertNotIn(recipe, result.output, 'Recipe should have been reset by finish but wasn\'t')
1532 self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipe)), 'Recipe directory should not exist after finish')
1533 result = runCmd('git status --porcelain .', cwd=recipedir)
1534 if result.output.strip():
1535 self.fail('Recipe directory for %s contains the following unexpected changes after finish:\n%s' % (recipe, result.output.strip()))
1536 recipefn = os.path.splitext(os.path.basename(oldrecipefile))[0]
1537 recipefn = recipefn.split('_')[0] + '_%'
1538 appendfile = os.path.join(appenddir, recipefn + '.bbappend')
1539 self.assertTrue(os.path.exists(appendfile), 'bbappend %s should have been created but wasn\'t' % appendfile)
1540 newdir = os.path.join(appenddir, recipe)
1541 files = os.listdir(newdir)
1542 foundpatch = None
1543 for fn in files:
1544 if fnmatch.fnmatch(fn, '*-Add-a-comment-to-the-code.patch'):
1545 foundpatch = fn
1546 if not foundpatch:
1547 self.fail('No patch file created next to bbappend')
1548 files.remove(foundpatch)
1549 if files:
1550 self.fail('Unexpected file(s) copied next to bbappend: %s' % ', '.join(files))
1551
1552 def test_devtool_rename(self):
1553 # Check preconditions
1554 self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory')
1555 self.track_for_cleanup(self.workspacedir)
1556 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1557
1558 # First run devtool add
1559 # We already have this recipe in OE-Core, but that doesn't matter
1560 recipename = 'i2c-tools'
1561 recipever = '3.1.2'
1562 recipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, recipever))
1563 url = 'http://downloads.yoctoproject.org/mirror/sources/i2c-tools-%s.tar.bz2' % recipever
1564 def add_recipe():
1565 result = runCmd('devtool add %s' % url)
1566 self.assertTrue(os.path.exists(recipefile), 'Expected recipe file not created')
1567 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'sources', recipename)), 'Source directory not created')
1568 checkvars = {}
1569 checkvars['S'] = None
1570 checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
1571 self._test_recipe_contents(recipefile, checkvars, [])
1572 add_recipe()
1573 # Now rename it - change both name and version
1574 newrecipename = 'mynewrecipe'
1575 newrecipever = '456'
1576 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, newrecipever))
1577 result = runCmd('devtool rename %s %s -V %s' % (recipename, newrecipename, newrecipever))
1578 self.assertTrue(os.path.exists(newrecipefile), 'Recipe file not renamed')
1579 self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipename)), 'Old recipe directory still exists')
1580 newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename)
1581 self.assertTrue(os.path.exists(newsrctree), 'Source directory not renamed')
1582 checkvars = {}
1583 checkvars['S'] = '${WORKDIR}/%s-%s' % (recipename, recipever)
1584 checkvars['SRC_URI'] = url
1585 self._test_recipe_contents(newrecipefile, checkvars, [])
1586 # Try again - change just name this time
1587 result = runCmd('devtool reset -n %s' % newrecipename)
1588 shutil.rmtree(newsrctree)
1589 add_recipe()
1590 newrecipefile = os.path.join(self.workspacedir, 'recipes', newrecipename, '%s_%s.bb' % (newrecipename, recipever))
1591 result = runCmd('devtool rename %s %s' % (recipename, newrecipename))
1592 self.assertTrue(os.path.exists(newrecipefile), 'Recipe file not renamed')
1593 self.assertFalse(os.path.exists(os.path.join(self.workspacedir, 'recipes', recipename)), 'Old recipe directory still exists')
1594 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'sources', newrecipename)), 'Source directory not renamed')
1595 checkvars = {}
1596 checkvars['S'] = '${WORKDIR}/%s-${PV}' % recipename
1597 checkvars['SRC_URI'] = url.replace(recipever, '${PV}')
1598 self._test_recipe_contents(newrecipefile, checkvars, [])
1599 # Try again - change just version this time
1600 result = runCmd('devtool reset -n %s' % newrecipename)
1601 shutil.rmtree(newsrctree)
1602 add_recipe()
1603 newrecipefile = os.path.join(self.workspacedir, 'recipes', recipename, '%s_%s.bb' % (recipename, newrecipever))
1604 result = runCmd('devtool rename %s -V %s' % (recipename, newrecipever))
1605 self.assertTrue(os.path.exists(newrecipefile), 'Recipe file not renamed')
1606 self.assertTrue(os.path.exists(os.path.join(self.workspacedir, 'sources', recipename)), 'Source directory no longer exists')
1607 checkvars = {}
1608 checkvars['S'] = '${WORKDIR}/${BPN}-%s' % recipever
1609 checkvars['SRC_URI'] = url
1610 self._test_recipe_contents(newrecipefile, checkvars, [])
1611
1612 @OETestID(1577)
1613 def test_devtool_virtual_kernel_modify(self):
1614 """
1615 Summary: The purpose of this test case is to verify that
1616 devtool modify works correctly when building
1617 the kernel.
1618 Dependencies: NA
1619 Steps: 1. Build kernel with bitbake.
1620 2. Save the config file generated.
1621 3. Clean the environment.
1622 4. Use `devtool modify virtual/kernel` to validate following:
1623 4.1 The source is checked out correctly.
1624 4.2 The resulting configuration is the same as
1625 what was get on step 2.
1626 4.3 The Kernel can be build correctly.
1627 4.4 Changes made on the source are reflected on the
1628 subsequent builds.
1629 4.5 Changes on the configuration are reflected on the
1630 subsequent builds
1631 Expected: devtool modify is able to checkout the source of the kernel
1632 and modification to the source and configurations are reflected
1633 when building the kernel.
1634 """
1635 #Set machine to qemxu86 to be able to modify the kernel and
1636 #verify the modification.
1637 features = 'MACHINE = "qemux86"\n'
1638 self.append_config(features)
1639 kernel_provider = get_bb_var('PREFERRED_PROVIDER_virtual/kernel')
1640 # Clean up the enviroment
1641 bitbake('%s -c clean' % kernel_provider)
1642 tempdir = tempfile.mkdtemp(prefix='devtoolqa')
1643 self.track_for_cleanup(tempdir)
1644 self.track_for_cleanup(self.workspacedir)
1645 self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
1646 self.add_command_to_tearDown('bitbake -c clean %s' % kernel_provider)
1647 #Step 1
1648 #Here is just generated the config file instead of all the kernel to optimize the
1649 #time of executing this test case.
1650 bitbake('%s -c configure' % kernel_provider)
1651 bbconfig = os.path.join(get_bb_var('B', kernel_provider),'.config')
1652 buildir= get_bb_var('TOPDIR')
1653 #Step 2
1654 runCmd('cp %s %s' % (bbconfig, buildir))
1655 self.assertTrue(os.path.exists(os.path.join(buildir, '.config')),
1656 'Could not copy .config file from kernel')
1657
1658 tmpconfig = os.path.join(buildir, '.config')
1659 #Step 3
1660 bitbake('%s -c clean' % kernel_provider)
1661 #Step 4.1
1662 runCmd('devtool modify virtual/kernel -x %s' % tempdir)
1663 self.assertTrue(os.path.exists(os.path.join(tempdir, 'Makefile')),
1664 'Extracted source could not be found')
1665 #Step 4.2
1666 configfile = os.path.join(tempdir,'.config')
1667 diff = runCmd('diff %s %s' % (tmpconfig, configfile))
1668 self.assertEqual(0,diff.status,'Kernel .config file is not the same using bitbake and devtool')
1669 #Step 4.3
1670 #NOTE: virtual/kernel is mapped to kernel_provider
1671 result = runCmd('devtool build %s' % kernel_provider)
1672 self.assertEqual(0,result.status,'Cannot build kernel using `devtool build`')
1673 kernelfile = os.path.join(get_bb_var('KBUILD_OUTPUT', kernel_provider), 'vmlinux')
1674 self.assertTrue(os.path.exists(kernelfile),'Kernel was not build correctly')
1675
1676 #Modify the kernel source, this is specific for qemux86
1677 modfile = os.path.join(tempdir,'arch/x86/boot/header.S')
1678 modstring = "use a boot loader - Devtool kernel testing"
1679 modapplied = runCmd("sed -i 's/boot loader/%s/' %s" % (modstring, modfile))
1680 self.assertEqual(0,modapplied.status,'Modification to %s on kernel source failed' % modfile)
1681 #Modify the configuration
1682 codeconfigfile = os.path.join(tempdir,'.config.new')
1683 modconfopt = "CONFIG_SG_POOL=n"
1684 modconf = runCmd("sed -i 's/CONFIG_SG_POOL=y/%s/' %s" % (modconfopt, codeconfigfile))
1685 self.assertEqual(0,modconf.status,'Modification to %s failed' % codeconfigfile)
1686 #Build again kernel with devtool
1687 rebuild = runCmd('devtool build %s' % kernel_provider)
1688 self.assertEqual(0,rebuild.status,'Fail to build kernel after modification of source and config')
1689 #Step 4.4
1690 bzimagename = 'bzImage-' + get_bb_var('KERNEL_VERSION_NAME', kernel_provider)
1691 bzimagefile = os.path.join(get_bb_var('D', kernel_provider),'boot', bzimagename)
1692 checkmodcode = runCmd("grep '%s' %s" % (modstring, bzimagefile))
1693 self.assertEqual(0,checkmodcode.status,'Modification on kernel source failed')
1694 #Step 4.5
1695 checkmodconfg = runCmd("grep %s %s" % (modconfopt, codeconfigfile))
1696 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 @@
1import tempfile
2import shutil
3import os
4import glob
5from oeqa.core.decorator.oeid import OETestID
6from oeqa.selftest.case import OESelftestTestCase
7from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
8
9class oeSDKExtSelfTest(OESelftestTestCase):
10 """
11 # Bugzilla Test Plan: 6033
12 # This code is planned to be part of the automation for eSDK containig
13 # Install libraries and headers, image generation binary feeds, sdk-update.
14 """
15
16 @staticmethod
17 def get_esdk_environment(env_eSDK, tmpdir_eSDKQA):
18 # XXX: at this time use the first env need to investigate
19 # what environment load oe-selftest, i586, x86_64
20 pattern = os.path.join(tmpdir_eSDKQA, 'environment-setup-*')
21 return glob.glob(pattern)[0]
22
23 @staticmethod
24 def run_esdk_cmd(env_eSDK, tmpdir_eSDKQA, cmd, postconfig=None, **options):
25 if postconfig:
26 esdk_conf_file = os.path.join(tmpdir_eSDKQA, 'conf', 'local.conf')
27 with open(esdk_conf_file, 'a+') as f:
28 f.write(postconfig)
29 if not options:
30 options = {}
31 if not 'shell' in options:
32 options['shell'] = True
33
34 runCmd("cd %s; . %s; %s" % (tmpdir_eSDKQA, env_eSDK, cmd), **options)
35
36 @staticmethod
37 def generate_eSDK(image):
38 pn_task = '%s -c populate_sdk_ext' % image
39 bitbake(pn_task)
40
41 @staticmethod
42 def get_eSDK_toolchain(image):
43 pn_task = '%s -c populate_sdk_ext' % image
44
45 bb_vars = get_bb_vars(['SDK_DEPLOY', 'TOOLCHAINEXT_OUTPUTNAME'], pn_task)
46 sdk_deploy = bb_vars['SDK_DEPLOY']
47 toolchain_name = bb_vars['TOOLCHAINEXT_OUTPUTNAME']
48 return os.path.join(sdk_deploy, toolchain_name + '.sh')
49
50 @staticmethod
51 def update_configuration(cls, image, tmpdir_eSDKQA, env_eSDK, ext_sdk_path):
52 sstate_dir = os.path.join(os.environ['BUILDDIR'], 'sstate-cache')
53
54 oeSDKExtSelfTest.generate_eSDK(cls.image)
55
56 cls.ext_sdk_path = oeSDKExtSelfTest.get_eSDK_toolchain(cls.image)
57 runCmd("%s -y -d \"%s\"" % (cls.ext_sdk_path, cls.tmpdir_eSDKQA))
58
59 cls.env_eSDK = oeSDKExtSelfTest.get_esdk_environment('', cls.tmpdir_eSDKQA)
60
61 sstate_config="""
62SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS"
63SSTATE_MIRRORS = "file://.* file://%s/PATH"
64CORE_IMAGE_EXTRA_INSTALL = "perl"
65 """ % sstate_dir
66
67 with open(os.path.join(cls.tmpdir_eSDKQA, 'conf', 'local.conf'), 'a+') as f:
68 f.write(sstate_config)
69
70 @classmethod
71 def setUpClass(cls):
72 super(oeSDKExtSelfTest, cls).setUpClass()
73 cls.tmpdir_eSDKQA = tempfile.mkdtemp(prefix='eSDKQA')
74
75 sstate_dir = get_bb_var('SSTATE_DIR')
76
77 cls.image = 'core-image-minimal'
78 oeSDKExtSelfTest.generate_eSDK(cls.image)
79
80 # Install eSDK
81 cls.ext_sdk_path = oeSDKExtSelfTest.get_eSDK_toolchain(cls.image)
82 runCmd("%s -y -d \"%s\"" % (cls.ext_sdk_path, cls.tmpdir_eSDKQA))
83
84 cls.env_eSDK = oeSDKExtSelfTest.get_esdk_environment('', cls.tmpdir_eSDKQA)
85
86 # Configure eSDK to use sstate mirror from poky
87 sstate_config="""
88SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS"
89SSTATE_MIRRORS = "file://.* file://%s/PATH"
90 """ % sstate_dir
91 with open(os.path.join(cls.tmpdir_eSDKQA, 'conf', 'local.conf'), 'a+') as f:
92 f.write(sstate_config)
93
94 @classmethod
95 def tearDownClass(cls):
96 shutil.rmtree(cls.tmpdir_eSDKQA)
97 super(oeSDKExtSelfTest, cls).tearDownClass()
98
99 @OETestID(1602)
100 def test_install_libraries_headers(self):
101 pn_sstate = 'bc'
102 bitbake(pn_sstate)
103 cmd = "devtool sdk-install %s " % pn_sstate
104 oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, cmd)
105
106 @OETestID(1603)
107 def test_image_generation_binary_feeds(self):
108 image = 'core-image-minimal'
109 cmd = "devtool build-image %s" % image
110 oeSDKExtSelfTest.run_esdk_cmd(self.env_eSDK, self.tmpdir_eSDKQA, cmd)
111
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 @@
1import os
2
3from oeqa.selftest.case import OESelftestTestCase
4from oeqa.utils.commands import bitbake
5
6class ImageTypeDepTests(OESelftestTestCase):
7
8 # Verify that when specifying a IMAGE_TYPEDEP_ of the form "foo.bar" that
9 # the conversion type bar gets added as a dep as well
10 def test_conversion_typedep_added(self):
11
12 self.write_recipeinc('emptytest', """
13# Try to empty out the default dependency list
14PACKAGE_INSTALL = ""
15DISTRO_EXTRA_RDEPENDS=""
16
17LICENSE = "MIT"
18IMAGE_FSTYPES = "testfstype"
19
20IMAGE_TYPES_MASKED += "testfstype"
21IMAGE_TYPEDEP_testfstype = "tar.bz2"
22
23inherit image
24
25""")
26 # First get the dependency that should exist for bz2, it will look
27 # like CONVERSION_DEPENDS_bz2="somedep"
28 result = bitbake('-e emptytest')
29
30 for line in result.output.split('\n'):
31 if line.startswith('CONVERSION_DEPENDS_bz2'):
32 dep = line.split('=')[1].strip('"')
33 break
34
35 # Now get the dependency task list and check for the expected task
36 # dependency
37 bitbake('-g emptytest')
38
39 taskdependsfile = os.path.join(self.builddir, 'task-depends.dot')
40 dep = dep + ".do_populate_sysroot"
41 depfound = False
42 expectedline = '"emptytest.do_rootfs" -> "{}"'.format(dep)
43
44 with open(taskdependsfile, "r") as f:
45 for line in f:
46 if line.strip() == expectedline:
47 depfound = True
48 break
49
50 if not depfound:
51 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu
3from oeqa.core.decorator.oeid import OETestID
4from oeqa.utils.sshcontrol import SSHControl
5import os
6
7class ImageFeatures(OESelftestTestCase):
8
9 test_user = 'tester'
10 root_user = 'root'
11
12 @OETestID(1107)
13 def test_non_root_user_can_connect_via_ssh_without_password(self):
14 """
15 Summary: Check if non root user can connect via ssh without password
16 Expected: 1. Connection to the image via ssh using root user without providing a password should be allowed.
17 2. Connection to the image via ssh using tester user without providing a password should be allowed.
18 Product: oe-core
19 Author: Ionut Chisanovici <ionutx.chisanovici@intel.com>
20 AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
21 """
22
23 features = 'EXTRA_IMAGE_FEATURES = "ssh-server-openssh empty-root-password allow-empty-password"\n'
24 features += 'INHERIT += "extrausers"\n'
25 features += 'EXTRA_USERS_PARAMS = "useradd -p \'\' {}; usermod -s /bin/sh {};"'.format(self.test_user, self.test_user)
26 self.write_config(features)
27
28 # Build a core-image-minimal
29 bitbake('core-image-minimal')
30
31 with runqemu("core-image-minimal") as qemu:
32 # Attempt to ssh with each user into qemu with empty password
33 for user in [self.root_user, self.test_user]:
34 ssh = SSHControl(ip=qemu.ip, logfile=qemu.sshlog, user=user)
35 status, output = ssh.run("true")
36 self.assertEqual(status, 0, 'ssh to user %s failed with %s' % (user, output))
37
38 @OETestID(1115)
39 def test_all_users_can_connect_via_ssh_without_password(self):
40 """
41 Summary: Check if all users can connect via ssh without password
42 Expected: 1. Connection to the image via ssh using root user without providing a password should NOT be allowed.
43 2. Connection to the image via ssh using tester user without providing a password should be allowed.
44 Product: oe-core
45 Author: Ionut Chisanovici <ionutx.chisanovici@intel.com>
46 AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
47 """
48
49 features = 'EXTRA_IMAGE_FEATURES = "ssh-server-openssh allow-empty-password"\n'
50 features += 'INHERIT += "extrausers"\n'
51 features += 'EXTRA_USERS_PARAMS = "useradd -p \'\' {}; usermod -s /bin/sh {};"'.format(self.test_user, self.test_user)
52 self.write_config(features)
53
54 # Build a core-image-minimal
55 bitbake('core-image-minimal')
56
57 with runqemu("core-image-minimal") as qemu:
58 # Attempt to ssh with each user into qemu with empty password
59 for user in [self.root_user, self.test_user]:
60 ssh = SSHControl(ip=qemu.ip, logfile=qemu.sshlog, user=user)
61 status, output = ssh.run("true")
62 if user == 'root':
63 self.assertNotEqual(status, 0, 'ssh to user root was allowed when it should not have been')
64 else:
65 self.assertEqual(status, 0, 'ssh to user tester failed with %s' % output)
66
67
68 @OETestID(1116)
69 def test_clutter_image_can_be_built(self):
70 """
71 Summary: Check if clutter image can be built
72 Expected: 1. core-image-clutter can be built
73 Product: oe-core
74 Author: Ionut Chisanovici <ionutx.chisanovici@intel.com>
75 AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
76 """
77
78 # Build a core-image-clutter
79 bitbake('core-image-clutter')
80
81 @OETestID(1117)
82 def test_wayland_support_in_image(self):
83 """
84 Summary: Check Wayland support in image
85 Expected: 1. Wayland image can be build
86 2. Wayland feature can be installed
87 Product: oe-core
88 Author: Ionut Chisanovici <ionutx.chisanovici@intel.com>
89 AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
90 """
91
92 distro_features = get_bb_var('DISTRO_FEATURES')
93 if not ('opengl' in distro_features and 'wayland' in distro_features):
94 self.skipTest('neither opengl nor wayland present on DISTRO_FEATURES so core-image-weston cannot be built')
95
96 # Build a core-image-weston
97 bitbake('core-image-weston')
98
99 def test_bmap(self):
100 """
101 Summary: Check bmap support
102 Expected: 1. core-image-minimal can be build with bmap support
103 2. core-image-minimal is sparse
104 Product: oe-core
105 Author: Ed Bartosh <ed.bartosh@linux.intel.com>
106 """
107
108 features = 'IMAGE_FSTYPES += " ext4 ext4.bmap"'
109 self.write_config(features)
110
111 image_name = 'core-image-minimal'
112 bitbake(image_name)
113
114 deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
115 link_name = get_bb_var('IMAGE_LINK_NAME', image_name)
116 image_path = os.path.join(deploy_dir_image, "%s.ext4" % link_name)
117 bmap_path = "%s.bmap" % image_path
118
119 # check if result image and bmap file are in deploy directory
120 self.assertTrue(os.path.exists(image_path))
121 self.assertTrue(os.path.exists(bmap_path))
122
123 # check if result image is sparse
124 image_stat = os.stat(image_path)
125 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 @@
1import os
2
3from oeqa.selftest.case import OESelftestTestCase
4from oeqa.utils.commands import runCmd, bitbake, get_bb_var
5import oeqa.utils.ftools as ftools
6from oeqa.core.decorator.oeid import OETestID
7
8class LayerAppendTests(OESelftestTestCase):
9 layerconf = """
10# We have a conf and classes directory, append to BBPATH
11BBPATH .= ":${LAYERDIR}"
12
13# We have a recipes directory, add to BBFILES
14BBFILES += "${LAYERDIR}/recipes*/*.bb ${LAYERDIR}/recipes*/*.bbappend"
15
16BBFILE_COLLECTIONS += "meta-layerINT"
17BBFILE_PATTERN_meta-layerINT := "^${LAYERDIR}/"
18BBFILE_PRIORITY_meta-layerINT = "6"
19"""
20 recipe = """
21LICENSE="CLOSED"
22INHIBIT_DEFAULT_DEPS = "1"
23
24python do_build() {
25 bb.plain('Building ...')
26}
27addtask build
28"""
29 append = """
30FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
31
32SRC_URI_append = " file://appendtest.txt"
33
34sysroot_stage_all_append() {
35 install -m 644 ${WORKDIR}/appendtest.txt ${SYSROOT_DESTDIR}/
36}
37
38"""
39
40 append2 = """
41FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
42
43SRC_URI_append += "file://appendtest.txt"
44"""
45 layerappend = ''
46
47 def tearDownLocal(self):
48 if self.layerappend:
49 ftools.remove_from_file(self.builddir + "/conf/bblayers.conf", self.layerappend)
50 super(LayerAppendTests, self).tearDownLocal()
51
52 @OETestID(1196)
53 def test_layer_appends(self):
54 corebase = get_bb_var("COREBASE")
55
56 for l in ["0", "1", "2"]:
57 layer = os.path.join(corebase, "meta-layertest" + l)
58 self.assertFalse(os.path.exists(layer))
59 os.mkdir(layer)
60 os.mkdir(layer + "/conf")
61 with open(layer + "/conf/layer.conf", "w") as f:
62 f.write(self.layerconf.replace("INT", l))
63 os.mkdir(layer + "/recipes-test")
64 if l == "0":
65 with open(layer + "/recipes-test/layerappendtest.bb", "w") as f:
66 f.write(self.recipe)
67 elif l == "1":
68 with open(layer + "/recipes-test/layerappendtest.bbappend", "w") as f:
69 f.write(self.append)
70 os.mkdir(layer + "/recipes-test/layerappendtest")
71 with open(layer + "/recipes-test/layerappendtest/appendtest.txt", "w") as f:
72 f.write("Layer 1 test")
73 elif l == "2":
74 with open(layer + "/recipes-test/layerappendtest.bbappend", "w") as f:
75 f.write(self.append2)
76 os.mkdir(layer + "/recipes-test/layerappendtest")
77 with open(layer + "/recipes-test/layerappendtest/appendtest.txt", "w") as f:
78 f.write("Layer 2 test")
79 self.track_for_cleanup(layer)
80
81 self.layerappend = "BBLAYERS += \"{0}/meta-layertest0 {0}/meta-layertest1 {0}/meta-layertest2\"".format(corebase)
82 ftools.append_file(self.builddir + "/conf/bblayers.conf", self.layerappend)
83 stagingdir = get_bb_var("SYSROOT_DESTDIR", "layerappendtest")
84 bitbake("layerappendtest")
85 data = ftools.read_file(stagingdir + "/appendtest.txt")
86 self.assertEqual(data, "Layer 2 test")
87 os.remove(corebase + "/meta-layertest2/recipes-test/layerappendtest/appendtest.txt")
88 bitbake("layerappendtest")
89 data = ftools.read_file(stagingdir + "/appendtest.txt")
90 self.assertEqual(data, "Layer 1 test")
91 with open(corebase + "/meta-layertest2/recipes-test/layerappendtest/appendtest.txt", "w") as f:
92 f.write("Layer 2 test")
93 bitbake("layerappendtest")
94 data = ftools.read_file(stagingdir + "/appendtest.txt")
95 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2from oeqa.utils.commands import get_bb_var, get_bb_vars, bitbake, runCmd
3import oe.path
4import os
5
6class LibOE(OESelftestTestCase):
7
8 @classmethod
9 def setUpClass(cls):
10 super(LibOE, cls).setUpClass()
11 cls.tmp_dir = get_bb_var('TMPDIR')
12
13 def test_copy_tree_special(self):
14 """
15 Summary: oe.path.copytree() should copy files with special character
16 Expected: 'test file with sp£c!al @nd spaces' should exist in
17 copy destination
18 Product: OE-Core
19 Author: Joshua Lock <joshua.g.lock@intel.com>
20 """
21 testloc = oe.path.join(self.tmp_dir, 'liboetests')
22 src = oe.path.join(testloc, 'src')
23 dst = oe.path.join(testloc, 'dst')
24 bb.utils.mkdirhier(testloc)
25 bb.utils.mkdirhier(src)
26 testfilename = 'test file with sp£c!al @nd spaces'
27
28 # create the test file and copy it
29 open(oe.path.join(src, testfilename), 'w+b').close()
30 oe.path.copytree(src, dst)
31
32 # ensure path exists in dest
33 fileindst = os.path.isfile(oe.path.join(dst, testfilename))
34 self.assertTrue(fileindst, "File with spaces doesn't exist in dst")
35
36 oe.path.remove(testloc)
37
38 def test_copy_tree_xattr(self):
39 """
40 Summary: oe.path.copytree() should preserve xattr on copied files
41 Expected: testxattr file in destination should have user.oetest
42 extended attribute
43 Product: OE-Core
44 Author: Joshua Lock <joshua.g.lock@intel.com>
45 """
46 testloc = oe.path.join(self.tmp_dir, 'liboetests')
47 src = oe.path.join(testloc, 'src')
48 dst = oe.path.join(testloc, 'dst')
49 bb.utils.mkdirhier(testloc)
50 bb.utils.mkdirhier(src)
51 testfilename = 'testxattr'
52
53 # ensure we have setfattr available
54 bitbake("attr-native")
55
56 bb_vars = get_bb_vars(['SYSROOT_DESTDIR', 'bindir'], 'attr-native')
57 destdir = bb_vars['SYSROOT_DESTDIR']
58 bindir = bb_vars['bindir']
59 bindir = destdir + bindir
60
61 # create a file with xattr and copy it
62 open(oe.path.join(src, testfilename), 'w+b').close()
63 runCmd('%s/setfattr -n user.oetest -v "testing liboe" %s' % (bindir, oe.path.join(src, testfilename)))
64 oe.path.copytree(src, dst)
65
66 # ensure file in dest has user.oetest xattr
67 result = runCmd('%s/getfattr -n user.oetest %s' % (bindir, oe.path.join(dst, testfilename)))
68 self.assertIn('user.oetest="testing liboe"', result.output, 'Extended attribute not sert in dst')
69
70 oe.path.remove(testloc)
71
72 def test_copy_hardlink_tree_count(self):
73 """
74 Summary: oe.path.copyhardlinktree() shouldn't miss out files
75 Expected: src and dst should have the same number of files
76 Product: OE-Core
77 Author: Joshua Lock <joshua.g.lock@intel.com>
78 """
79 testloc = oe.path.join(self.tmp_dir, 'liboetests')
80 src = oe.path.join(testloc, 'src')
81 dst = oe.path.join(testloc, 'dst')
82 bb.utils.mkdirhier(testloc)
83 bb.utils.mkdirhier(src)
84 testfiles = ['foo', 'bar', '.baz', 'quux']
85
86 def touchfile(tf):
87 open(oe.path.join(src, tf), 'w+b').close()
88
89 for f in testfiles:
90 touchfile(f)
91
92 oe.path.copyhardlinktree(src, dst)
93
94 dstcnt = len(os.listdir(dst))
95 srccnt = len(os.listdir(src))
96 self.assertEquals(dstcnt, len(testfiles), "Number of files in dst (%s) differs from number of files in src(%s)." % (dstcnt, srccnt))
97
98 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 @@
1import os
2import tempfile
3
4from oeqa.selftest.case import OESelftestTestCase
5from oeqa.utils.commands import bitbake
6from oeqa.utils import CommandError
7from oeqa.core.decorator.oeid import OETestID
8
9class LicenseTests(OESelftestTestCase):
10
11 # Verify that changing a license file that has an absolute path causes
12 # the license qa to fail due to a mismatched md5sum.
13 @OETestID(1197)
14 def test_nonmatching_checksum(self):
15 bitbake_cmd = '-c populate_lic emptytest'
16 error_msg = 'emptytest: The new md5 checksum is 8d777f385d3dfec8815d20f7496026dc'
17
18 lic_file, lic_path = tempfile.mkstemp()
19 os.close(lic_file)
20 self.track_for_cleanup(lic_path)
21
22 self.write_recipeinc('emptytest', """
23INHIBIT_DEFAULT_DEPS = "1"
24LIC_FILES_CHKSUM = "file://%s;md5=d41d8cd98f00b204e9800998ecf8427e"
25SRC_URI = "file://%s;md5=d41d8cd98f00b204e9800998ecf8427e"
26""" % (lic_path, lic_path))
27 result = bitbake(bitbake_cmd)
28
29 with open(lic_path, "w") as f:
30 f.write("data")
31
32 self.write_config("INHERIT_remove = \"report-error\"")
33 result = bitbake(bitbake_cmd, ignore_status=True)
34 if error_msg not in result.output:
35 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 @@
1import os
2
3from oeqa.selftest.case import OESelftestTestCase
4from oeqa.utils.commands import get_bb_var, get_bb_vars, bitbake
5from oeqa.core.decorator.oeid import OETestID
6
7class ManifestEntry:
8 '''A manifest item of a collection able to list missing packages'''
9 def __init__(self, entry):
10 self.file = entry
11 self.missing = []
12
13class VerifyManifest(OESelftestTestCase):
14 '''Tests for the manifest files and contents of an image'''
15
16 @classmethod
17 def check_manifest_entries(self, manifest, path):
18 manifest_errors = []
19 try:
20 with open(manifest, "r") as mfile:
21 for line in mfile:
22 manifest_entry = os.path.join(path, line.split()[0])
23 self.logger.debug("{}: looking for {}"\
24 .format(self.classname, manifest_entry))
25 if not os.path.isfile(manifest_entry):
26 manifest_errors.append(manifest_entry)
27 self.logger.debug("{}: {} not found"\
28 .format(self.classname, manifest_entry))
29 except OSError as e:
30 self.logger.debug("{}: checking of {} failed"\
31 .format(self.classname, manifest))
32 raise e
33
34 return manifest_errors
35
36 #this will possibly move from here
37 @classmethod
38 def get_dir_from_bb_var(self, bb_var, target = None):
39 target == self.buildtarget if target == None else target
40 directory = get_bb_var(bb_var, target);
41 if not directory or not os.path.isdir(directory):
42 self.logger.debug("{}: {} points to {} when target = {}"\
43 .format(self.classname, bb_var, directory, target))
44 raise OSError
45 return directory
46
47 @classmethod
48 def setUpClass(self):
49
50 super(VerifyManifest, self).setUpClass()
51 self.buildtarget = 'core-image-minimal'
52 self.classname = 'VerifyManifest'
53
54 self.logger.info("{}: doing bitbake {} as a prerequisite of the test"\
55 .format(self.classname, self.buildtarget))
56 if bitbake(self.buildtarget).status:
57 self.logger.debug("{} Failed to setup {}"\
58 .format(self.classname, self.buildtarget))
59 self.skipTest("{}: Cannot setup testing scenario"\
60 .format(self.classname))
61
62 @OETestID(1380)
63 def test_SDK_manifest_entries(self):
64 '''Verifying the SDK manifest entries exist, this may take a build'''
65
66 # the setup should bitbake core-image-minimal and here it is required
67 # to do an additional setup for the sdk
68 sdktask = '-c populate_sdk'
69 bbargs = sdktask + ' ' + self.buildtarget
70 self.logger.debug("{}: doing bitbake {} as a prerequisite of the test"\
71 .format(self.classname, bbargs))
72 if bitbake(bbargs).status:
73 self.logger.debug("{} Failed to bitbake {}"\
74 .format(self.classname, bbargs))
75 self.skipTest("{}: Cannot setup testing scenario"\
76 .format(self.classname))
77
78
79 pkgdata_dir = reverse_dir = {}
80 mfilename = mpath = m_entry = {}
81 # get manifest location based on target to query about
82 d_target= dict(target = self.buildtarget,
83 host = 'nativesdk-packagegroup-sdk-host')
84 try:
85 mdir = self.get_dir_from_bb_var('SDK_DEPLOY', self.buildtarget)
86 for k in d_target.keys():
87 bb_vars = get_bb_vars(['SDK_NAME', 'SDK_VERSION'], self.buildtarget)
88 mfilename[k] = "{}-toolchain-{}.{}.manifest".format(
89 bb_vars['SDK_NAME'],
90 bb_vars['SDK_VERSION'],
91 k)
92 mpath[k] = os.path.join(mdir, mfilename[k])
93 if not os.path.isfile(mpath[k]):
94 self.logger.debug("{}: {} does not exist".format(
95 self.classname, mpath[k]))
96 raise IOError
97 m_entry[k] = ManifestEntry(mpath[k])
98
99 pkgdata_dir[k] = self.get_dir_from_bb_var('PKGDATA_DIR',
100 d_target[k])
101 reverse_dir[k] = os.path.join(pkgdata_dir[k],
102 'runtime-reverse')
103 if not os.path.exists(reverse_dir[k]):
104 self.logger.debug("{}: {} does not exist".format(
105 self.classname, reverse_dir[k]))
106 raise IOError
107 except OSError:
108 raise self.skipTest("{}: Error in obtaining manifest dirs"\
109 .format(self.classname))
110 except IOError:
111 msg = "{}: Error cannot find manifests in the specified dir:\n{}"\
112 .format(self.classname, mdir)
113 self.fail(msg)
114
115 for k in d_target.keys():
116 self.logger.debug("{}: Check manifest {}".format(
117 self.classname, m_entry[k].file))
118
119 m_entry[k].missing = self.check_manifest_entries(\
120 m_entry[k].file,reverse_dir[k])
121 if m_entry[k].missing:
122 msg = '{}: {} Error has the following missing entries'\
123 .format(self.classname, m_entry[k].file)
124 logmsg = msg+':\n'+'\n'.join(m_entry[k].missing)
125 self.logger.debug(logmsg)
126 self.logger.info(msg)
127 self.fail(logmsg)
128
129 @OETestID(1381)
130 def test_image_manifest_entries(self):
131 '''Verifying the image manifest entries exist'''
132
133 # get manifest location based on target to query about
134 try:
135 mdir = self.get_dir_from_bb_var('DEPLOY_DIR_IMAGE',
136 self.buildtarget)
137 mfilename = get_bb_var("IMAGE_LINK_NAME", self.buildtarget)\
138 + ".manifest"
139 mpath = os.path.join(mdir, mfilename)
140 if not os.path.isfile(mpath): raise IOError
141 m_entry = ManifestEntry(mpath)
142
143 pkgdata_dir = {}
144 pkgdata_dir = self.get_dir_from_bb_var('PKGDATA_DIR',
145 self.buildtarget)
146 revdir = os.path.join(pkgdata_dir, 'runtime-reverse')
147 if not os.path.exists(revdir): raise IOError
148 except OSError:
149 raise self.skipTest("{}: Error in obtaining manifest dirs"\
150 .format(self.classname))
151 except IOError:
152 msg = "{}: Error cannot find manifests in dir:\n{}"\
153 .format(self.classname, mdir)
154 self.fail(msg)
155
156 self.logger.debug("{}: Check manifest {}"\
157 .format(self.classname, m_entry.file))
158 m_entry.missing = self.check_manifest_entries(\
159 m_entry.file, revdir)
160 if m_entry.missing:
161 msg = '{}: {} Error has the following missing entries'\
162 .format(self.classname, m_entry.file)
163 logmsg = msg+':\n'+'\n'.join(m_entry.missing)
164 self.logger.debug(logmsg)
165 self.logger.info(msg)
166 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 @@
1import os
2from oeqa.selftest.case import OESelftestTestCase
3import tempfile
4from git import Repo
5from oeqa.utils.commands import get_bb_var
6from oe.buildhistory_analysis import blob_to_dict, compare_dict_blobs
7
8class TestBlobParsing(OESelftestTestCase):
9
10 def setUp(self):
11 import time
12 self.repo_path = tempfile.mkdtemp(prefix='selftest-buildhistory',
13 dir=get_bb_var('TOPDIR'))
14
15 self.repo = Repo.init(self.repo_path)
16 self.test_file = "test"
17 self.var_map = {}
18
19 def tearDown(self):
20 import shutil
21 shutil.rmtree(self.repo_path)
22
23 def commit_vars(self, to_add={}, to_remove = [], msg="A commit message"):
24 if len(to_add) == 0 and len(to_remove) == 0:
25 return
26
27 for k in to_remove:
28 self.var_map.pop(x,None)
29 for k in to_add:
30 self.var_map[k] = to_add[k]
31
32 with open(os.path.join(self.repo_path, self.test_file), 'w') as repo_file:
33 for k in self.var_map:
34 repo_file.write("%s = %s\n" % (k, self.var_map[k]))
35
36 self.repo.git.add("--all")
37 self.repo.git.commit(message=msg)
38
39 def test_blob_to_dict(self):
40 """
41 Test convertion of git blobs to dictionary
42 """
43 valuesmap = { "foo" : "1", "bar" : "2" }
44 self.commit_vars(to_add = valuesmap)
45
46 blob = self.repo.head.commit.tree.blobs[0]
47 self.assertEqual(valuesmap, blob_to_dict(blob),
48 "commit was not translated correctly to dictionary")
49
50 def test_compare_dict_blobs(self):
51 """
52 Test comparisson of dictionaries extracted from git blobs
53 """
54 changesmap = { "foo-2" : ("2", "8"), "bar" : ("","4"), "bar-2" : ("","5")}
55
56 self.commit_vars(to_add = { "foo" : "1", "foo-2" : "2", "foo-3" : "3" })
57 blob1 = self.repo.heads.master.commit.tree.blobs[0]
58
59 self.commit_vars(to_add = { "foo-2" : "8", "bar" : "4", "bar-2" : "5" })
60 blob2 = self.repo.heads.master.commit.tree.blobs[0]
61
62 change_records = compare_dict_blobs(os.path.join(self.repo_path, self.test_file),
63 blob1, blob2, False, False)
64
65 var_changes = { x.fieldname : (x.oldvalue, x.newvalue) for x in change_records}
66 self.assertEqual(changesmap, var_changes, "Changes not reported correctly")
67
68 def test_compare_dict_blobs_default(self):
69 """
70 Test default values for comparisson of git blob dictionaries
71 """
72 defaultmap = { x : ("default", "1") for x in ["PKG", "PKGE", "PKGV", "PKGR"]}
73
74 self.commit_vars(to_add = { "foo" : "1" })
75 blob1 = self.repo.heads.master.commit.tree.blobs[0]
76
77 self.commit_vars(to_add = { "PKG" : "1", "PKGE" : "1", "PKGV" : "1", "PKGR" : "1" })
78 blob2 = self.repo.heads.master.commit.tree.blobs[0]
79
80 change_records = compare_dict_blobs(os.path.join(self.repo_path, self.test_file),
81 blob1, blob2, False, False)
82
83 var_changes = {}
84 for x in change_records:
85 oldvalue = "default" if ("default" in x.oldvalue) else x.oldvalue
86 var_changes[x.fieldname] = (oldvalue, x.newvalue)
87
88 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2import oe.qa
3
4class TestElf(OESelftestTestCase):
5 def test_machine_name(self):
6 """
7 Test elf_machine_to_string()
8 """
9 self.assertEqual(oe.qa.elf_machine_to_string(0x02), "SPARC")
10 self.assertEqual(oe.qa.elf_machine_to_string(0x03), "x86")
11 self.assertEqual(oe.qa.elf_machine_to_string(0x08), "MIPS")
12 self.assertEqual(oe.qa.elf_machine_to_string(0x14), "PowerPC")
13 self.assertEqual(oe.qa.elf_machine_to_string(0x28), "ARM")
14 self.assertEqual(oe.qa.elf_machine_to_string(0x2A), "SuperH")
15 self.assertEqual(oe.qa.elf_machine_to_string(0x32), "IA-64")
16 self.assertEqual(oe.qa.elf_machine_to_string(0x3E), "x86-64")
17 self.assertEqual(oe.qa.elf_machine_to_string(0xB7), "AArch64")
18
19 self.assertEqual(oe.qa.elf_machine_to_string(0x00), "Unknown (0)")
20 self.assertEqual(oe.qa.elf_machine_to_string(0xDEADBEEF), "Unknown (3735928559)")
21 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2import oe.license
3
4class SeenVisitor(oe.license.LicenseVisitor):
5 def __init__(self):
6 self.seen = []
7 oe.license.LicenseVisitor.__init__(self)
8
9 def visit_Str(self, node):
10 self.seen.append(node.s)
11
12class TestSingleLicense(OESelftestTestCase):
13 licenses = [
14 "GPLv2",
15 "LGPL-2.0",
16 "Artistic",
17 "MIT",
18 "GPLv3+",
19 "FOO_BAR",
20 ]
21 invalid_licenses = ["GPL/BSD"]
22
23 @staticmethod
24 def parse(licensestr):
25 visitor = SeenVisitor()
26 visitor.visit_string(licensestr)
27 return visitor.seen
28
29 def test_single_licenses(self):
30 for license in self.licenses:
31 licenses = self.parse(license)
32 self.assertListEqual(licenses, [license])
33
34 def test_invalid_licenses(self):
35 for license in self.invalid_licenses:
36 with self.assertRaises(oe.license.InvalidLicense) as cm:
37 self.parse(license)
38 self.assertEqual(cm.exception.license, license)
39
40class TestSimpleCombinations(OESelftestTestCase):
41 tests = {
42 "FOO&BAR": ["FOO", "BAR"],
43 "BAZ & MOO": ["BAZ", "MOO"],
44 "ALPHA|BETA": ["ALPHA"],
45 "BAZ&MOO|FOO": ["FOO"],
46 "FOO&BAR|BAZ": ["FOO", "BAR"],
47 }
48 preferred = ["ALPHA", "FOO", "BAR"]
49
50 def test_tests(self):
51 def choose(a, b):
52 if all(lic in self.preferred for lic in b):
53 return b
54 else:
55 return a
56
57 for license, expected in self.tests.items():
58 licenses = oe.license.flattened_licenses(license, choose)
59 self.assertListEqual(licenses, expected)
60
61class TestComplexCombinations(TestSimpleCombinations):
62 tests = {
63 "FOO & (BAR | BAZ)&MOO": ["FOO", "BAR", "MOO"],
64 "(ALPHA|(BETA&THETA)|OMEGA)&DELTA": ["OMEGA", "DELTA"],
65 "((ALPHA|BETA)&FOO)|BAZ": ["BETA", "FOO"],
66 "(GPL-2.0|Proprietary)&BSD-4-clause&MIT": ["GPL-2.0", "BSD-4-clause", "MIT"],
67 }
68 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2import oe, oe.path
3import tempfile
4import os
5import errno
6import shutil
7
8class TestRealPath(OESelftestTestCase):
9 DIRS = [ "a", "b", "etc", "sbin", "usr", "usr/bin", "usr/binX", "usr/sbin", "usr/include", "usr/include/gdbm" ]
10 FILES = [ "etc/passwd", "b/file" ]
11 LINKS = [
12 ( "bin", "/usr/bin", "/usr/bin" ),
13 ( "binX", "usr/binX", "/usr/binX" ),
14 ( "c", "broken", "/broken" ),
15 ( "etc/passwd-1", "passwd", "/etc/passwd" ),
16 ( "etc/passwd-2", "passwd-1", "/etc/passwd" ),
17 ( "etc/passwd-3", "/etc/passwd-1", "/etc/passwd" ),
18 ( "etc/shadow-1", "/etc/shadow", "/etc/shadow" ),
19 ( "etc/shadow-2", "/etc/shadow-1", "/etc/shadow" ),
20 ( "prog-A", "bin/prog-A", "/usr/bin/prog-A" ),
21 ( "prog-B", "/bin/prog-B", "/usr/bin/prog-B" ),
22 ( "usr/bin/prog-C", "../../sbin/prog-C", "/sbin/prog-C" ),
23 ( "usr/bin/prog-D", "/sbin/prog-D", "/sbin/prog-D" ),
24 ( "usr/binX/prog-E", "../sbin/prog-E", None ),
25 ( "usr/bin/prog-F", "../../../sbin/prog-F", "/sbin/prog-F" ),
26 ( "loop", "a/loop", None ),
27 ( "a/loop", "../loop", None ),
28 ( "b/test", "file/foo", "/b/file/foo" ),
29 ]
30
31 LINKS_PHYS = [
32 ( "./", "/", "" ),
33 ( "binX/prog-E", "/usr/sbin/prog-E", "/sbin/prog-E" ),
34 ]
35
36 EXCEPTIONS = [
37 ( "loop", errno.ELOOP ),
38 ( "b/test", errno.ENOENT ),
39 ]
40
41 def __del__(self):
42 try:
43 #os.system("tree -F %s" % self.tmpdir)
44 shutil.rmtree(self.tmpdir)
45 except:
46 pass
47
48 def setUp(self):
49 self.tmpdir = tempfile.mkdtemp(prefix = "oe-test_path")
50 self.root = os.path.join(self.tmpdir, "R")
51
52 os.mkdir(os.path.join(self.tmpdir, "_real"))
53 os.symlink("_real", self.root)
54
55 for d in self.DIRS:
56 os.mkdir(os.path.join(self.root, d))
57 for f in self.FILES:
58 open(os.path.join(self.root, f), "w")
59 for l in self.LINKS:
60 os.symlink(l[1], os.path.join(self.root, l[0]))
61
62 def __realpath(self, file, use_physdir, assume_dir = True):
63 return oe.path.realpath(os.path.join(self.root, file), self.root,
64 use_physdir, assume_dir = assume_dir)
65
66 def test_norm(self):
67 for l in self.LINKS:
68 if l[2] == None:
69 continue
70
71 target_p = self.__realpath(l[0], True)
72 target_l = self.__realpath(l[0], False)
73
74 if l[2] != False:
75 self.assertEqual(target_p, target_l)
76 self.assertEqual(l[2], target_p[len(self.root):])
77
78 def test_phys(self):
79 for l in self.LINKS_PHYS:
80 target_p = self.__realpath(l[0], True)
81 target_l = self.__realpath(l[0], False)
82
83 self.assertEqual(l[1], target_p[len(self.root):])
84 self.assertEqual(l[2], target_l[len(self.root):])
85
86 def test_loop(self):
87 for e in self.EXCEPTIONS:
88 self.assertRaisesRegex(OSError, r'\[Errno %u\]' % e[1],
89 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2from oe.maketype import create
3
4class TestBooleanType(OESelftestTestCase):
5 def test_invalid(self):
6 self.assertRaises(ValueError, create, '', 'boolean')
7 self.assertRaises(ValueError, create, 'foo', 'boolean')
8 self.assertRaises(TypeError, create, object(), 'boolean')
9
10 def test_true(self):
11 self.assertTrue(create('y', 'boolean'))
12 self.assertTrue(create('yes', 'boolean'))
13 self.assertTrue(create('1', 'boolean'))
14 self.assertTrue(create('t', 'boolean'))
15 self.assertTrue(create('true', 'boolean'))
16 self.assertTrue(create('TRUE', 'boolean'))
17 self.assertTrue(create('truE', 'boolean'))
18
19 def test_false(self):
20 self.assertFalse(create('n', 'boolean'))
21 self.assertFalse(create('no', 'boolean'))
22 self.assertFalse(create('0', 'boolean'))
23 self.assertFalse(create('f', 'boolean'))
24 self.assertFalse(create('false', 'boolean'))
25 self.assertFalse(create('FALSE', 'boolean'))
26 self.assertFalse(create('faLse', 'boolean'))
27
28 def test_bool_equality(self):
29 self.assertEqual(create('n', 'boolean'), False)
30 self.assertNotEqual(create('n', 'boolean'), True)
31 self.assertEqual(create('y', 'boolean'), True)
32 self.assertNotEqual(create('y', 'boolean'), False)
33
34class TestList(OESelftestTestCase):
35 def assertListEqual(self, value, valid, sep=None):
36 obj = create(value, 'list', separator=sep)
37 self.assertEqual(obj, valid)
38 if sep is not None:
39 self.assertEqual(obj.separator, sep)
40 self.assertEqual(str(obj), obj.separator.join(obj))
41
42 def test_list_nosep(self):
43 testlist = ['alpha', 'beta', 'theta']
44 self.assertListEqual('alpha beta theta', testlist)
45 self.assertListEqual('alpha beta\ttheta', testlist)
46 self.assertListEqual('alpha', ['alpha'])
47
48 def test_list_usersep(self):
49 self.assertListEqual('foo:bar', ['foo', 'bar'], ':')
50 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2from oe.utils import packages_filter_out_system, trim_version
3
4class TestPackagesFilterOutSystem(OESelftestTestCase):
5 def test_filter(self):
6 """
7 Test that oe.utils.packages_filter_out_system works.
8 """
9 try:
10 import bb
11 except ImportError:
12 self.skipTest("Cannot import bb")
13
14 d = bb.data_smart.DataSmart()
15 d.setVar("PN", "foo")
16
17 d.setVar("PACKAGES", "foo foo-doc foo-dev")
18 pkgs = packages_filter_out_system(d)
19 self.assertEqual(pkgs, [])
20
21 d.setVar("PACKAGES", "foo foo-doc foo-data foo-dev")
22 pkgs = packages_filter_out_system(d)
23 self.assertEqual(pkgs, ["foo-data"])
24
25 d.setVar("PACKAGES", "foo foo-locale-en-gb")
26 pkgs = packages_filter_out_system(d)
27 self.assertEqual(pkgs, [])
28
29 d.setVar("PACKAGES", "foo foo-data foo-locale-en-gb")
30 pkgs = packages_filter_out_system(d)
31 self.assertEqual(pkgs, ["foo-data"])
32
33
34class TestTrimVersion(OESelftestTestCase):
35 def test_version_exception(self):
36 with self.assertRaises(TypeError):
37 trim_version(None, 2)
38 with self.assertRaises(TypeError):
39 trim_version((1, 2, 3), 2)
40
41 def test_num_exception(self):
42 with self.assertRaises(ValueError):
43 trim_version("1.2.3", 0)
44 with self.assertRaises(ValueError):
45 trim_version("1.2.3", -1)
46
47 def test_valid(self):
48 self.assertEqual(trim_version("1.2.3", 1), "1")
49 self.assertEqual(trim_version("1.2.3", 2), "1.2")
50 self.assertEqual(trim_version("1.2.3", 3), "1.2.3")
51 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2from oeqa.selftest.cases.buildhistory import BuildhistoryBase
3from oeqa.utils.commands import Command, runCmd, bitbake, get_bb_var, get_test_layer
4from oeqa.core.decorator.oeid import OETestID
5
6class BuildhistoryDiffTests(BuildhistoryBase):
7
8 @OETestID(295)
9 def test_buildhistory_diff(self):
10 target = 'xcursor-transparent-theme'
11 self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True)
12 self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True)
13 result = runCmd("buildhistory-diff -p %s" % get_bb_var('BUILDHISTORY_DIR'))
14 expected_output = 'PR changed from "r1" to "r0"'
15 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2from oeqa.utils.commands import bitbake, get_bb_vars
3import subprocess, os
4import oe.path
5
6class VersionOrdering(OESelftestTestCase):
7 # version1, version2, sort order
8 tests = (
9 ("1.0", "1.0", 0),
10 ("1.0", "2.0", -1),
11 ("2.0", "1.0", 1),
12 ("2.0-rc", "2.0", 1),
13 ("2.0~rc", "2.0", -1),
14 ("1.2rc2", "1.2.0", -1)
15 )
16
17 @classmethod
18 def setUpClass(cls):
19 # Build the tools we need and populate a sysroot
20 bitbake("dpkg-native opkg-native rpm-native python3-native")
21 bitbake("build-sysroots -c build_native_sysroot")
22
23 # Get the paths so we can point into the sysroot correctly
24 vars = get_bb_vars(["STAGING_DIR", "BUILD_ARCH", "bindir_native", "libdir_native"])
25 cls.staging = oe.path.join(vars["STAGING_DIR"], vars["BUILD_ARCH"])
26 cls.bindir = oe.path.join(cls.staging, vars["bindir_native"])
27 cls.libdir = oe.path.join(cls.staging, vars["libdir_native"])
28
29 def setUp(self):
30 # Just for convenience
31 self.staging = type(self).staging
32 self.bindir = type(self).bindir
33 self.libdir = type(self).libdir
34
35 def test_dpkg(self):
36 for ver1, ver2, sort in self.tests:
37 op = { -1: "<<", 0: "=", 1: ">>" }[sort]
38 status = subprocess.call((oe.path.join(self.bindir, "dpkg"), "--compare-versions", ver1, op, ver2))
39 self.assertEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
40
41 # Now do it again but with incorrect operations
42 op = { -1: ">>", 0: ">>", 1: "<<" }[sort]
43 status = subprocess.call((oe.path.join(self.bindir, "dpkg"), "--compare-versions", ver1, op, ver2))
44 self.assertNotEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
45
46 # Now do it again but with incorrect operations
47 op = { -1: "=", 0: "<<", 1: "=" }[sort]
48 status = subprocess.call((oe.path.join(self.bindir, "dpkg"), "--compare-versions", ver1, op, ver2))
49 self.assertNotEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
50
51 def test_opkg(self):
52 for ver1, ver2, sort in self.tests:
53 op = { -1: "<<", 0: "=", 1: ">>" }[sort]
54 status = subprocess.call((oe.path.join(self.bindir, "opkg"), "compare-versions", ver1, op, ver2))
55 self.assertEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
56
57 # Now do it again but with incorrect operations
58 op = { -1: ">>", 0: ">>", 1: "<<" }[sort]
59 status = subprocess.call((oe.path.join(self.bindir, "opkg"), "compare-versions", ver1, op, ver2))
60 self.assertNotEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
61
62 # Now do it again but with incorrect operations
63 op = { -1: "=", 0: "<<", 1: "=" }[sort]
64 status = subprocess.call((oe.path.join(self.bindir, "opkg"), "compare-versions", ver1, op, ver2))
65 self.assertNotEqual(status, 0, "%s %s %s failed" % (ver1, op, ver2))
66
67 def test_rpm(self):
68 # Need to tell the Python bindings where to find its configuration
69 env = os.environ.copy()
70 env["RPM_CONFIGDIR"] = oe.path.join(self.libdir, "rpm")
71
72 for ver1, ver2, sort in self.tests:
73 # The only way to test rpm is via the Python module, so we need to
74 # execute python3-native. labelCompare returns -1/0/1 (like strcmp)
75 # so add 100 and use that as the exit code.
76 command = (oe.path.join(self.bindir, "python3-native", "python3"), "-c",
77 "import sys, rpm; v1=(None, \"%s\", None); v2=(None, \"%s\", None); sys.exit(rpm.labelCompare(v1, v2) + 100)" % (ver1, ver2))
78 status = subprocess.call(command, env=env)
79 self.assertIn(status, (99, 100, 101))
80 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 @@
1import os
2import tempfile
3import fnmatch
4
5from oeqa.selftest.case import OESelftestTestCase
6from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
7from oeqa.core.decorator.oeid import OETestID
8
9class OePkgdataUtilTests(OESelftestTestCase):
10
11 @classmethod
12 def setUpClass(cls):
13 super(OePkgdataUtilTests, cls).setUpClass()
14 # Ensure we have the right data in pkgdata
15 cls.logger.info('Running bitbake to generate pkgdata')
16 bitbake('busybox zlib m4')
17
18 @OETestID(1203)
19 def test_lookup_pkg(self):
20 # Forward tests
21 result = runCmd('oe-pkgdata-util lookup-pkg "zlib busybox"')
22 self.assertEqual(result.output, 'libz1\nbusybox')
23 result = runCmd('oe-pkgdata-util lookup-pkg zlib-dev')
24 self.assertEqual(result.output, 'libz-dev')
25 result = runCmd('oe-pkgdata-util lookup-pkg nonexistentpkg', ignore_status=True)
26 self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
27 self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
28 # Reverse tests
29 result = runCmd('oe-pkgdata-util lookup-pkg -r "libz1 busybox"')
30 self.assertEqual(result.output, 'zlib\nbusybox')
31 result = runCmd('oe-pkgdata-util lookup-pkg -r libz-dev')
32 self.assertEqual(result.output, 'zlib-dev')
33 result = runCmd('oe-pkgdata-util lookup-pkg -r nonexistentpkg', ignore_status=True)
34 self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
35 self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
36
37 @OETestID(1205)
38 def test_read_value(self):
39 result = runCmd('oe-pkgdata-util read-value PN libz1')
40 self.assertEqual(result.output, 'zlib')
41 result = runCmd('oe-pkgdata-util read-value PKG libz1')
42 self.assertEqual(result.output, 'libz1')
43 result = runCmd('oe-pkgdata-util read-value PKGSIZE m4')
44 pkgsize = int(result.output.strip())
45 self.assertGreater(pkgsize, 1, "Size should be greater than 1. %s" % result.output)
46
47 @OETestID(1198)
48 def test_find_path(self):
49 result = runCmd('oe-pkgdata-util find-path /lib/libz.so.1')
50 self.assertEqual(result.output, 'zlib: /lib/libz.so.1')
51 result = runCmd('oe-pkgdata-util find-path /usr/bin/m4')
52 self.assertEqual(result.output, 'm4: /usr/bin/m4')
53 result = runCmd('oe-pkgdata-util find-path /not/exist', ignore_status=True)
54 self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
55 self.assertEqual(result.output, 'ERROR: Unable to find any package producing path /not/exist')
56
57 @OETestID(1204)
58 def test_lookup_recipe(self):
59 result = runCmd('oe-pkgdata-util lookup-recipe "libz-staticdev busybox"')
60 self.assertEqual(result.output, 'zlib\nbusybox')
61 result = runCmd('oe-pkgdata-util lookup-recipe libz-dbg')
62 self.assertEqual(result.output, 'zlib')
63 result = runCmd('oe-pkgdata-util lookup-recipe nonexistentpkg', ignore_status=True)
64 self.assertEqual(result.status, 1, "Status different than 1. output: %s" % result.output)
65 self.assertEqual(result.output, 'ERROR: The following packages could not be found: nonexistentpkg')
66
67 @OETestID(1202)
68 def test_list_pkgs(self):
69 # No arguments
70 result = runCmd('oe-pkgdata-util list-pkgs')
71 pkglist = result.output.split()
72 self.assertIn('zlib', pkglist, "Listed packages: %s" % result.output)
73 self.assertIn('zlib-dev', pkglist, "Listed packages: %s" % result.output)
74 # No pkgspec, runtime
75 result = runCmd('oe-pkgdata-util list-pkgs -r')
76 pkglist = result.output.split()
77 self.assertIn('libz-dev', pkglist, "Listed packages: %s" % result.output)
78 # With recipe specified
79 result = runCmd('oe-pkgdata-util list-pkgs -p zlib')
80 pkglist = sorted(result.output.split())
81 try:
82 pkglist.remove('zlib-ptest') # in case ptest is disabled
83 except ValueError:
84 pass
85 self.assertEqual(pkglist, ['zlib', 'zlib-dbg', 'zlib-dev', 'zlib-doc', 'zlib-staticdev'], "Packages listed after remove: %s" % result.output)
86 # With recipe specified, runtime
87 result = runCmd('oe-pkgdata-util list-pkgs -p zlib -r')
88 pkglist = sorted(result.output.split())
89 try:
90 pkglist.remove('libz-ptest') # in case ptest is disabled
91 except ValueError:
92 pass
93 self.assertEqual(pkglist, ['libz-dbg', 'libz-dev', 'libz-doc', 'libz-staticdev', 'libz1'], "Packages listed after remove: %s" % result.output)
94 # With recipe specified and unpackaged
95 result = runCmd('oe-pkgdata-util list-pkgs -p zlib -u')
96 pkglist = sorted(result.output.split())
97 self.assertIn('zlib-locale', pkglist, "Listed packages: %s" % result.output)
98 # With recipe specified and unpackaged, runtime
99 result = runCmd('oe-pkgdata-util list-pkgs -p zlib -u -r')
100 pkglist = sorted(result.output.split())
101 self.assertIn('libz-locale', pkglist, "Listed packages: %s" % result.output)
102 # With recipe specified and pkgspec
103 result = runCmd('oe-pkgdata-util list-pkgs -p zlib "*-d*"')
104 pkglist = sorted(result.output.split())
105 self.assertEqual(pkglist, ['zlib-dbg', 'zlib-dev', 'zlib-doc'], "Packages listed: %s" % result.output)
106 # With recipe specified and pkgspec, runtime
107 result = runCmd('oe-pkgdata-util list-pkgs -p zlib -r "*-d*"')
108 pkglist = sorted(result.output.split())
109 self.assertEqual(pkglist, ['libz-dbg', 'libz-dev', 'libz-doc'], "Packages listed: %s" % result.output)
110
111 @OETestID(1201)
112 def test_list_pkg_files(self):
113 def splitoutput(output):
114 files = {}
115 curpkg = None
116 for line in output.splitlines():
117 if line.startswith('\t'):
118 self.assertTrue(curpkg, 'Unexpected non-package line:\n%s' % line)
119 files[curpkg].append(line.strip())
120 else:
121 self.assertTrue(line.rstrip().endswith(':'), 'Invalid package line in output:\n%s' % line)
122 curpkg = line.split(':')[0]
123 files[curpkg] = []
124 return files
125 bb_vars = get_bb_vars(['base_libdir', 'libdir', 'includedir', 'mandir'])
126 base_libdir = bb_vars['base_libdir']
127 libdir = bb_vars['libdir']
128 includedir = bb_vars['includedir']
129 mandir = bb_vars['mandir']
130 # Test recipe-space package name
131 result = runCmd('oe-pkgdata-util list-pkg-files zlib-dev zlib-doc')
132 files = splitoutput(result.output)
133 self.assertIn('zlib-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
134 self.assertIn('zlib-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
135 self.assertIn(os.path.join(includedir, 'zlib.h'), files['zlib-dev'])
136 self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['zlib-doc'])
137 # Test runtime package name
138 result = runCmd('oe-pkgdata-util list-pkg-files -r libz1 libz-dev')
139 files = splitoutput(result.output)
140 self.assertIn('libz1', list(files.keys()), "listed pkgs. files: %s" %result.output)
141 self.assertIn('libz-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
142 self.assertGreater(len(files['libz1']), 1)
143 libspec = os.path.join(base_libdir, 'libz.so.1.*')
144 found = False
145 for fileitem in files['libz1']:
146 if fnmatch.fnmatchcase(fileitem, libspec):
147 found = True
148 break
149 self.assertTrue(found, 'Could not find zlib library file %s in libz1 package file list: %s' % (libspec, files['libz1']))
150 self.assertIn(os.path.join(includedir, 'zlib.h'), files['libz-dev'])
151 # Test recipe
152 result = runCmd('oe-pkgdata-util list-pkg-files -p zlib')
153 files = splitoutput(result.output)
154 self.assertIn('zlib-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
155 self.assertIn('zlib-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
156 self.assertIn('zlib-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
157 self.assertIn('zlib-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
158 self.assertIn('zlib', list(files.keys()), "listed pkgs. files: %s" %result.output)
159 self.assertNotIn('zlib-locale', list(files.keys()), "listed pkgs. files: %s" %result.output)
160 # (ignore ptest, might not be there depending on config)
161 self.assertIn(os.path.join(includedir, 'zlib.h'), files['zlib-dev'])
162 self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['zlib-doc'])
163 self.assertIn(os.path.join(libdir, 'libz.a'), files['zlib-staticdev'])
164 # Test recipe, runtime
165 result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -r')
166 files = splitoutput(result.output)
167 self.assertIn('libz-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
168 self.assertIn('libz-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
169 self.assertIn('libz-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
170 self.assertIn('libz-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
171 self.assertIn('libz1', list(files.keys()), "listed pkgs. files: %s" %result.output)
172 self.assertNotIn('libz-locale', list(files.keys()), "listed pkgs. files: %s" %result.output)
173 self.assertIn(os.path.join(includedir, 'zlib.h'), files['libz-dev'])
174 self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['libz-doc'])
175 self.assertIn(os.path.join(libdir, 'libz.a'), files['libz-staticdev'])
176 # Test recipe, unpackaged
177 result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -u')
178 files = splitoutput(result.output)
179 self.assertIn('zlib-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
180 self.assertIn('zlib-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
181 self.assertIn('zlib-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
182 self.assertIn('zlib-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
183 self.assertIn('zlib', list(files.keys()), "listed pkgs. files: %s" %result.output)
184 self.assertIn('zlib-locale', list(files.keys()), "listed pkgs. files: %s" %result.output) # this is the key one
185 self.assertIn(os.path.join(includedir, 'zlib.h'), files['zlib-dev'])
186 self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['zlib-doc'])
187 self.assertIn(os.path.join(libdir, 'libz.a'), files['zlib-staticdev'])
188 # Test recipe, runtime, unpackaged
189 result = runCmd('oe-pkgdata-util list-pkg-files -p zlib -r -u')
190 files = splitoutput(result.output)
191 self.assertIn('libz-dbg', list(files.keys()), "listed pkgs. files: %s" %result.output)
192 self.assertIn('libz-doc', list(files.keys()), "listed pkgs. files: %s" %result.output)
193 self.assertIn('libz-dev', list(files.keys()), "listed pkgs. files: %s" %result.output)
194 self.assertIn('libz-staticdev', list(files.keys()), "listed pkgs. files: %s" %result.output)
195 self.assertIn('libz1', list(files.keys()), "listed pkgs. files: %s" %result.output)
196 self.assertIn('libz-locale', list(files.keys()), "listed pkgs. files: %s" %result.output) # this is the key one
197 self.assertIn(os.path.join(includedir, 'zlib.h'), files['libz-dev'])
198 self.assertIn(os.path.join(mandir, 'man3/zlib.3'), files['libz-doc'])
199 self.assertIn(os.path.join(libdir, 'libz.a'), files['libz-staticdev'])
200
201 @OETestID(1200)
202 def test_glob(self):
203 tempdir = tempfile.mkdtemp(prefix='pkgdataqa')
204 self.track_for_cleanup(tempdir)
205 pkglistfile = os.path.join(tempdir, 'pkglist')
206 with open(pkglistfile, 'w') as f:
207 f.write('libz1\n')
208 f.write('busybox\n')
209 result = runCmd('oe-pkgdata-util glob %s "*-dev"' % pkglistfile)
210 desiredresult = ['libz-dev', 'busybox-dev']
211 self.assertEqual(sorted(result.output.split()), sorted(desiredresult))
212 # The following should not error (because when we use this during rootfs construction, sometimes the complementary package won't exist)
213 result = runCmd('oe-pkgdata-util glob %s "*-nonexistent"' % pkglistfile)
214 self.assertEqual(result.output, '')
215 # Test exclude option
216 result = runCmd('oe-pkgdata-util glob %s "*-dev *-dbg" -x "^libz"' % pkglistfile)
217 resultlist = result.output.split()
218 self.assertNotIn('libz-dev', resultlist)
219 self.assertNotIn('libz-dbg', resultlist)
220
221 @OETestID(1206)
222 def test_specify_pkgdatadir(self):
223 result = runCmd('oe-pkgdata-util -p %s lookup-pkg zlib' % get_bb_var('PKGDATA_DIR'))
224 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 @@
1import os
2import re
3import shutil
4import datetime
5
6import oeqa.utils.ftools as ftools
7from oeqa.selftest.case import OESelftestTestCase
8from oeqa.utils.commands import runCmd, bitbake, get_bb_var
9from oeqa.core.decorator.oeid import OETestID
10from oeqa.utils.network import get_free_port
11
12class BitbakePrTests(OESelftestTestCase):
13
14 @classmethod
15 def setUpClass(cls):
16 super(BitbakePrTests, cls).setUpClass()
17 cls.pkgdata_dir = get_bb_var('PKGDATA_DIR')
18
19 def get_pr_version(self, package_name):
20 package_data_file = os.path.join(self.pkgdata_dir, 'runtime', package_name)
21 package_data = ftools.read_file(package_data_file)
22 find_pr = re.search("PKGR: r[0-9]+\.([0-9]+)", package_data)
23 self.assertTrue(find_pr, "No PKG revision found in %s" % package_data_file)
24 return int(find_pr.group(1))
25
26 def get_task_stamp(self, package_name, recipe_task):
27 stampdata = get_bb_var('STAMP', target=package_name).split('/')
28 prefix = stampdata[-1]
29 package_stamps_path = "/".join(stampdata[:-1])
30 stamps = []
31 for stamp in os.listdir(package_stamps_path):
32 find_stamp = re.match("%s\.%s\.([a-z0-9]{32})" % (re.escape(prefix), recipe_task), stamp)
33 if find_stamp:
34 stamps.append(find_stamp.group(1))
35 self.assertFalse(len(stamps) == 0, msg="Cound not find stamp for task %s for recipe %s" % (recipe_task, package_name))
36 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))
37 return str(stamps[0])
38
39 def increment_package_pr(self, package_name):
40 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()
41 self.write_recipeinc(package_name, inc_data)
42 res = bitbake(package_name, ignore_status=True)
43 self.delete_recipeinc(package_name)
44 self.assertEqual(res.status, 0, msg=res.output)
45 self.assertTrue("NOTE: Started PRServer with DBfile" in res.output, msg=res.output)
46
47 def config_pr_tests(self, package_name, package_type='rpm', pr_socket='localhost:0'):
48 config_package_data = 'PACKAGE_CLASSES = "package_%s"' % package_type
49 self.write_config(config_package_data)
50 config_server_data = 'PRSERV_HOST = "%s"' % pr_socket
51 self.append_config(config_server_data)
52
53 def run_test_pr_service(self, package_name, package_type='rpm', track_task='do_package', pr_socket='localhost:0'):
54 self.config_pr_tests(package_name, package_type, pr_socket)
55
56 self.increment_package_pr(package_name)
57 pr_1 = self.get_pr_version(package_name)
58 stamp_1 = self.get_task_stamp(package_name, track_task)
59
60 self.increment_package_pr(package_name)
61 pr_2 = self.get_pr_version(package_name)
62 stamp_2 = self.get_task_stamp(package_name, track_task)
63
64 self.assertTrue(pr_2 - pr_1 == 1, "Step between same pkg. revision is greater than 1")
65 self.assertTrue(stamp_1 != stamp_2, "Different pkg rev. but same stamp: %s" % stamp_1)
66
67 def run_test_pr_export_import(self, package_name, replace_current_db=True):
68 self.config_pr_tests(package_name)
69
70 self.increment_package_pr(package_name)
71 pr_1 = self.get_pr_version(package_name)
72
73 exported_db_path = os.path.join(self.builddir, 'export.inc')
74 export_result = runCmd("bitbake-prserv-tool export %s" % exported_db_path, ignore_status=True)
75 self.assertEqual(export_result.status, 0, msg="PR Service database export failed: %s" % export_result.output)
76
77 if replace_current_db:
78 current_db_path = os.path.join(get_bb_var('PERSISTENT_DIR'), 'prserv.sqlite3')
79 self.assertTrue(os.path.exists(current_db_path), msg="Path to current PR Service database is invalid: %s" % current_db_path)
80 os.remove(current_db_path)
81
82 import_result = runCmd("bitbake-prserv-tool import %s" % exported_db_path, ignore_status=True)
83 os.remove(exported_db_path)
84 self.assertEqual(import_result.status, 0, msg="PR Service database import failed: %s" % import_result.output)
85
86 self.increment_package_pr(package_name)
87 pr_2 = self.get_pr_version(package_name)
88
89 self.assertTrue(pr_2 - pr_1 == 1, "Step between same pkg. revision is greater than 1")
90
91 @OETestID(930)
92 def test_import_export_replace_db(self):
93 self.run_test_pr_export_import('m4')
94
95 @OETestID(931)
96 def test_import_export_override_db(self):
97 self.run_test_pr_export_import('m4', replace_current_db=False)
98
99 @OETestID(932)
100 def test_pr_service_rpm_arch_dep(self):
101 self.run_test_pr_service('m4', 'rpm', 'do_package')
102
103 @OETestID(934)
104 def test_pr_service_deb_arch_dep(self):
105 self.run_test_pr_service('m4', 'deb', 'do_package')
106
107 @OETestID(933)
108 def test_pr_service_ipk_arch_dep(self):
109 self.run_test_pr_service('m4', 'ipk', 'do_package')
110
111 @OETestID(935)
112 def test_pr_service_rpm_arch_indep(self):
113 self.run_test_pr_service('xcursor-transparent-theme', 'rpm', 'do_package')
114
115 @OETestID(937)
116 def test_pr_service_deb_arch_indep(self):
117 self.run_test_pr_service('xcursor-transparent-theme', 'deb', 'do_package')
118
119 @OETestID(936)
120 def test_pr_service_ipk_arch_indep(self):
121 self.run_test_pr_service('xcursor-transparent-theme', 'ipk', 'do_package')
122
123 @OETestID(1419)
124 def test_stopping_prservice_message(self):
125 port = get_free_port()
126
127 runCmd('bitbake-prserv --host localhost --port %s --loglevel=DEBUG --start' % port)
128 ret = runCmd('bitbake-prserv --host localhost --port %s --loglevel=DEBUG --stop' % port)
129
130 self.assertEqual(ret.status, 0)
131
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 @@
1import os
2import shutil
3import tempfile
4import urllib.parse
5
6from oeqa.utils.commands import runCmd, bitbake, get_bb_var
7from oeqa.utils.commands import get_bb_vars, create_temp_layer
8from oeqa.core.decorator.oeid import OETestID
9from oeqa.selftest.cases import devtool
10
11templayerdir = None
12
13def setUpModule():
14 global templayerdir
15 templayerdir = tempfile.mkdtemp(prefix='recipetoolqa')
16 create_temp_layer(templayerdir, 'selftestrecipetool')
17 runCmd('bitbake-layers add-layer %s' % templayerdir)
18
19
20def tearDownModule():
21 runCmd('bitbake-layers remove-layer %s' % templayerdir, ignore_status=True)
22 runCmd('rm -rf %s' % templayerdir)
23
24
25class RecipetoolBase(devtool.DevtoolBase):
26
27 def setUpLocal(self):
28 super(RecipetoolBase, self).setUpLocal()
29 self.templayerdir = templayerdir
30 self.tempdir = tempfile.mkdtemp(prefix='recipetoolqa')
31 self.track_for_cleanup(self.tempdir)
32 self.testfile = os.path.join(self.tempdir, 'testfile')
33 with open(self.testfile, 'w') as f:
34 f.write('Test file\n')
35
36 def tearDownLocal(self):
37 runCmd('rm -rf %s/recipes-*' % self.templayerdir)
38 super(RecipetoolBase, self).tearDownLocal()
39
40 def _try_recipetool_appendcmd(self, cmd, testrecipe, expectedfiles, expectedlines=None):
41 result = runCmd(cmd)
42 self.assertNotIn('Traceback', result.output)
43
44 # Check the bbappend was created and applies properly
45 recipefile = get_bb_var('FILE', testrecipe)
46 bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir)
47
48 # Check the bbappend contents
49 if expectedlines is not None:
50 with open(bbappendfile, 'r') as f:
51 self.assertEqual(expectedlines, f.readlines(), "Expected lines are not present in %s" % bbappendfile)
52
53 # Check file was copied
54 filesdir = os.path.join(os.path.dirname(bbappendfile), testrecipe)
55 for expectedfile in expectedfiles:
56 self.assertTrue(os.path.isfile(os.path.join(filesdir, expectedfile)), 'Expected file %s to be copied next to bbappend, but it wasn\'t' % expectedfile)
57
58 # Check no other files created
59 createdfiles = []
60 for root, _, files in os.walk(filesdir):
61 for f in files:
62 createdfiles.append(os.path.relpath(os.path.join(root, f), filesdir))
63 self.assertTrue(sorted(createdfiles), sorted(expectedfiles))
64
65 return bbappendfile, result.output
66
67
68class RecipetoolTests(RecipetoolBase):
69
70 @classmethod
71 def setUpClass(cls):
72 super(RecipetoolTests, cls).setUpClass()
73 # Ensure we have the right data in shlibs/pkgdata
74 cls.logger.info('Running bitbake to generate pkgdata')
75 bitbake('-c packagedata base-files coreutils busybox selftest-recipetool-appendfile')
76 bb_vars = get_bb_vars(['COREBASE', 'BBPATH'])
77 cls.corebase = bb_vars['COREBASE']
78 cls.bbpath = bb_vars['BBPATH']
79
80 def _try_recipetool_appendfile(self, testrecipe, destfile, newfile, options, expectedlines, expectedfiles):
81 cmd = 'recipetool appendfile %s %s %s %s' % (self.templayerdir, destfile, newfile, options)
82 return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines)
83
84 def _try_recipetool_appendfile_fail(self, destfile, newfile, checkerror):
85 cmd = 'recipetool appendfile %s %s %s' % (self.templayerdir, destfile, newfile)
86 result = runCmd(cmd, ignore_status=True)
87 self.assertNotEqual(result.status, 0, 'Command "%s" should have failed but didn\'t' % cmd)
88 self.assertNotIn('Traceback', result.output)
89 for errorstr in checkerror:
90 self.assertIn(errorstr, result.output)
91
92 @OETestID(1177)
93 def test_recipetool_appendfile_basic(self):
94 # Basic test
95 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
96 '\n']
97 _, output = self._try_recipetool_appendfile('base-files', '/etc/motd', self.testfile, '', expectedlines, ['motd'])
98 self.assertNotIn('WARNING: ', output)
99
100 @OETestID(1183)
101 def test_recipetool_appendfile_invalid(self):
102 # Test some commands that should error
103 self._try_recipetool_appendfile_fail('/etc/passwd', self.testfile, ['ERROR: /etc/passwd cannot be handled by this tool', 'useradd', 'extrausers'])
104 self._try_recipetool_appendfile_fail('/etc/timestamp', self.testfile, ['ERROR: /etc/timestamp cannot be handled by this tool'])
105 self._try_recipetool_appendfile_fail('/dev/console', self.testfile, ['ERROR: /dev/console cannot be handled by this tool'])
106
107 @OETestID(1176)
108 def test_recipetool_appendfile_alternatives(self):
109 # Now try with a file we know should be an alternative
110 # (this is very much a fake example, but one we know is reliably an alternative)
111 self._try_recipetool_appendfile_fail('/bin/ls', self.testfile, ['ERROR: File /bin/ls is an alternative possibly provided by the following recipes:', 'coreutils', 'busybox'])
112 # Need a test file - should be executable
113 testfile2 = os.path.join(self.corebase, 'oe-init-build-env')
114 testfile2name = os.path.basename(testfile2)
115 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
116 '\n',
117 'SRC_URI += "file://%s"\n' % testfile2name,
118 '\n',
119 'do_install_append() {\n',
120 ' install -d ${D}${base_bindir}\n',
121 ' install -m 0755 ${WORKDIR}/%s ${D}${base_bindir}/ls\n' % testfile2name,
122 '}\n']
123 self._try_recipetool_appendfile('coreutils', '/bin/ls', testfile2, '-r coreutils', expectedlines, [testfile2name])
124 # Now try bbappending the same file again, contents should not change
125 bbappendfile, _ = self._try_recipetool_appendfile('coreutils', '/bin/ls', self.testfile, '-r coreutils', expectedlines, [testfile2name])
126 # But file should have
127 copiedfile = os.path.join(os.path.dirname(bbappendfile), 'coreutils', testfile2name)
128 result = runCmd('diff -q %s %s' % (testfile2, copiedfile), ignore_status=True)
129 self.assertNotEqual(result.status, 0, 'New file should have been copied but was not %s' % result.output)
130
131 @OETestID(1178)
132 def test_recipetool_appendfile_binary(self):
133 # Try appending a binary file
134 # /bin/ls can be a symlink to /usr/bin/ls
135 ls = os.path.realpath("/bin/ls")
136 result = runCmd('recipetool appendfile %s /bin/ls %s -r coreutils' % (self.templayerdir, ls))
137 self.assertIn('WARNING: ', result.output)
138 self.assertIn('is a binary', result.output)
139
140 @OETestID(1173)
141 def test_recipetool_appendfile_add(self):
142 # Try arbitrary file add to a recipe
143 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
144 '\n',
145 'SRC_URI += "file://testfile"\n',
146 '\n',
147 'do_install_append() {\n',
148 ' install -d ${D}${datadir}\n',
149 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n',
150 '}\n']
151 self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase', expectedlines, ['testfile'])
152 # Try adding another file, this time where the source file is executable
153 # (so we're testing that, plus modifying an existing bbappend)
154 testfile2 = os.path.join(self.corebase, 'oe-init-build-env')
155 testfile2name = os.path.basename(testfile2)
156 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
157 '\n',
158 'SRC_URI += "file://testfile \\\n',
159 ' file://%s \\\n' % testfile2name,
160 ' "\n',
161 '\n',
162 'do_install_append() {\n',
163 ' install -d ${D}${datadir}\n',
164 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n',
165 ' install -m 0755 ${WORKDIR}/%s ${D}${datadir}/scriptname\n' % testfile2name,
166 '}\n']
167 self._try_recipetool_appendfile('netbase', '/usr/share/scriptname', testfile2, '-r netbase', expectedlines, ['testfile', testfile2name])
168
169 @OETestID(1174)
170 def test_recipetool_appendfile_add_bindir(self):
171 # Try arbitrary file add to a recipe, this time to a location such that should be installed as executable
172 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
173 '\n',
174 'SRC_URI += "file://testfile"\n',
175 '\n',
176 'do_install_append() {\n',
177 ' install -d ${D}${bindir}\n',
178 ' install -m 0755 ${WORKDIR}/testfile ${D}${bindir}/selftest-recipetool-testbin\n',
179 '}\n']
180 _, output = self._try_recipetool_appendfile('netbase', '/usr/bin/selftest-recipetool-testbin', self.testfile, '-r netbase', expectedlines, ['testfile'])
181 self.assertNotIn('WARNING: ', output)
182
183 @OETestID(1175)
184 def test_recipetool_appendfile_add_machine(self):
185 # Try arbitrary file add to a recipe, this time to a location such that should be installed as executable
186 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
187 '\n',
188 'PACKAGE_ARCH = "${MACHINE_ARCH}"\n',
189 '\n',
190 'SRC_URI_append_mymachine = " file://testfile"\n',
191 '\n',
192 'do_install_append_mymachine() {\n',
193 ' install -d ${D}${datadir}\n',
194 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n',
195 '}\n']
196 _, output = self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase -m mymachine', expectedlines, ['mymachine/testfile'])
197 self.assertNotIn('WARNING: ', output)
198
199 @OETestID(1184)
200 def test_recipetool_appendfile_orig(self):
201 # A file that's in SRC_URI and in do_install with the same name
202 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
203 '\n']
204 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-orig', self.testfile, '', expectedlines, ['selftest-replaceme-orig'])
205 self.assertNotIn('WARNING: ', output)
206
207 @OETestID(1191)
208 def test_recipetool_appendfile_todir(self):
209 # A file that's in SRC_URI and in do_install with destination directory rather than file
210 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
211 '\n']
212 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-todir', self.testfile, '', expectedlines, ['selftest-replaceme-todir'])
213 self.assertNotIn('WARNING: ', output)
214
215 @OETestID(1187)
216 def test_recipetool_appendfile_renamed(self):
217 # A file that's in SRC_URI with a different name to the destination file
218 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
219 '\n']
220 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-renamed', self.testfile, '', expectedlines, ['file1'])
221 self.assertNotIn('WARNING: ', output)
222
223 @OETestID(1190)
224 def test_recipetool_appendfile_subdir(self):
225 # A file that's in SRC_URI in a subdir
226 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
227 '\n',
228 'SRC_URI += "file://testfile"\n',
229 '\n',
230 'do_install_append() {\n',
231 ' install -d ${D}${datadir}\n',
232 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-subdir\n',
233 '}\n']
234 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-subdir', self.testfile, '', expectedlines, ['testfile'])
235 self.assertNotIn('WARNING: ', output)
236
237 @OETestID(1189)
238 def test_recipetool_appendfile_src_glob(self):
239 # A file that's in SRC_URI as a glob
240 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
241 '\n',
242 'SRC_URI += "file://testfile"\n',
243 '\n',
244 'do_install_append() {\n',
245 ' install -d ${D}${datadir}\n',
246 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-src-globfile\n',
247 '}\n']
248 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-src-globfile', self.testfile, '', expectedlines, ['testfile'])
249 self.assertNotIn('WARNING: ', output)
250
251 @OETestID(1181)
252 def test_recipetool_appendfile_inst_glob(self):
253 # A file that's in do_install as a glob
254 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
255 '\n']
256 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-globfile', self.testfile, '', expectedlines, ['selftest-replaceme-inst-globfile'])
257 self.assertNotIn('WARNING: ', output)
258
259 @OETestID(1182)
260 def test_recipetool_appendfile_inst_todir_glob(self):
261 # A file that's in do_install as a glob with destination as a directory
262 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
263 '\n']
264 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-todir-globfile', self.testfile, '', expectedlines, ['selftest-replaceme-inst-todir-globfile'])
265 self.assertNotIn('WARNING: ', output)
266
267 @OETestID(1185)
268 def test_recipetool_appendfile_patch(self):
269 # A file that's added by a patch in SRC_URI
270 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
271 '\n',
272 'SRC_URI += "file://testfile"\n',
273 '\n',
274 'do_install_append() {\n',
275 ' install -d ${D}${sysconfdir}\n',
276 ' install -m 0644 ${WORKDIR}/testfile ${D}${sysconfdir}/selftest-replaceme-patched\n',
277 '}\n']
278 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/etc/selftest-replaceme-patched', self.testfile, '', expectedlines, ['testfile'])
279 for line in output.splitlines():
280 if 'WARNING: ' in line:
281 self.assertIn('add-file.patch', line, 'Unexpected warning found in output:\n%s' % line)
282 break
283 else:
284 self.fail('Patch warning not found in output:\n%s' % output)
285
286 @OETestID(1188)
287 def test_recipetool_appendfile_script(self):
288 # Now, a file that's in SRC_URI but installed by a script (so no mention in do_install)
289 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
290 '\n',
291 'SRC_URI += "file://testfile"\n',
292 '\n',
293 'do_install_append() {\n',
294 ' install -d ${D}${datadir}\n',
295 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-scripted\n',
296 '}\n']
297 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-scripted', self.testfile, '', expectedlines, ['testfile'])
298 self.assertNotIn('WARNING: ', output)
299
300 @OETestID(1180)
301 def test_recipetool_appendfile_inst_func(self):
302 # A file that's installed from a function called by do_install
303 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
304 '\n']
305 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-inst-func', self.testfile, '', expectedlines, ['selftest-replaceme-inst-func'])
306 self.assertNotIn('WARNING: ', output)
307
308 @OETestID(1186)
309 def test_recipetool_appendfile_postinstall(self):
310 # A file that's created by a postinstall script (and explicitly mentioned in it)
311 # First try without specifying recipe
312 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'])
313 # Now specify recipe
314 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
315 '\n',
316 'SRC_URI += "file://testfile"\n',
317 '\n',
318 'do_install_append() {\n',
319 ' install -d ${D}${datadir}\n',
320 ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-postinst\n',
321 '}\n']
322 _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-postinst', self.testfile, '-r selftest-recipetool-appendfile', expectedlines, ['testfile'])
323
324 @OETestID(1179)
325 def test_recipetool_appendfile_extlayer(self):
326 # Try creating a bbappend in a layer that's not in bblayers.conf and has a different structure
327 exttemplayerdir = os.path.join(self.tempdir, 'extlayer')
328 self._create_temp_layer(exttemplayerdir, False, 'oeselftestextlayer', recipepathspec='metadata/recipes/recipes-*/*')
329 result = runCmd('recipetool appendfile %s /usr/share/selftest-replaceme-orig %s' % (exttemplayerdir, self.testfile))
330 self.assertNotIn('Traceback', result.output)
331 createdfiles = []
332 for root, _, files in os.walk(exttemplayerdir):
333 for f in files:
334 createdfiles.append(os.path.relpath(os.path.join(root, f), exttemplayerdir))
335 createdfiles.remove('conf/layer.conf')
336 expectedfiles = ['metadata/recipes/recipes-test/selftest-recipetool-appendfile/selftest-recipetool-appendfile.bbappend',
337 'metadata/recipes/recipes-test/selftest-recipetool-appendfile/selftest-recipetool-appendfile/selftest-replaceme-orig']
338 self.assertEqual(sorted(createdfiles), sorted(expectedfiles))
339
340 @OETestID(1192)
341 def test_recipetool_appendfile_wildcard(self):
342
343 def try_appendfile_wc(options):
344 result = runCmd('recipetool appendfile %s /etc/profile %s %s' % (self.templayerdir, self.testfile, options))
345 self.assertNotIn('Traceback', result.output)
346 bbappendfile = None
347 for root, _, files in os.walk(self.templayerdir):
348 for f in files:
349 if f.endswith('.bbappend'):
350 bbappendfile = f
351 break
352 if not bbappendfile:
353 self.fail('No bbappend file created')
354 runCmd('rm -rf %s/recipes-*' % self.templayerdir)
355 return bbappendfile
356
357 # Check without wildcard option
358 recipefn = os.path.basename(get_bb_var('FILE', 'base-files'))
359 filename = try_appendfile_wc('')
360 self.assertEqual(filename, recipefn.replace('.bb', '.bbappend'))
361 # Now check with wildcard option
362 filename = try_appendfile_wc('-w')
363 self.assertEqual(filename, recipefn.split('_')[0] + '_%.bbappend')
364
365 @OETestID(1193)
366 def test_recipetool_create(self):
367 # Try adding a recipe
368 tempsrc = os.path.join(self.tempdir, 'srctree')
369 os.makedirs(tempsrc)
370 recipefile = os.path.join(self.tempdir, 'logrotate_3.8.7.bb')
371 srcuri = 'https://github.com/logrotate/logrotate/archive/r3-8-7.tar.gz'
372 result = runCmd('recipetool create -o %s %s -x %s' % (recipefile, srcuri, tempsrc))
373 self.assertTrue(os.path.isfile(recipefile))
374 checkvars = {}
375 checkvars['LICENSE'] = 'GPLv2'
376 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=18810669f13b87348459e611d31ab760'
377 checkvars['SRC_URI'] = 'https://github.com/logrotate/logrotate/archive/r3-8-7.tar.gz'
378 checkvars['SRC_URI[md5sum]'] = '6b1aa0e0d07eda3c9a2526520850397a'
379 checkvars['SRC_URI[sha256sum]'] = 'dece4bfeb9d8374a0ecafa34be139b5a697db5c926dcc69a9b8715431a22e733'
380 self._test_recipe_contents(recipefile, checkvars, [])
381
382 @OETestID(1194)
383 def test_recipetool_create_git(self):
384 if 'x11' not in get_bb_var('DISTRO_FEATURES'):
385 self.skipTest('Test requires x11 as distro feature')
386 # Ensure we have the right data in shlibs/pkgdata
387 bitbake('libpng pango libx11 libxext jpeg libcheck')
388 # Try adding a recipe
389 tempsrc = os.path.join(self.tempdir, 'srctree')
390 os.makedirs(tempsrc)
391 recipefile = os.path.join(self.tempdir, 'libmatchbox.bb')
392 srcuri = 'git://git.yoctoproject.org/libmatchbox'
393 result = runCmd(['recipetool', 'create', '-o', recipefile, srcuri + ";rev=9f7cf8895ae2d39c465c04cc78e918c157420269", '-x', tempsrc])
394 self.assertTrue(os.path.isfile(recipefile), 'recipetool did not create recipe file; output:\n%s' % result.output)
395 checkvars = {}
396 checkvars['LICENSE'] = 'LGPLv2.1'
397 checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34'
398 checkvars['S'] = '${WORKDIR}/git'
399 checkvars['PV'] = '1.11+git${SRCPV}'
400 checkvars['SRC_URI'] = srcuri
401 checkvars['DEPENDS'] = set(['libcheck', 'libjpeg-turbo', 'libpng', 'libx11', 'libxext', 'pango'])
402 inherits = ['autotools', 'pkgconfig']
403 self._test_recipe_contents(recipefile, checkvars, inherits)
404
405 @OETestID(1392)
406 def test_recipetool_create_simple(self):
407 # Try adding a recipe
408 temprecipe = os.path.join(self.tempdir, 'recipe')
409 os.makedirs(temprecipe)
410 pv = '1.7.3.0'
411 srcuri = 'http://www.dest-unreach.org/socat/download/socat-%s.tar.bz2' % pv
412 result = runCmd('recipetool create %s -o %s' % (srcuri, temprecipe))
413 dirlist = os.listdir(temprecipe)
414 if len(dirlist) > 1:
415 self.fail('recipetool created more than just one file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist)))
416 if len(dirlist) < 1 or not os.path.isfile(os.path.join(temprecipe, dirlist[0])):
417 self.fail('recipetool did not create recipe file; output:\n%s\ndirlist:\n%s' % (result.output, str(dirlist)))
418 self.assertEqual(dirlist[0], 'socat_%s.bb' % pv, 'Recipe file incorrectly named')
419 checkvars = {}
420 checkvars['LICENSE'] = set(['Unknown', 'GPLv2'])
421 checkvars['LIC_FILES_CHKSUM'] = set(['file://COPYING.OpenSSL;md5=5c9bccc77f67a8328ef4ebaf468116f4', 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263'])
422 # We don't check DEPENDS since they are variable for this recipe depending on what's in the sysroot
423 checkvars['S'] = None
424 checkvars['SRC_URI'] = srcuri.replace(pv, '${PV}')
425 inherits = ['autotools']
426 self._test_recipe_contents(os.path.join(temprecipe, dirlist[0]), checkvars, inherits)
427
428 @OETestID(1418)
429 def test_recipetool_create_cmake(self):
430 # Try adding a recipe
431 temprecipe = os.path.join(self.tempdir, 'recipe')
432 os.makedirs(temprecipe)
433 recipefile = os.path.join(temprecipe, 'navit_0.5.0.bb')
434 srcuri = 'http://downloads.sourceforge.net/project/navit/v0.5.0/navit-0.5.0.tar.gz'
435 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
436 self.assertTrue(os.path.isfile(recipefile))
437 checkvars = {}
438 checkvars['LICENSE'] = set(['Unknown', 'GPLv2', 'LGPLv2'])
439 checkvars['SRC_URI'] = 'http://downloads.sourceforge.net/project/navit/v${PV}/navit-${PV}.tar.gz'
440 checkvars['SRC_URI[md5sum]'] = '242f398e979a6b8c0f3c802b63435b68'
441 checkvars['SRC_URI[sha256sum]'] = '13353481d7fc01a4f64e385dda460b51496366bba0fd2cc85a89a0747910e94d'
442 checkvars['DEPENDS'] = set(['freetype', 'zlib', 'openssl', 'glib-2.0', 'virtual/libgl', 'virtual/egl', 'gtk+', 'libpng', 'libsdl', 'freeglut', 'dbus-glib'])
443 inherits = ['cmake', 'python-dir', 'gettext', 'pkgconfig']
444 self._test_recipe_contents(recipefile, checkvars, inherits)
445
446 def test_recipetool_create_github(self):
447 # Basic test to see if github URL mangling works
448 temprecipe = os.path.join(self.tempdir, 'recipe')
449 os.makedirs(temprecipe)
450 recipefile = os.path.join(temprecipe, 'meson_git.bb')
451 srcuri = 'https://github.com/mesonbuild/meson;rev=0.32.0'
452 result = runCmd(['recipetool', 'create', '-o', temprecipe, srcuri])
453 self.assertTrue(os.path.isfile(recipefile))
454 checkvars = {}
455 checkvars['LICENSE'] = set(['Apache-2.0'])
456 checkvars['SRC_URI'] = 'git://github.com/mesonbuild/meson;protocol=https'
457 inherits = ['setuptools']
458 self._test_recipe_contents(recipefile, checkvars, inherits)
459
460 def test_recipetool_create_github_tarball(self):
461 # Basic test to ensure github URL mangling doesn't apply to release tarballs
462 temprecipe = os.path.join(self.tempdir, 'recipe')
463 os.makedirs(temprecipe)
464 pv = '0.32.0'
465 recipefile = os.path.join(temprecipe, 'meson_%s.bb' % pv)
466 srcuri = 'https://github.com/mesonbuild/meson/releases/download/%s/meson-%s.tar.gz' % (pv, pv)
467 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
468 self.assertTrue(os.path.isfile(recipefile))
469 checkvars = {}
470 checkvars['LICENSE'] = set(['Apache-2.0'])
471 checkvars['SRC_URI'] = 'https://github.com/mesonbuild/meson/releases/download/${PV}/meson-${PV}.tar.gz'
472 inherits = ['setuptools']
473 self._test_recipe_contents(recipefile, checkvars, inherits)
474
475 def test_recipetool_create_git_http(self):
476 # Basic test to check http git URL mangling works
477 temprecipe = os.path.join(self.tempdir, 'recipe')
478 os.makedirs(temprecipe)
479 recipefile = os.path.join(temprecipe, 'matchbox-terminal_git.bb')
480 srcuri = 'http://git.yoctoproject.org/git/matchbox-terminal'
481 result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
482 self.assertTrue(os.path.isfile(recipefile))
483 checkvars = {}
484 checkvars['LICENSE'] = set(['GPLv2'])
485 checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/matchbox-terminal;protocol=http'
486 inherits = ['pkgconfig', 'autotools']
487 self._test_recipe_contents(recipefile, checkvars, inherits)
488
489 def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
490 dstdir = basedstdir
491 self.assertTrue(os.path.exists(dstdir))
492 for p in paths:
493 dstdir = os.path.join(dstdir, p)
494 if not os.path.exists(dstdir):
495 os.makedirs(dstdir)
496 self.track_for_cleanup(dstdir)
497 dstfile = os.path.join(dstdir, os.path.basename(srcfile))
498 if srcfile != dstfile:
499 shutil.copy(srcfile, dstfile)
500 self.track_for_cleanup(dstfile)
501
502 def test_recipetool_load_plugin(self):
503 """Test that recipetool loads only the first found plugin in BBPATH."""
504
505 recipetool = runCmd("which recipetool")
506 fromname = runCmd("recipetool --quiet pluginfile")
507 srcfile = fromname.output
508 searchpath = self.bbpath.split(':') + [os.path.dirname(recipetool.output)]
509 plugincontent = []
510 with open(srcfile) as fh:
511 plugincontent = fh.readlines()
512 try:
513 self.assertIn('meta-selftest', srcfile, 'wrong bbpath plugin found')
514 for path in searchpath:
515 self._copy_file_with_cleanup(srcfile, path, 'lib', 'recipetool')
516 result = runCmd("recipetool --quiet count")
517 self.assertEqual(result.output, '1')
518 result = runCmd("recipetool --quiet multiloaded")
519 self.assertEqual(result.output, "no")
520 for path in searchpath:
521 result = runCmd("recipetool --quiet bbdir")
522 self.assertEqual(result.output, path)
523 os.unlink(os.path.join(result.output, 'lib', 'recipetool', 'bbpath.py'))
524 finally:
525 with open(srcfile, 'w') as fh:
526 fh.writelines(plugincontent)
527
528
529class RecipetoolAppendsrcBase(RecipetoolBase):
530 def _try_recipetool_appendsrcfile(self, testrecipe, newfile, destfile, options, expectedlines, expectedfiles):
531 cmd = 'recipetool appendsrcfile %s %s %s %s %s' % (options, self.templayerdir, testrecipe, newfile, destfile)
532 return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines)
533
534 def _try_recipetool_appendsrcfiles(self, testrecipe, newfiles, expectedlines=None, expectedfiles=None, destdir=None, options=''):
535
536 if destdir:
537 options += ' -D %s' % destdir
538
539 if expectedfiles is None:
540 expectedfiles = [os.path.basename(f) for f in newfiles]
541
542 cmd = 'recipetool appendsrcfiles %s %s %s %s' % (options, self.templayerdir, testrecipe, ' '.join(newfiles))
543 return self._try_recipetool_appendcmd(cmd, testrecipe, expectedfiles, expectedlines)
544
545 def _try_recipetool_appendsrcfile_fail(self, testrecipe, newfile, destfile, checkerror):
546 cmd = 'recipetool appendsrcfile %s %s %s %s' % (self.templayerdir, testrecipe, newfile, destfile or '')
547 result = runCmd(cmd, ignore_status=True)
548 self.assertNotEqual(result.status, 0, 'Command "%s" should have failed but didn\'t' % cmd)
549 self.assertNotIn('Traceback', result.output)
550 for errorstr in checkerror:
551 self.assertIn(errorstr, result.output)
552
553 @staticmethod
554 def _get_first_file_uri(recipe):
555 '''Return the first file:// in SRC_URI for the specified recipe.'''
556 src_uri = get_bb_var('SRC_URI', recipe).split()
557 for uri in src_uri:
558 p = urllib.parse.urlparse(uri)
559 if p.scheme == 'file':
560 return p.netloc + p.path
561
562 def _test_appendsrcfile(self, testrecipe, filename=None, destdir=None, has_src_uri=True, srcdir=None, newfile=None, options=''):
563 if newfile is None:
564 newfile = self.testfile
565
566 if srcdir:
567 if destdir:
568 expected_subdir = os.path.join(srcdir, destdir)
569 else:
570 expected_subdir = srcdir
571 else:
572 options += " -W"
573 expected_subdir = destdir
574
575 if filename:
576 if destdir:
577 destpath = os.path.join(destdir, filename)
578 else:
579 destpath = filename
580 else:
581 filename = os.path.basename(newfile)
582 if destdir:
583 destpath = destdir + os.sep
584 else:
585 destpath = '.' + os.sep
586
587 expectedlines = ['FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"\n',
588 '\n']
589 if has_src_uri:
590 uri = 'file://%s' % filename
591 if expected_subdir:
592 uri += ';subdir=%s' % expected_subdir
593 expectedlines[0:0] = ['SRC_URI += "%s"\n' % uri,
594 '\n']
595
596 return self._try_recipetool_appendsrcfile(testrecipe, newfile, destpath, options, expectedlines, [filename])
597
598 def _test_appendsrcfiles(self, testrecipe, newfiles, expectedfiles=None, destdir=None, options=''):
599 if expectedfiles is None:
600 expectedfiles = [os.path.basename(n) for n in newfiles]
601
602 self._try_recipetool_appendsrcfiles(testrecipe, newfiles, expectedfiles=expectedfiles, destdir=destdir, options=options)
603
604 bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'FILESEXTRAPATHS'], testrecipe)
605 src_uri = bb_vars['SRC_URI'].split()
606 for f in expectedfiles:
607 if destdir:
608 self.assertIn('file://%s;subdir=%s' % (f, destdir), src_uri)
609 else:
610 self.assertIn('file://%s' % f, src_uri)
611
612 recipefile = bb_vars['FILE']
613 bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir)
614 filesdir = os.path.join(os.path.dirname(bbappendfile), testrecipe)
615 filesextrapaths = bb_vars['FILESEXTRAPATHS'].split(':')
616 self.assertIn(filesdir, filesextrapaths)
617
618
619
620
621class RecipetoolAppendsrcTests(RecipetoolAppendsrcBase):
622
623 @OETestID(1273)
624 def test_recipetool_appendsrcfile_basic(self):
625 self._test_appendsrcfile('base-files', 'a-file')
626
627 @OETestID(1274)
628 def test_recipetool_appendsrcfile_basic_wildcard(self):
629 testrecipe = 'base-files'
630 self._test_appendsrcfile(testrecipe, 'a-file', options='-w')
631 recipefile = get_bb_var('FILE', testrecipe)
632 bbappendfile = self._check_bbappend(testrecipe, recipefile, self.templayerdir)
633 self.assertEqual(os.path.basename(bbappendfile), '%s_%%.bbappend' % testrecipe)
634
635 @OETestID(1281)
636 def test_recipetool_appendsrcfile_subdir_basic(self):
637 self._test_appendsrcfile('base-files', 'a-file', 'tmp')
638
639 @OETestID(1282)
640 def test_recipetool_appendsrcfile_subdir_basic_dirdest(self):
641 self._test_appendsrcfile('base-files', destdir='tmp')
642
643 @OETestID(1280)
644 def test_recipetool_appendsrcfile_srcdir_basic(self):
645 testrecipe = 'bash'
646 bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe)
647 srcdir = bb_vars['S']
648 workdir = bb_vars['WORKDIR']
649 subdir = os.path.relpath(srcdir, workdir)
650 self._test_appendsrcfile(testrecipe, 'a-file', srcdir=subdir)
651
652 @OETestID(1275)
653 def test_recipetool_appendsrcfile_existing_in_src_uri(self):
654 testrecipe = 'base-files'
655 filepath = self._get_first_file_uri(testrecipe)
656 self.assertTrue(filepath, 'Unable to test, no file:// uri found in SRC_URI for %s' % testrecipe)
657 self._test_appendsrcfile(testrecipe, filepath, has_src_uri=False)
658
659 @OETestID(1276)
660 def test_recipetool_appendsrcfile_existing_in_src_uri_diff_params(self):
661 testrecipe = 'base-files'
662 subdir = 'tmp'
663 filepath = self._get_first_file_uri(testrecipe)
664 self.assertTrue(filepath, 'Unable to test, no file:// uri found in SRC_URI for %s' % testrecipe)
665
666 output = self._test_appendsrcfile(testrecipe, filepath, subdir, has_src_uri=False)
667 self.assertTrue(any('with different parameters' in l for l in output))
668
669 @OETestID(1277)
670 def test_recipetool_appendsrcfile_replace_file_srcdir(self):
671 testrecipe = 'bash'
672 filepath = 'Makefile.in'
673 bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe)
674 srcdir = bb_vars['S']
675 workdir = bb_vars['WORKDIR']
676 subdir = os.path.relpath(srcdir, workdir)
677
678 self._test_appendsrcfile(testrecipe, filepath, srcdir=subdir)
679 bitbake('%s:do_unpack' % testrecipe)
680 self.assertEqual(open(self.testfile, 'r').read(), open(os.path.join(srcdir, filepath), 'r').read())
681
682 @OETestID(1278)
683 def test_recipetool_appendsrcfiles_basic(self, destdir=None):
684 newfiles = [self.testfile]
685 for i in range(1, 5):
686 testfile = os.path.join(self.tempdir, 'testfile%d' % i)
687 with open(testfile, 'w') as f:
688 f.write('Test file %d\n' % i)
689 newfiles.append(testfile)
690 self._test_appendsrcfiles('gcc', newfiles, destdir=destdir, options='-W')
691
692 @OETestID(1279)
693 def test_recipetool_appendsrcfiles_basic_subdir(self):
694 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 @@
1#
2# Copyright (c) 2017 Wind River Systems, Inc.
3#
4
5import re
6import logging
7
8from oeqa.selftest.case import OESelftestTestCase
9from oeqa.utils.commands import bitbake, runqemu, get_bb_var
10from oeqa.core.decorator.oeid import OETestID
11
12class RunqemuTests(OESelftestTestCase):
13 """Runqemu test class"""
14
15 image_is_ready = False
16 deploy_dir_image = ''
17
18 def setUpLocal(self):
19 super(RunqemuTests, self).setUpLocal()
20 self.recipe = 'core-image-minimal'
21 self.machine = 'qemux86-64'
22 self.fstypes = "ext4 iso hddimg vmdk qcow2 vdi"
23 self.cmd_common = "runqemu nographic"
24
25 # Avoid emit the same record multiple times.
26 mainlogger = logging.getLogger("BitBake.Main")
27 mainlogger.propagate = False
28
29 self.write_config(
30"""
31MACHINE = "%s"
32IMAGE_FSTYPES = "%s"
33# 10 means 1 second
34SYSLINUX_TIMEOUT = "10"
35"""
36% (self.machine, self.fstypes)
37 )
38
39 if not RunqemuTests.image_is_ready:
40 RunqemuTests.deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE')
41 bitbake(self.recipe)
42 RunqemuTests.image_is_ready = True
43
44 @OETestID(2001)
45 def test_boot_machine(self):
46 """Test runqemu machine"""
47 cmd = "%s %s" % (self.cmd_common, self.machine)
48 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
49 self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
50
51 @OETestID(2002)
52 def test_boot_machine_ext4(self):
53 """Test runqemu machine ext4"""
54 cmd = "%s %s ext4" % (self.cmd_common, self.machine)
55 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
56 with open(qemu.qemurunnerlog) as f:
57 self.assertTrue('rootfs.ext4' in f.read(), "Failed: %s" % cmd)
58
59 @OETestID(2003)
60 def test_boot_machine_iso(self):
61 """Test runqemu machine iso"""
62 cmd = "%s %s iso" % (self.cmd_common, self.machine)
63 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
64 with open(qemu.qemurunnerlog) as f:
65 self.assertTrue(' -cdrom ' in f.read(), "Failed: %s" % cmd)
66
67 @OETestID(2004)
68 def test_boot_recipe_image(self):
69 """Test runqemu recipe-image"""
70 cmd = "%s %s" % (self.cmd_common, self.recipe)
71 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
72 self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
73
74 @OETestID(2005)
75 def test_boot_recipe_image_vmdk(self):
76 """Test runqemu recipe-image vmdk"""
77 cmd = "%s %s vmdk" % (self.cmd_common, self.recipe)
78 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
79 with open(qemu.qemurunnerlog) as f:
80 self.assertTrue('format=vmdk' in f.read(), "Failed: %s" % cmd)
81
82 @OETestID(2006)
83 def test_boot_recipe_image_vdi(self):
84 """Test runqemu recipe-image vdi"""
85 cmd = "%s %s vdi" % (self.cmd_common, self.recipe)
86 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
87 with open(qemu.qemurunnerlog) as f:
88 self.assertTrue('format=vdi' in f.read(), "Failed: %s" % cmd)
89
90 @OETestID(2007)
91 def test_boot_deploy(self):
92 """Test runqemu deploy_dir_image"""
93 cmd = "%s %s" % (self.cmd_common, self.deploy_dir_image)
94 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
95 self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
96
97 @OETestID(2008)
98 def test_boot_deploy_hddimg(self):
99 """Test runqemu deploy_dir_image hddimg"""
100 cmd = "%s %s hddimg" % (self.cmd_common, self.deploy_dir_image)
101 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
102 with open(qemu.qemurunnerlog) as f:
103 self.assertTrue(re.search('file=.*.hddimg', f.read()), "Failed: %s" % cmd)
104
105 @OETestID(2009)
106 def test_boot_machine_slirp(self):
107 """Test runqemu machine slirp"""
108 cmd = "%s slirp %s" % (self.cmd_common, self.machine)
109 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
110 with open(qemu.qemurunnerlog) as f:
111 self.assertTrue(' -netdev user' in f.read(), "Failed: %s" % cmd)
112
113 @OETestID(2009)
114 def test_boot_machine_slirp_qcow2(self):
115 """Test runqemu machine slirp qcow2"""
116 cmd = "%s slirp qcow2 %s" % (self.cmd_common, self.machine)
117 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
118 with open(qemu.qemurunnerlog) as f:
119 self.assertTrue('format=qcow2' in f.read(), "Failed: %s" % cmd)
120
121 @OETestID(2010)
122 def test_boot_qemu_boot(self):
123 """Test runqemu /path/to/image.qemuboot.conf"""
124 qemuboot_conf = "%s-%s.qemuboot.conf" % (self.recipe, self.machine)
125 qemuboot_conf = os.path.join(self.deploy_dir_image, qemuboot_conf)
126 if not os.path.exists(qemuboot_conf):
127 self.skipTest("%s not found" % qemuboot_conf)
128 cmd = "%s %s" % (self.cmd_common, qemuboot_conf)
129 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
130 self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd)
131
132 @OETestID(2011)
133 def test_boot_rootfs(self):
134 """Test runqemu /path/to/rootfs.ext4"""
135 rootfs = "%s-%s.ext4" % (self.recipe, self.machine)
136 rootfs = os.path.join(self.deploy_dir_image, rootfs)
137 if not os.path.exists(rootfs):
138 self.skipTest("%s not found" % rootfs)
139 cmd = "%s %s" % (self.cmd_common, rootfs)
140 with runqemu(self.recipe, ssh=False, launch_cmd=cmd) as qemu:
141 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
3from oeqa.core.decorator.oeid import OETestID
4import os
5import re
6
7class TestExport(OESelftestTestCase):
8
9 @classmethod
10 def tearDownClass(cls):
11 runCmd("rm -rf /tmp/sdk")
12 super(TestExport, cls).tearDownClass()
13
14 def test_testexport_basic(self):
15 """
16 Summary: Check basic testexport functionality with only ping test enabled.
17 Expected: 1. testexport directory must be created.
18 2. runexported.py must run without any error/exception.
19 3. ping test must succeed.
20 Product: oe-core
21 Author: Mariano Lopez <mariano.lopez@intel.com>
22 """
23
24 features = 'INHERIT += "testexport"\n'
25 # These aren't the actual IP addresses but testexport class needs something defined
26 features += 'TEST_SERVER_IP = "192.168.7.1"\n'
27 features += 'TEST_TARGET_IP = "192.168.7.1"\n'
28 features += 'TEST_SUITES = "ping"\n'
29 self.write_config(features)
30
31 # Build tesexport for core-image-minimal
32 bitbake('core-image-minimal')
33 bitbake('-c testexport core-image-minimal')
34
35 testexport_dir = get_bb_var('TEST_EXPORT_DIR', 'core-image-minimal')
36
37 # Verify if TEST_EXPORT_DIR was created
38 isdir = os.path.isdir(testexport_dir)
39 self.assertEqual(True, isdir, 'Failed to create testexport dir: %s' % testexport_dir)
40
41 with runqemu('core-image-minimal') as qemu:
42 # Attempt to run runexported.py to perform ping test
43 test_path = os.path.join(testexport_dir, "oe-test")
44 data_file = os.path.join(testexport_dir, 'data', 'testdata.json')
45 manifest = os.path.join(testexport_dir, 'data', 'manifest')
46 cmd = ("%s runtime --test-data-file %s --packages-manifest %s "
47 "--target-ip %s --server-ip %s --quiet"
48 % (test_path, data_file, manifest, qemu.ip, qemu.server_ip))
49 result = runCmd(cmd)
50 # Verify ping test was succesful
51 self.assertEqual(0, result.status, 'oe-test runtime returned a non 0 status')
52
53 def test_testexport_sdk(self):
54 """
55 Summary: Check sdk functionality for testexport.
56 Expected: 1. testexport directory must be created.
57 2. SDK tarball must exists.
58 3. Uncompressing of tarball must succeed.
59 4. Check if the SDK directory is added to PATH.
60 5. Run tar from the SDK directory.
61 Product: oe-core
62 Author: Mariano Lopez <mariano.lopez@intel.com>
63 """
64
65 features = 'INHERIT += "testexport"\n'
66 # These aren't the actual IP addresses but testexport class needs something defined
67 features += 'TEST_SERVER_IP = "192.168.7.1"\n'
68 features += 'TEST_TARGET_IP = "192.168.7.1"\n'
69 features += 'TEST_SUITES = "ping"\n'
70 features += 'TEST_EXPORT_SDK_ENABLED = "1"\n'
71 features += 'TEST_EXPORT_SDK_PACKAGES = "nativesdk-tar"\n'
72 self.write_config(features)
73
74 # Build tesexport for core-image-minimal
75 bitbake('core-image-minimal')
76 bitbake('-c testexport core-image-minimal')
77
78 needed_vars = ['TEST_EXPORT_DIR', 'TEST_EXPORT_SDK_DIR', 'TEST_EXPORT_SDK_NAME']
79 bb_vars = get_bb_vars(needed_vars, 'core-image-minimal')
80 testexport_dir = bb_vars['TEST_EXPORT_DIR']
81 sdk_dir = bb_vars['TEST_EXPORT_SDK_DIR']
82 sdk_name = bb_vars['TEST_EXPORT_SDK_NAME']
83
84 # Check for SDK
85 tarball_name = "%s.sh" % sdk_name
86 tarball_path = os.path.join(testexport_dir, sdk_dir, tarball_name)
87 msg = "Couldn't find SDK tarball: %s" % tarball_path
88 self.assertEqual(os.path.isfile(tarball_path), True, msg)
89
90 # Extract SDK and run tar from SDK
91 result = runCmd("%s -y -d /tmp/sdk" % tarball_path)
92 self.assertEqual(0, result.status, "Couldn't extract SDK")
93
94 env_script = result.output.split()[-1]
95 result = runCmd(". %s; which tar" % env_script, shell=True)
96 self.assertEqual(0, result.status, "Couldn't setup SDK environment")
97 is_sdk_tar = True if "/tmp/sdk" in result.output else False
98 self.assertTrue(is_sdk_tar, "Couldn't setup SDK environment")
99
100 tar_sdk = result.output
101 result = runCmd("%s --version" % tar_sdk)
102 self.assertEqual(0, result.status, "Couldn't run tar from SDK")
103
104
105class TestImage(OESelftestTestCase):
106
107 def test_testimage_install(self):
108 """
109 Summary: Check install packages functionality for testimage/testexport.
110 Expected: 1. Import tests from a directory other than meta.
111 2. Check install/uninstall of socat.
112 3. Check that remote package feeds can be accessed
113 Product: oe-core
114 Author: Mariano Lopez <mariano.lopez@intel.com>
115 Author: Alexander Kanavin <alexander.kanavin@intel.com>
116 """
117 if get_bb_var('DISTRO') == 'poky-tiny':
118 self.skipTest('core-image-full-cmdline not buildable for poky-tiny')
119
120 features = 'INHERIT += "testimage"\n'
121 features += 'TEST_SUITES = "ping ssh selftest"\n'
122 # We don't yet know what the server ip and port will be - they will be patched
123 # in at the start of the on-image test
124 features += 'PACKAGE_FEED_URIS = "http://bogus_ip:bogus_port"\n'
125 features += 'EXTRA_IMAGE_FEATURES += "package-management"\n'
126 features += 'PACKAGE_CLASSES = "package_rpm"'
127 self.write_config(features)
128
129 # Build core-image-sato and testimage
130 bitbake('core-image-full-cmdline socat')
131 bitbake('-c testimage core-image-full-cmdline')
132
133class Postinst(OESelftestTestCase):
134 @OETestID(1540)
135 def test_verify_postinst(self):
136 """
137 Summary: The purpose of this test is to verify the execution order of postinst Bugzilla ID: [5319]
138 Expected :
139 1. Compile a minimal image.
140 2. The compiled image will add the created layer with the recipes postinst[ abdpt]
141 3. Run qemux86
142 4. Validate the task execution order
143 Author: Francisco Pedraza <francisco.j.pedraza.gonzalez@intel.com>
144 """
145 features = 'INHERIT += "testimage"\n'
146 features += 'CORE_IMAGE_EXTRA_INSTALL += "postinst-at-rootfs \
147postinst-delayed-a \
148postinst-delayed-b \
149postinst-delayed-d \
150postinst-delayed-p \
151postinst-delayed-t \
152"\n'
153 self.write_config(features)
154
155 bitbake('core-image-minimal -f ')
156
157 postinst_list = ['100-postinst-at-rootfs',
158 '101-postinst-delayed-a',
159 '102-postinst-delayed-b',
160 '103-postinst-delayed-d',
161 '104-postinst-delayed-p',
162 '105-postinst-delayed-t']
163 path_workdir = get_bb_var('WORKDIR','core-image-minimal')
164 workspacedir = 'testimage/qemu_boot_log'
165 workspacedir = os.path.join(path_workdir, workspacedir)
166 rexp = re.compile("^Running postinst .*/(?P<postinst>.*)\.\.\.$")
167 with runqemu('core-image-minimal') as qemu:
168 with open(workspacedir) as f:
169 found = False
170 idx = 0
171 for line in f.readlines():
172 line = line.strip().replace("^M","")
173 if not line: # To avoid empty lines
174 continue
175 m = rexp.search(line)
176 if m:
177 self.assertEqual(postinst_list[idx], m.group('postinst'), "Fail")
178 idx = idx+1
179 found = True
180 elif found:
181 self.assertEqual(idx, len(postinst_list), "Not found all postinsts")
182 break
183
184 @OETestID(1545)
185 def test_postinst_rootfs_and_boot(self):
186 """
187 Summary: The purpose of this test case is to verify Post-installation
188 scripts are called when rootfs is created and also test
189 that script can be delayed to run at first boot.
190 Dependencies: NA
191 Steps: 1. Add proper configuration to local.conf file
192 2. Build a "core-image-minimal" image
193 3. Verify that file created by postinst_rootfs recipe is
194 present on rootfs dir.
195 4. Boot the image created on qemu and verify that the file
196 created by postinst_boot recipe is present on image.
197 Expected: The files are successfully created during rootfs and boot
198 time for 3 different package managers: rpm,ipk,deb and
199 for initialization managers: sysvinit and systemd.
200
201 """
202 file_rootfs_name = "this-was-created-at-rootfstime"
203 fileboot_name = "this-was-created-at-first-boot"
204 rootfs_pkg = 'postinst-at-rootfs'
205 boot_pkg = 'postinst-delayed-a'
206 #Step 1
207 common_features = 'MACHINE = "qemux86"\n'
208 common_features += 'CORE_IMAGE_EXTRA_INSTALL += "%s %s "\n'% (rootfs_pkg, boot_pkg)
209 common_features += 'IMAGE_FEATURES += "ssh-server-openssh"\n'
210 for init_manager in ("sysvinit", "systemd"):
211 #for sysvinit no extra configuration is needed,
212 features = ''
213 if (init_manager is "systemd"):
214 features += 'DISTRO_FEATURES_append = " systemd"\n'
215 features += 'VIRTUAL-RUNTIME_init_manager = "systemd"\n'
216 features += 'DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"\n'
217 features += 'VIRTUAL-RUNTIME_initscripts = ""\n'
218 for classes in ("package_rpm package_deb package_ipk",
219 "package_deb package_rpm package_ipk",
220 "package_ipk package_deb package_rpm"):
221 features += 'PACKAGE_CLASSES = "%s"\n' % classes
222 self.write_config(common_features + features)
223
224 #Step 2
225 bitbake('core-image-minimal')
226
227 #Step 3
228 file_rootfs_created = os.path.join(get_bb_var('IMAGE_ROOTFS',"core-image-minimal"),
229 file_rootfs_name)
230 found = os.path.isfile(file_rootfs_created)
231 self.assertTrue(found, "File %s was not created at rootfs time by %s" % \
232 (file_rootfs_name, rootfs_pkg))
233
234 #Step 4
235 testcommand = 'ls /etc/'+fileboot_name
236 with runqemu('core-image-minimal') as qemu:
237 sshargs = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
238 result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand))
239 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 @@
1from oeqa.selftest.case import OESelftestTestCase
2from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
3import os
4import glob
5import re
6import shutil
7import tempfile
8from oeqa.core.decorator.oeid import OETestID
9from oeqa.utils.ftools import write_file
10
11
12class Signing(OESelftestTestCase):
13
14 gpg_dir = ""
15 pub_key_path = ""
16 secret_key_path = ""
17
18 @classmethod
19 def setUpClass(cls):
20 super(Signing, cls).setUpClass()
21 # Check that we can find the gpg binary and fail early if we can't
22 if not shutil.which("gpg"):
23 raise AssertionError("This test needs GnuPG")
24
25 cls.gpg_home_dir = tempfile.TemporaryDirectory(prefix="oeqa-signing-")
26 cls.gpg_dir = cls.gpg_home_dir.name
27
28 cls.pub_key_path = os.path.join(cls.testlayer_path, 'files', 'signing', "key.pub")
29 cls.secret_key_path = os.path.join(cls.testlayer_path, 'files', 'signing', "key.secret")
30
31 runCmd('gpg --batch --homedir %s --import %s %s' % (cls.gpg_dir, cls.pub_key_path, cls.secret_key_path))
32
33 @OETestID(1362)
34 def test_signing_packages(self):
35 """
36 Summary: Test that packages can be signed in the package feed
37 Expected: Package should be signed with the correct key
38 Expected: Images can be created from signed packages
39 Product: oe-core
40 Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
41 Author: Alexander Kanavin <alexander.kanavin@intel.com>
42 AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
43 """
44 import oe.packagedata
45
46 package_classes = get_bb_var('PACKAGE_CLASSES')
47 if 'package_rpm' not in package_classes:
48 self.skipTest('This test requires RPM Packaging.')
49
50 test_recipe = 'ed'
51
52 feature = 'INHERIT += "sign_rpm"\n'
53 feature += 'RPM_GPG_PASSPHRASE = "test123"\n'
54 feature += 'RPM_GPG_NAME = "testuser"\n'
55 feature += 'GPG_PATH = "%s"\n' % self.gpg_dir
56
57 self.write_config(feature)
58
59 bitbake('-c clean %s' % test_recipe)
60 bitbake('-f -c package_write_rpm %s' % test_recipe)
61
62 self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
63
64 needed_vars = ['PKGDATA_DIR', 'DEPLOY_DIR_RPM', 'PACKAGE_ARCH', 'STAGING_BINDIR_NATIVE']
65 bb_vars = get_bb_vars(needed_vars, test_recipe)
66 pkgdatadir = bb_vars['PKGDATA_DIR']
67 pkgdata = oe.packagedata.read_pkgdatafile(pkgdatadir + "/runtime/ed")
68 if 'PKGE' in pkgdata:
69 pf = pkgdata['PN'] + "-" + pkgdata['PKGE'] + pkgdata['PKGV'] + '-' + pkgdata['PKGR']
70 else:
71 pf = pkgdata['PN'] + "-" + pkgdata['PKGV'] + '-' + pkgdata['PKGR']
72 deploy_dir_rpm = bb_vars['DEPLOY_DIR_RPM']
73 package_arch = bb_vars['PACKAGE_ARCH'].replace('-', '_')
74 staging_bindir_native = bb_vars['STAGING_BINDIR_NATIVE']
75
76 pkg_deploy = os.path.join(deploy_dir_rpm, package_arch, '.'.join((pf, package_arch, 'rpm')))
77
78 # Use a temporary rpmdb
79 rpmdb = tempfile.mkdtemp(prefix='oeqa-rpmdb')
80
81 runCmd('%s/rpmkeys --define "_dbpath %s" --import %s' %
82 (staging_bindir_native, rpmdb, self.pub_key_path))
83
84 ret = runCmd('%s/rpmkeys --define "_dbpath %s" --checksig %s' %
85 (staging_bindir_native, rpmdb, pkg_deploy))
86 # tmp/deploy/rpm/i586/ed-1.9-r0.i586.rpm: rsa sha1 md5 OK
87 self.assertIn('rsa sha1 (md5) pgp md5 OK', ret.output, 'Package signed incorrectly.')
88 shutil.rmtree(rpmdb)
89
90 #Check that an image can be built from signed packages
91 self.add_command_to_tearDown('bitbake -c clean core-image-minimal')
92 bitbake('-c clean core-image-minimal')
93 bitbake('core-image-minimal')
94
95
96 @OETestID(1382)
97 def test_signing_sstate_archive(self):
98 """
99 Summary: Test that sstate archives can be signed
100 Expected: Package should be signed with the correct key
101 Product: oe-core
102 Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
103 AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
104 """
105
106 test_recipe = 'ed'
107
108 builddir = os.environ.get('BUILDDIR')
109 sstatedir = os.path.join(builddir, 'test-sstate')
110
111 self.add_command_to_tearDown('bitbake -c clean %s' % test_recipe)
112 self.add_command_to_tearDown('rm -rf %s' % sstatedir)
113
114 feature = 'SSTATE_SIG_KEY ?= "testuser"\n'
115 feature += 'SSTATE_SIG_PASSPHRASE ?= "test123"\n'
116 feature += 'SSTATE_VERIFY_SIG ?= "1"\n'
117 feature += 'GPG_PATH = "%s"\n' % self.gpg_dir
118 feature += 'SSTATE_DIR = "%s"\n' % sstatedir
119 # Any mirror might have partial sstate without .sig files, triggering failures
120 feature += 'SSTATE_MIRRORS_forcevariable = ""\n'
121
122 self.write_config(feature)
123
124 bitbake('-c clean %s' % test_recipe)
125 bitbake(test_recipe)
126
127 recipe_sig = glob.glob(sstatedir + '/*/*:ed:*_package.tgz.sig')
128 recipe_tgz = glob.glob(sstatedir + '/*/*:ed:*_package.tgz')
129
130 self.assertEqual(len(recipe_sig), 1, 'Failed to find .sig file.')
131 self.assertEqual(len(recipe_tgz), 1, 'Failed to find .tgz file.')
132
133 ret = runCmd('gpg --homedir %s --verify %s %s' % (self.gpg_dir, recipe_sig[0], recipe_tgz[0]))
134 # gpg: Signature made Thu 22 Oct 2015 01:45:09 PM EEST using RSA key ID 61EEFB30
135 # gpg: Good signature from "testuser (nocomment) <testuser@email.com>"
136 self.assertIn('gpg: Good signature from', ret.output, 'Package signed incorrectly.')
137
138
139class LockedSignatures(OESelftestTestCase):
140
141 @OETestID(1420)
142 def test_locked_signatures(self):
143 """
144 Summary: Test locked signature mechanism
145 Expected: Locked signatures will prevent task to run
146 Product: oe-core
147 Author: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
148 AutomatedBy: Daniel Istrate <daniel.alexandrux.istrate@intel.com>
149 """
150
151 test_recipe = 'ed'
152 locked_sigs_file = 'locked-sigs.inc'
153
154 self.add_command_to_tearDown('rm -f %s' % os.path.join(self.builddir, locked_sigs_file))
155
156 bitbake(test_recipe)
157 # Generate locked sigs include file
158 bitbake('-S none %s' % test_recipe)
159
160 feature = 'require %s\n' % locked_sigs_file
161 feature += 'SIGGEN_LOCKEDSIGS_TASKSIG_CHECK = "warn"\n'
162 self.write_config(feature)
163
164 # Build a locked recipe
165 bitbake(test_recipe)
166
167 # Make a change that should cause the locked task signature to change
168 recipe_append_file = test_recipe + '_' + get_bb_var('PV', test_recipe) + '.bbappend'
169 recipe_append_path = os.path.join(self.testlayer_path, 'recipes-test', test_recipe, recipe_append_file)
170 feature = 'SUMMARY += "test locked signature"\n'
171
172 os.mkdir(os.path.join(self.testlayer_path, 'recipes-test', test_recipe))
173 write_file(recipe_append_path, feature)
174
175 self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, 'recipes-test', test_recipe))
176
177 # Build the recipe again
178 ret = bitbake(test_recipe)
179
180 # Verify you get the warning and that the real task *isn't* run (i.e. the locked signature has worked)
181 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
182 found_warn = re.search(patt, ret.output)
183
184 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 @@
1import datetime
2import unittest
3import os
4import re
5import shutil
6
7import oeqa.utils.ftools as ftools
8from oeqa.selftest.case import OESelftestTestCase
9from oeqa.utils.commands import runCmd, bitbake, get_bb_vars, get_test_layer
10
11
12class SStateBase(OESelftestTestCase):
13
14 def setUpLocal(self):
15 super(SStateBase, self).setUpLocal()
16 self.temp_sstate_location = None
17 needed_vars = ['SSTATE_DIR', 'NATIVELSBSTRING', 'TCLIBC', 'TUNE_ARCH',
18 'TOPDIR', 'TARGET_VENDOR', 'TARGET_OS']
19 bb_vars = get_bb_vars(needed_vars)
20 self.sstate_path = bb_vars['SSTATE_DIR']
21 self.hostdistro = bb_vars['NATIVELSBSTRING']
22 self.tclibc = bb_vars['TCLIBC']
23 self.tune_arch = bb_vars['TUNE_ARCH']
24 self.topdir = bb_vars['TOPDIR']
25 self.target_vendor = bb_vars['TARGET_VENDOR']
26 self.target_os = bb_vars['TARGET_OS']
27 self.distro_specific_sstate = os.path.join(self.sstate_path, self.hostdistro)
28
29 # Creates a special sstate configuration with the option to add sstate mirrors
30 def config_sstate(self, temp_sstate_location=False, add_local_mirrors=[]):
31 self.temp_sstate_location = temp_sstate_location
32
33 if self.temp_sstate_location:
34 temp_sstate_path = os.path.join(self.builddir, "temp_sstate_%s" % datetime.datetime.now().strftime('%Y%m%d%H%M%S'))
35 config_temp_sstate = "SSTATE_DIR = \"%s\"" % temp_sstate_path
36 self.append_config(config_temp_sstate)
37 self.track_for_cleanup(temp_sstate_path)
38 bb_vars = get_bb_vars(['SSTATE_DIR', 'NATIVELSBSTRING'])
39 self.sstate_path = bb_vars['SSTATE_DIR']
40 self.hostdistro = bb_vars['NATIVELSBSTRING']
41 self.distro_specific_sstate = os.path.join(self.sstate_path, self.hostdistro)
42
43 if add_local_mirrors:
44 config_set_sstate_if_not_set = 'SSTATE_MIRRORS ?= ""'
45 self.append_config(config_set_sstate_if_not_set)
46 for local_mirror in add_local_mirrors:
47 self.assertFalse(os.path.join(local_mirror) == os.path.join(self.sstate_path), msg='Cannot add the current sstate path as a sstate mirror')
48 config_sstate_mirror = "SSTATE_MIRRORS += \"file://.* file:///%s/PATH\"" % local_mirror
49 self.append_config(config_sstate_mirror)
50
51 # Returns a list containing sstate files
52 def search_sstate(self, filename_regex, distro_specific=True, distro_nonspecific=True):
53 result = []
54 for root, dirs, files in os.walk(self.sstate_path):
55 if distro_specific and re.search("%s/[a-z0-9]{2}$" % self.hostdistro, root):
56 for f in files:
57 if re.search(filename_regex, f):
58 result.append(f)
59 if distro_nonspecific and re.search("%s/[a-z0-9]{2}$" % self.sstate_path, root):
60 for f in files:
61 if re.search(filename_regex, f):
62 result.append(f)
63 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 @@
1import os
2import shutil
3import glob
4import subprocess
5
6from oeqa.selftest.case import OESelftestTestCase
7from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer
8from oeqa.selftest.cases.sstate import SStateBase
9from oeqa.core.decorator.oeid import OETestID
10
11class SStateTests(SStateBase):
12
13 # Test sstate files creation and their location
14 def run_test_sstate_creation(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True, should_pass=True):
15 self.config_sstate(temp_sstate_location, [self.sstate_path])
16
17 if self.temp_sstate_location:
18 bitbake(['-cclean'] + targets)
19 else:
20 bitbake(['-ccleansstate'] + targets)
21
22 bitbake(targets)
23 file_tracker = []
24 results = self.search_sstate('|'.join(map(str, targets)), distro_specific, distro_nonspecific)
25 if distro_nonspecific:
26 for r in results:
27 if r.endswith(("_populate_lic.tgz", "_populate_lic.tgz.siginfo", "_fetch.tgz.siginfo", "_unpack.tgz.siginfo", "_patch.tgz.siginfo")):
28 continue
29 file_tracker.append(r)
30 else:
31 file_tracker = results
32
33 if should_pass:
34 self.assertTrue(file_tracker , msg="Could not find sstate files for: %s" % ', '.join(map(str, targets)))
35 else:
36 self.assertTrue(not file_tracker , msg="Found sstate files in the wrong place for: %s (found %s)" % (', '.join(map(str, targets)), str(file_tracker)))
37
38 @OETestID(975)
39 def test_sstate_creation_distro_specific_pass(self):
40 self.run_test_sstate_creation(['binutils-cross-'+ self.tune_arch, 'binutils-native'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
41
42 @OETestID(1374)
43 def test_sstate_creation_distro_specific_fail(self):
44 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)
45
46 @OETestID(976)
47 def test_sstate_creation_distro_nonspecific_pass(self):
48 self.run_test_sstate_creation(['linux-libc-headers'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
49
50 @OETestID(1375)
51 def test_sstate_creation_distro_nonspecific_fail(self):
52 self.run_test_sstate_creation(['linux-libc-headers'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True, should_pass=False)
53
54 # Test the sstate files deletion part of the do_cleansstate task
55 def run_test_cleansstate_task(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True):
56 self.config_sstate(temp_sstate_location, [self.sstate_path])
57
58 bitbake(['-ccleansstate'] + targets)
59
60 bitbake(targets)
61 tgz_created = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific)
62 self.assertTrue(tgz_created, msg="Could not find sstate .tgz files for: %s (%s)" % (', '.join(map(str, targets)), str(tgz_created)))
63
64 siginfo_created = self.search_sstate('|'.join(map(str, [s + '.*?\.siginfo$' for s in targets])), distro_specific, distro_nonspecific)
65 self.assertTrue(siginfo_created, msg="Could not find sstate .siginfo files for: %s (%s)" % (', '.join(map(str, targets)), str(siginfo_created)))
66
67 bitbake(['-ccleansstate'] + targets)
68 tgz_removed = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific, distro_nonspecific)
69 self.assertTrue(not tgz_removed, msg="do_cleansstate didn't remove .tgz sstate files for: %s (%s)" % (', '.join(map(str, targets)), str(tgz_removed)))
70
71 @OETestID(977)
72 def test_cleansstate_task_distro_specific_nonspecific(self):
73 targets = ['binutils-cross-'+ self.tune_arch, 'binutils-native']
74 targets.append('linux-libc-headers')
75 self.run_test_cleansstate_task(targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True)
76
77 @OETestID(1376)
78 def test_cleansstate_task_distro_nonspecific(self):
79 self.run_test_cleansstate_task(['linux-libc-headers'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True)
80
81 @OETestID(1377)
82 def test_cleansstate_task_distro_specific(self):
83 targets = ['binutils-cross-'+ self.tune_arch, 'binutils-native']
84 targets.append('linux-libc-headers')
85 self.run_test_cleansstate_task(targets, distro_specific=True, distro_nonspecific=False, temp_sstate_location=True)
86
87
88 # Test rebuilding of distro-specific sstate files
89 def run_test_rebuild_distro_specific_sstate(self, targets, temp_sstate_location=True):
90 self.config_sstate(temp_sstate_location, [self.sstate_path])
91
92 bitbake(['-ccleansstate'] + targets)
93
94 bitbake(targets)
95 results = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=False, distro_nonspecific=True)
96 filtered_results = []
97 for r in results:
98 if r.endswith(("_populate_lic.tgz", "_populate_lic.tgz.siginfo")):
99 continue
100 filtered_results.append(r)
101 self.assertTrue(filtered_results == [], msg="Found distro non-specific sstate for: %s (%s)" % (', '.join(map(str, targets)), str(filtered_results)))
102 file_tracker_1 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False)
103 self.assertTrue(len(file_tracker_1) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets)))
104
105 self.track_for_cleanup(self.distro_specific_sstate + "_old")
106 shutil.copytree(self.distro_specific_sstate, self.distro_specific_sstate + "_old")
107 shutil.rmtree(self.distro_specific_sstate)
108
109 bitbake(['-cclean'] + targets)
110 bitbake(targets)
111 file_tracker_2 = self.search_sstate('|'.join(map(str, [s + '.*?\.tgz$' for s in targets])), distro_specific=True, distro_nonspecific=False)
112 self.assertTrue(len(file_tracker_2) >= len(targets), msg = "Not all sstate files ware created for: %s" % ', '.join(map(str, targets)))
113
114 not_recreated = [x for x in file_tracker_1 if x not in file_tracker_2]
115 self.assertTrue(not_recreated == [], msg="The following sstate files ware not recreated: %s" % ', '.join(map(str, not_recreated)))
116
117 created_once = [x for x in file_tracker_2 if x not in file_tracker_1]
118 self.assertTrue(created_once == [], msg="The following sstate files ware created only in the second run: %s" % ', '.join(map(str, created_once)))
119
120 @OETestID(175)
121 def test_rebuild_distro_specific_sstate_cross_native_targets(self):
122 self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + self.tune_arch, 'binutils-native'], temp_sstate_location=True)
123
124 @OETestID(1372)
125 def test_rebuild_distro_specific_sstate_cross_target(self):
126 self.run_test_rebuild_distro_specific_sstate(['binutils-cross-' + self.tune_arch], temp_sstate_location=True)
127
128 @OETestID(1373)
129 def test_rebuild_distro_specific_sstate_native_target(self):
130 self.run_test_rebuild_distro_specific_sstate(['binutils-native'], temp_sstate_location=True)
131
132
133 # Test the sstate-cache-management script. Each element in the global_config list is used with the corresponding element in the target_config list
134 # 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)
135 def run_test_sstate_cache_management_script(self, target, global_config=[''], target_config=[''], ignore_patterns=[]):
136 self.assertTrue(global_config)
137 self.assertTrue(target_config)
138 self.assertTrue(len(global_config) == len(target_config), msg='Lists global_config and target_config should have the same number of elements')
139 self.config_sstate(temp_sstate_location=True, add_local_mirrors=[self.sstate_path])
140
141 # If buildhistory is enabled, we need to disable version-going-backwards
142 # QA checks for this test. It may report errors otherwise.
143 self.append_config('ERROR_QA_remove = "version-going-backwards"')
144
145 # For not this only checks if random sstate tasks are handled correctly as a group.
146 # In the future we should add control over what tasks we check for.
147
148 sstate_archs_list = []
149 expected_remaining_sstate = []
150 for idx in range(len(target_config)):
151 self.append_config(global_config[idx])
152 self.append_recipeinc(target, target_config[idx])
153 sstate_arch = get_bb_var('SSTATE_PKGARCH', target)
154 if not sstate_arch in sstate_archs_list:
155 sstate_archs_list.append(sstate_arch)
156 if target_config[idx] == target_config[-1]:
157 target_sstate_before_build = self.search_sstate(target + '.*?\.tgz$')
158 bitbake("-cclean %s" % target)
159 result = bitbake(target, ignore_status=True)
160 if target_config[idx] == target_config[-1]:
161 target_sstate_after_build = self.search_sstate(target + '.*?\.tgz$')
162 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)]
163 self.remove_config(global_config[idx])
164 self.remove_recipeinc(target, target_config[idx])
165 self.assertEqual(result.status, 0, msg = "build of %s failed with %s" % (target, result.output))
166
167 runCmd("sstate-cache-management.sh -y --cache-dir=%s --remove-duplicated --extra-archs=%s" % (self.sstate_path, ','.join(map(str, sstate_archs_list))))
168 actual_remaining_sstate = [x for x in self.search_sstate(target + '.*?\.tgz$') if not any(pattern in x for pattern in ignore_patterns)]
169
170 actual_not_expected = [x for x in actual_remaining_sstate if x not in expected_remaining_sstate]
171 self.assertFalse(actual_not_expected, msg="Files should have been removed but ware not: %s" % ', '.join(map(str, actual_not_expected)))
172 expected_not_actual = [x for x in expected_remaining_sstate if x not in actual_remaining_sstate]
173 self.assertFalse(expected_not_actual, msg="Extra files ware removed: %s" ', '.join(map(str, expected_not_actual)))
174
175 @OETestID(973)
176 def test_sstate_cache_management_script_using_pr_1(self):
177 global_config = []
178 target_config = []
179 global_config.append('')
180 target_config.append('PR = "0"')
181 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
182
183 @OETestID(978)
184 def test_sstate_cache_management_script_using_pr_2(self):
185 global_config = []
186 target_config = []
187 global_config.append('')
188 target_config.append('PR = "0"')
189 global_config.append('')
190 target_config.append('PR = "1"')
191 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
192
193 @OETestID(979)
194 def test_sstate_cache_management_script_using_pr_3(self):
195 global_config = []
196 target_config = []
197 global_config.append('MACHINE = "qemux86-64"')
198 target_config.append('PR = "0"')
199 global_config.append(global_config[0])
200 target_config.append('PR = "1"')
201 global_config.append('MACHINE = "qemux86"')
202 target_config.append('PR = "1"')
203 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
204
205 @OETestID(974)
206 def test_sstate_cache_management_script_using_machine(self):
207 global_config = []
208 target_config = []
209 global_config.append('MACHINE = "qemux86-64"')
210 target_config.append('')
211 global_config.append('MACHINE = "qemux86"')
212 target_config.append('')
213 self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic'])
214
215 @OETestID(1270)
216 def test_sstate_32_64_same_hash(self):
217 """
218 The sstate checksums for both native and target should not vary whether
219 they're built on a 32 or 64 bit system. Rather than requiring two different
220 build machines and running a builds, override the variables calling uname()
221 manually and check using bitbake -S.
222 """
223
224 self.write_config("""
225MACHINE = "qemux86"
226TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
227BUILD_ARCH = "x86_64"
228BUILD_OS = "linux"
229SDKMACHINE = "x86_64"
230PACKAGE_CLASSES = "package_rpm package_ipk package_deb"
231""")
232 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
233 bitbake("core-image-sato -S none")
234 self.write_config("""
235MACHINE = "qemux86"
236TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
237BUILD_ARCH = "i686"
238BUILD_OS = "linux"
239SDKMACHINE = "i686"
240PACKAGE_CLASSES = "package_rpm package_ipk package_deb"
241""")
242 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
243 bitbake("core-image-sato -S none")
244
245 def get_files(d):
246 f = []
247 for root, dirs, files in os.walk(d):
248 if "core-image-sato" in root:
249 # SDKMACHINE changing will change
250 # do_rootfs/do_testimage/do_build stamps of images which
251 # is safe to ignore.
252 continue
253 f.extend(os.path.join(root, name) for name in files)
254 return f
255 files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/")
256 files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/")
257 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]
258 self.maxDiff = None
259 self.assertCountEqual(files1, files2)
260
261
262 @OETestID(1271)
263 def test_sstate_nativelsbstring_same_hash(self):
264 """
265 The sstate checksums should be independent of whichever NATIVELSBSTRING is
266 detected. Rather than requiring two different build machines and running
267 builds, override the variables manually and check using bitbake -S.
268 """
269
270 self.write_config("""
271TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
272NATIVELSBSTRING = \"DistroA\"
273""")
274 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
275 bitbake("core-image-sato -S none")
276 self.write_config("""
277TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
278NATIVELSBSTRING = \"DistroB\"
279""")
280 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
281 bitbake("core-image-sato -S none")
282
283 def get_files(d):
284 f = []
285 for root, dirs, files in os.walk(d):
286 f.extend(os.path.join(root, name) for name in files)
287 return f
288 files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/")
289 files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/")
290 files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash") for x in files2]
291 self.maxDiff = None
292 self.assertCountEqual(files1, files2)
293
294 @OETestID(1368)
295 def test_sstate_allarch_samesigs(self):
296 """
297 The sstate checksums of allarch packages should be independent of whichever
298 MACHINE is set. Check this using bitbake -S.
299 Also, rather than duplicate the test, check nativesdk stamps are the same between
300 the two MACHINE values.
301 """
302
303 configA = """
304TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
305MACHINE = \"qemux86-64\"
306"""
307 configB = """
308TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
309MACHINE = \"qemuarm\"
310"""
311 self.sstate_allarch_samesigs(configA, configB)
312
313 def test_sstate_allarch_samesigs_multilib(self):
314 """
315 The sstate checksums of allarch multilib packages should be independent of whichever
316 MACHINE is set. Check this using bitbake -S.
317 Also, rather than duplicate the test, check nativesdk stamps are the same between
318 the two MACHINE values.
319 """
320
321 configA = """
322TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
323MACHINE = \"qemux86-64\"
324require conf/multilib.conf
325MULTILIBS = \"multilib:lib32\"
326DEFAULTTUNE_virtclass-multilib-lib32 = \"x86\"
327"""
328 configB = """
329TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
330MACHINE = \"qemuarm\"
331require conf/multilib.conf
332MULTILIBS = \"\"
333"""
334 self.sstate_allarch_samesigs(configA, configB)
335
336 def sstate_allarch_samesigs(self, configA, configB):
337
338 self.write_config(configA)
339 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
340 bitbake("world meta-toolchain -S none")
341 self.write_config(configB)
342 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
343 bitbake("world meta-toolchain -S none")
344
345 def get_files(d):
346 f = {}
347 for root, dirs, files in os.walk(d):
348 for name in files:
349 if "meta-environment" in root or "cross-canadian" in root:
350 continue
351 if "do_build" not in name:
352 # 1.4.1+gitAUTOINC+302fca9f4c-r0.do_package_write_ipk.sigdata.f3a2a38697da743f0dbed8b56aafcf79
353 (_, task, _, shash) = name.rsplit(".", 3)
354 f[os.path.join(os.path.basename(root), task)] = shash
355 return f
356 files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/all" + self.target_vendor + "-" + self.target_os)
357 files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/all" + self.target_vendor + "-" + self.target_os)
358 self.maxDiff = None
359 self.assertEqual(files1, files2)
360
361 nativesdkdir = os.path.basename(glob.glob(self.topdir + "/tmp-sstatesamehash/stamps/*-nativesdk*-linux")[0])
362
363 files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/" + nativesdkdir)
364 files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/" + nativesdkdir)
365 self.maxDiff = None
366 self.assertEqual(files1, files2)
367
368 @OETestID(1369)
369 def test_sstate_sametune_samesigs(self):
370 """
371 The sstate checksums of two identical machines (using the same tune) should be the
372 same, apart from changes within the machine specific stamps directory. We use the
373 qemux86copy machine to test this. Also include multilibs in the test.
374 """
375
376 self.write_config("""
377TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
378MACHINE = \"qemux86\"
379require conf/multilib.conf
380MULTILIBS = "multilib:lib32"
381DEFAULTTUNE_virtclass-multilib-lib32 = "x86"
382""")
383 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
384 bitbake("world meta-toolchain -S none")
385 self.write_config("""
386TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
387MACHINE = \"qemux86copy\"
388require conf/multilib.conf
389MULTILIBS = "multilib:lib32"
390DEFAULTTUNE_virtclass-multilib-lib32 = "x86"
391""")
392 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
393 bitbake("world meta-toolchain -S none")
394
395 def get_files(d):
396 f = []
397 for root, dirs, files in os.walk(d):
398 for name in files:
399 if "meta-environment" in root or "cross-canadian" in root:
400 continue
401 if "qemux86copy-" in root or "qemux86-" in root:
402 continue
403 if "do_build" not in name and "do_populate_sdk" not in name:
404 f.append(os.path.join(root, name))
405 return f
406 files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps")
407 files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps")
408 files2 = [x.replace("tmp-sstatesamehash2", "tmp-sstatesamehash") for x in files2]
409 self.maxDiff = None
410 self.assertCountEqual(files1, files2)
411
412
413 def test_sstate_noop_samesigs(self):
414 """
415 The sstate checksums of two builds with these variables changed or
416 classes inherits should be the same.
417 """
418
419 self.write_config("""
420TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
421BB_NUMBER_THREADS = "1"
422PARALLEL_MAKE = "-j 1"
423DL_DIR = "${TOPDIR}/download1"
424TIME = "111111"
425DATE = "20161111"
426INHERIT_remove = "buildstats-summary buildhistory uninative"
427http_proxy = ""
428""")
429 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash")
430 self.track_for_cleanup(self.topdir + "/download1")
431 bitbake("world meta-toolchain -S none")
432 self.write_config("""
433TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
434BB_NUMBER_THREADS = "2"
435PARALLEL_MAKE = "-j 2"
436DL_DIR = "${TOPDIR}/download2"
437TIME = "222222"
438DATE = "20161212"
439# Always remove uninative as we're changing proxies
440INHERIT_remove = "uninative"
441INHERIT += "buildstats-summary buildhistory"
442http_proxy = "http://example.com/"
443""")
444 self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2")
445 self.track_for_cleanup(self.topdir + "/download2")
446 bitbake("world meta-toolchain -S none")
447
448 def get_files(d):
449 f = {}
450 for root, dirs, files in os.walk(d):
451 for name in files:
452 name, shash = name.rsplit('.', 1)
453 # Extract just the machine and recipe name
454 base = os.sep.join(root.rsplit(os.sep, 2)[-2:] + [name])
455 f[base] = shash
456 return f
457 files1 = get_files(self.topdir + "/tmp-sstatesamehash/stamps/")
458 files2 = get_files(self.topdir + "/tmp-sstatesamehash2/stamps/")
459 # Remove items that are identical in both sets
460 for k,v in files1.items() & files2.items():
461 del files1[k]
462 del files2[k]
463 if not files1 and not files2:
464 # No changes, so we're done
465 return
466
467 for k in files1.keys() | files2.keys():
468 if k in files1 and k in files2:
469 print("%s differs:" % k)
470 print(subprocess.check_output(("bitbake-diffsigs",
471 self.topdir + "/tmp-sstatesamehash/stamps/" + k + "." + files1[k],
472 self.topdir + "/tmp-sstatesamehash2/stamps/" + k + "." + files2[k])))
473 elif k in files1 and k not in files2:
474 print("%s in files1" % k)
475 elif k not in files1 and k in files2:
476 print("%s in files2" % k)
477 else:
478 assert "shouldn't reach here"
479 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 @@
1import os
2import re
3import bb.tinfoil
4
5from oeqa.selftest.case import OESelftestTestCase
6from oeqa.utils.commands import runCmd
7from oeqa.core.decorator.oeid import OETestID
8
9class TinfoilTests(OESelftestTestCase):
10 """ Basic tests for the tinfoil API """
11
12 @OETestID(1568)
13 def test_getvar(self):
14 with bb.tinfoil.Tinfoil() as tinfoil:
15 tinfoil.prepare(True)
16 machine = tinfoil.config_data.getVar('MACHINE')
17 if not machine:
18 self.fail('Unable to get MACHINE value - returned %s' % machine)
19
20 @OETestID(1569)
21 def test_expand(self):
22 with bb.tinfoil.Tinfoil() as tinfoil:
23 tinfoil.prepare(True)
24 expr = '${@os.getpid()}'
25 pid = tinfoil.config_data.expand(expr)
26 if not pid:
27 self.fail('Unable to expand "%s" - returned %s' % (expr, pid))
28
29 @OETestID(1570)
30 def test_getvar_bb_origenv(self):
31 with bb.tinfoil.Tinfoil() as tinfoil:
32 tinfoil.prepare(True)
33 origenv = tinfoil.config_data.getVar('BB_ORIGENV', False)
34 if not origenv:
35 self.fail('Unable to get BB_ORIGENV value - returned %s' % origenv)
36 self.assertEqual(origenv.getVar('HOME', False), os.environ['HOME'])
37
38 @OETestID(1571)
39 def test_parse_recipe(self):
40 with bb.tinfoil.Tinfoil() as tinfoil:
41 tinfoil.prepare(config_only=False, quiet=2)
42 testrecipe = 'mdadm'
43 best = tinfoil.find_best_provider(testrecipe)
44 if not best:
45 self.fail('Unable to find recipe providing %s' % testrecipe)
46 rd = tinfoil.parse_recipe_file(best[3])
47 self.assertEqual(testrecipe, rd.getVar('PN'))
48
49 @OETestID(1572)
50 def test_parse_recipe_copy_expand(self):
51 with bb.tinfoil.Tinfoil() as tinfoil:
52 tinfoil.prepare(config_only=False, quiet=2)
53 testrecipe = 'mdadm'
54 best = tinfoil.find_best_provider(testrecipe)
55 if not best:
56 self.fail('Unable to find recipe providing %s' % testrecipe)
57 rd = tinfoil.parse_recipe_file(best[3])
58 # Check we can get variable values
59 self.assertEqual(testrecipe, rd.getVar('PN'))
60 # Check that expanding a value that includes a variable reference works
61 self.assertEqual(testrecipe, rd.getVar('BPN'))
62 # Now check that changing the referenced variable's value in a copy gives that
63 # value when expanding
64 localdata = bb.data.createCopy(rd)
65 localdata.setVar('PN', 'hello')
66 self.assertEqual('hello', localdata.getVar('BPN'))
67
68 @OETestID(1573)
69 def test_parse_recipe_initial_datastore(self):
70 with bb.tinfoil.Tinfoil() as tinfoil:
71 tinfoil.prepare(config_only=False, quiet=2)
72 testrecipe = 'mdadm'
73 best = tinfoil.find_best_provider(testrecipe)
74 if not best:
75 self.fail('Unable to find recipe providing %s' % testrecipe)
76 dcopy = bb.data.createCopy(tinfoil.config_data)
77 dcopy.setVar('MYVARIABLE', 'somevalue')
78 rd = tinfoil.parse_recipe_file(best[3], config_data=dcopy)
79 # Check we can get variable values
80 self.assertEqual('somevalue', rd.getVar('MYVARIABLE'))
81
82 @OETestID(1574)
83 def test_list_recipes(self):
84 with bb.tinfoil.Tinfoil() as tinfoil:
85 tinfoil.prepare(config_only=False, quiet=2)
86 # Check pkg_pn
87 checkpns = ['tar', 'automake', 'coreutils', 'm4-native', 'nativesdk-gcc']
88 pkg_pn = tinfoil.cooker.recipecaches[''].pkg_pn
89 for pn in checkpns:
90 self.assertIn(pn, pkg_pn)
91 # Check pkg_fn
92 checkfns = {'nativesdk-gcc': '^virtual:nativesdk:.*', 'coreutils': '.*/coreutils_.*.bb'}
93 for fn, pn in tinfoil.cooker.recipecaches[''].pkg_fn.items():
94 if pn in checkpns:
95 if pn in checkfns:
96 self.assertTrue(re.match(checkfns[pn], fn), 'Entry for %s: %s did not match %s' % (pn, fn, checkfns[pn]))
97 checkpns.remove(pn)
98 if checkpns:
99 self.fail('Unable to find pkg_fn entries for: %s' % ', '.join(checkpns))
100
101 @OETestID(1575)
102 def test_wait_event(self):
103 with bb.tinfoil.Tinfoil() as tinfoil:
104 tinfoil.prepare(config_only=True)
105 # Need to drain events otherwise events that will be masked will still be in the queue
106 while tinfoil.wait_event(0.25):
107 pass
108 tinfoil.set_event_mask(['bb.event.FilesMatchingFound', 'bb.command.CommandCompleted'])
109 pattern = 'conf'
110 res = tinfoil.run_command('findFilesMatchingInDir', pattern, 'conf/machine')
111 self.assertTrue(res)
112
113 eventreceived = False
114 waitcount = 5
115 while waitcount > 0:
116 event = tinfoil.wait_event(1)
117 if event:
118 if isinstance(event, bb.command.CommandCompleted):
119 break
120 elif isinstance(event, bb.event.FilesMatchingFound):
121 self.assertEqual(pattern, event._pattern)
122 self.assertIn('qemuarm.conf', event._matches)
123 eventreceived = True
124 else:
125 self.fail('Unexpected event: %s' % event)
126
127 waitcount = waitcount - 1
128
129 self.assertNotEqual(waitcount, 0, 'Timed out waiting for CommandCompleted event from bitbake server')
130 self.assertTrue(eventreceived, 'Did not receive FilesMatchingFound event from bitbake server')
131
132 @OETestID(1576)
133 def test_setvariable_clean(self):
134 # First check that setVariable affects the datastore
135 with bb.tinfoil.Tinfoil() as tinfoil:
136 tinfoil.prepare(config_only=True)
137 tinfoil.run_command('setVariable', 'TESTVAR', 'specialvalue')
138 self.assertEqual(tinfoil.config_data.getVar('TESTVAR'), 'specialvalue', 'Value set using setVariable is not reflected in client-side getVar()')
139
140 # Now check that the setVariable's effects are no longer present
141 # (this may legitimately break in future if we stop reinitialising
142 # the datastore, in which case we'll have to reconsider use of
143 # setVariable entirely)
144 with bb.tinfoil.Tinfoil() as tinfoil:
145 tinfoil.prepare(config_only=True)
146 self.assertNotEqual(tinfoil.config_data.getVar('TESTVAR'), 'specialvalue', 'Value set using setVariable is still present!')
147
148 # Now check that setVar on the main datastore works (uses setVariable internally)
149 with bb.tinfoil.Tinfoil() as tinfoil:
150 tinfoil.prepare(config_only=True)
151 tinfoil.config_data.setVar('TESTVAR', 'specialvalue')
152 value = tinfoil.run_command('getVariable', 'TESTVAR')
153 self.assertEqual(value, 'specialvalue', 'Value set using config_data.setVar() is not reflected in config_data.getVar()')
154
155 def test_datastore_operations(self):
156 with bb.tinfoil.Tinfoil() as tinfoil:
157 tinfoil.prepare(config_only=True)
158 # Test setVarFlag() / getVarFlag()
159 tinfoil.config_data.setVarFlag('TESTVAR', 'flagname', 'flagval')
160 value = tinfoil.config_data.getVarFlag('TESTVAR', 'flagname')
161 self.assertEqual(value, 'flagval', 'Value set using config_data.setVarFlag() is not reflected in config_data.getVarFlag()')
162 # Test delVarFlag()
163 tinfoil.config_data.setVarFlag('TESTVAR', 'otherflag', 'othervalue')
164 tinfoil.config_data.delVarFlag('TESTVAR', 'flagname')
165 value = tinfoil.config_data.getVarFlag('TESTVAR', 'flagname')
166 self.assertEqual(value, None, 'Varflag deleted using config_data.delVarFlag() is not reflected in config_data.getVarFlag()')
167 value = tinfoil.config_data.getVarFlag('TESTVAR', 'otherflag')
168 self.assertEqual(value, 'othervalue', 'Varflag deleted using config_data.delVarFlag() caused unrelated flag to be removed')
169 # Test delVar()
170 tinfoil.config_data.setVar('TESTVAR', 'varvalue')
171 value = tinfoil.config_data.getVar('TESTVAR')
172 self.assertEqual(value, 'varvalue', 'Value set using config_data.setVar() is not reflected in config_data.getVar()')
173 tinfoil.config_data.delVar('TESTVAR')
174 value = tinfoil.config_data.getVar('TESTVAR')
175 self.assertEqual(value, None, 'Variable deleted using config_data.delVar() appears to still have a value')
176 # Test renameVar()
177 tinfoil.config_data.setVar('TESTVAROLD', 'origvalue')
178 tinfoil.config_data.renameVar('TESTVAROLD', 'TESTVARNEW')
179 value = tinfoil.config_data.getVar('TESTVAROLD')
180 self.assertEqual(value, None, 'Variable renamed using config_data.renameVar() still seems to exist')
181 value = tinfoil.config_data.getVar('TESTVARNEW')
182 self.assertEqual(value, 'origvalue', 'Variable renamed using config_data.renameVar() does not appear with new name')
183 # Test overrides
184 tinfoil.config_data.setVar('TESTVAR', 'original')
185 tinfoil.config_data.setVar('TESTVAR_overrideone', 'one')
186 tinfoil.config_data.setVar('TESTVAR_overridetwo', 'two')
187 tinfoil.config_data.appendVar('OVERRIDES', ':overrideone')
188 value = tinfoil.config_data.getVar('TESTVAR')
189 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 @@
1#!/usr/bin/env python
2# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4#
5# Copyright (c) 2015, Intel Corporation.
6# All rights reserved.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20#
21# AUTHORS
22# Ed Bartosh <ed.bartosh@linux.intel.com>
23
24"""Test cases for wic."""
25
26import os
27import sys
28import unittest
29
30from glob import glob
31from shutil import rmtree
32from functools import wraps, lru_cache
33from tempfile import NamedTemporaryFile
34
35from oeqa.selftest.case import OESelftestTestCase
36from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu
37from oeqa.core.decorator.oeid import OETestID
38
39
40@lru_cache(maxsize=32)
41def get_host_arch(recipe):
42 """A cached call to get_bb_var('HOST_ARCH', <recipe>)"""
43 return get_bb_var('HOST_ARCH', recipe)
44
45
46def only_for_arch(archs, image='core-image-minimal'):
47 """Decorator for wrapping test cases that can be run only for specific target
48 architectures. A list of compatible architectures is passed in `archs`.
49 Current architecture will be determined by parsing bitbake output for
50 `image` recipe.
51 """
52 def wrapper(func):
53 @wraps(func)
54 def wrapped_f(*args, **kwargs):
55 arch = get_host_arch(image)
56 if archs and arch not in archs:
57 raise unittest.SkipTest("Testcase arch dependency not met: %s" % arch)
58 return func(*args, **kwargs)
59 wrapped_f.__name__ = func.__name__
60 return wrapped_f
61 return wrapper
62
63
64class Wic(OESelftestTestCase):
65 """Wic test class."""
66
67 resultdir = "/var/tmp/wic.oe-selftest/"
68 image_is_ready = False
69 native_sysroot = None
70 wicenv_cache = {}
71
72 def setUpLocal(self):
73 """This code is executed before each test method."""
74 super(Wic, self).setUpLocal()
75 if not self.native_sysroot:
76 Wic.native_sysroot = get_bb_var('STAGING_DIR_NATIVE', 'wic-tools')
77
78 # Do this here instead of in setUpClass as the base setUp does some
79 # clean up which can result in the native tools built earlier in
80 # setUpClass being unavailable.
81 if not Wic.image_is_ready:
82 if get_bb_var('USE_NLS') == 'yes':
83 bitbake('wic-tools')
84 else:
85 self.skipTest('wic-tools cannot be built due its (intltool|gettext)-native dependency and NLS disable')
86
87 bitbake('core-image-minimal')
88 Wic.image_is_ready = True
89
90 rmtree(self.resultdir, ignore_errors=True)
91
92 def tearDownLocal(self):
93 """Remove resultdir as it may contain images."""
94 rmtree(self.resultdir, ignore_errors=True)
95 super(Wic, self).tearDownLocal()
96
97 @OETestID(1552)
98 def test_version(self):
99 """Test wic --version"""
100 self.assertEqual(0, runCmd('wic --version').status)
101
102 @OETestID(1208)
103 def test_help(self):
104 """Test wic --help and wic -h"""
105 self.assertEqual(0, runCmd('wic --help').status)
106 self.assertEqual(0, runCmd('wic -h').status)
107
108 @OETestID(1209)
109 def test_createhelp(self):
110 """Test wic create --help"""
111 self.assertEqual(0, runCmd('wic create --help').status)
112
113 @OETestID(1210)
114 def test_listhelp(self):
115 """Test wic list --help"""
116 self.assertEqual(0, runCmd('wic list --help').status)
117
118 @OETestID(1553)
119 def test_help_create(self):
120 """Test wic help create"""
121 self.assertEqual(0, runCmd('wic help create').status)
122
123 @OETestID(1554)
124 def test_help_list(self):
125 """Test wic help list"""
126 self.assertEqual(0, runCmd('wic help list').status)
127
128 @OETestID(1215)
129 def test_help_overview(self):
130 """Test wic help overview"""
131 self.assertEqual(0, runCmd('wic help overview').status)
132
133 @OETestID(1216)
134 def test_help_plugins(self):
135 """Test wic help plugins"""
136 self.assertEqual(0, runCmd('wic help plugins').status)
137
138 @OETestID(1217)
139 def test_help_kickstart(self):
140 """Test wic help kickstart"""
141 self.assertEqual(0, runCmd('wic help kickstart').status)
142
143 @OETestID(1555)
144 def test_list_images(self):
145 """Test wic list images"""
146 self.assertEqual(0, runCmd('wic list images').status)
147
148 @OETestID(1556)
149 def test_list_source_plugins(self):
150 """Test wic list source-plugins"""
151 self.assertEqual(0, runCmd('wic list source-plugins').status)
152
153 @OETestID(1557)
154 def test_listed_images_help(self):
155 """Test wic listed images help"""
156 output = runCmd('wic list images').output
157 imagelist = [line.split()[0] for line in output.splitlines()]
158 for image in imagelist:
159 self.assertEqual(0, runCmd('wic list %s help' % image).status)
160
161 @OETestID(1213)
162 def test_unsupported_subcommand(self):
163 """Test unsupported subcommand"""
164 self.assertNotEqual(0, runCmd('wic unsupported', ignore_status=True).status)
165
166 @OETestID(1214)
167 def test_no_command(self):
168 """Test wic without command"""
169 self.assertEqual(1, runCmd('wic', ignore_status=True).status)
170
171 @OETestID(1211)
172 def test_build_image_name(self):
173 """Test wic create wictestdisk --image-name=core-image-minimal"""
174 cmd = "wic create wictestdisk --image-name=core-image-minimal -o %s" % self.resultdir
175 self.assertEqual(0, runCmd(cmd).status)
176 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
177
178 @OETestID(1157)
179 @only_for_arch(['i586', 'i686', 'x86_64'])
180 def test_gpt_image(self):
181 """Test creation of core-image-minimal with gpt table and UUID boot"""
182 cmd = "wic create directdisk-gpt --image-name core-image-minimal -o %s" % self.resultdir
183 self.assertEqual(0, runCmd(cmd).status)
184 self.assertEqual(1, len(glob(self.resultdir + "directdisk-*.direct")))
185
186 @OETestID(1346)
187 @only_for_arch(['i586', 'i686', 'x86_64'])
188 def test_iso_image(self):
189 """Test creation of hybrid iso image with legacy and EFI boot"""
190 config = 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
191 'MACHINE_FEATURES_append = " efi"\n'
192 self.append_config(config)
193 bitbake('core-image-minimal')
194 self.remove_config(config)
195 cmd = "wic create mkhybridiso --image-name core-image-minimal -o %s" % self.resultdir
196 self.assertEqual(0, runCmd(cmd).status)
197 self.assertEqual(1, len(glob(self.resultdir + "HYBRID_ISO_IMG-*.direct")))
198 self.assertEqual(1, len(glob(self.resultdir + "HYBRID_ISO_IMG-*.iso")))
199
200 @OETestID(1348)
201 @only_for_arch(['i586', 'i686', 'x86_64'])
202 def test_qemux86_directdisk(self):
203 """Test creation of qemux-86-directdisk image"""
204 cmd = "wic create qemux86-directdisk -e core-image-minimal -o %s" % self.resultdir
205 self.assertEqual(0, runCmd(cmd).status)
206 self.assertEqual(1, len(glob(self.resultdir + "qemux86-directdisk-*direct")))
207
208 @OETestID(1350)
209 @only_for_arch(['i586', 'i686', 'x86_64'])
210 def test_mkefidisk(self):
211 """Test creation of mkefidisk image"""
212 cmd = "wic create mkefidisk -e core-image-minimal -o %s" % self.resultdir
213 self.assertEqual(0, runCmd(cmd).status)
214 self.assertEqual(1, len(glob(self.resultdir + "mkefidisk-*direct")))
215
216 @OETestID(1385)
217 @only_for_arch(['i586', 'i686', 'x86_64'])
218 def test_bootloader_config(self):
219 """Test creation of directdisk-bootloader-config image"""
220 cmd = "wic create directdisk-bootloader-config -e core-image-minimal -o %s" % self.resultdir
221 self.assertEqual(0, runCmd(cmd).status)
222 self.assertEqual(1, len(glob(self.resultdir + "directdisk-bootloader-config-*direct")))
223
224 @OETestID(1560)
225 @only_for_arch(['i586', 'i686', 'x86_64'])
226 def test_systemd_bootdisk(self):
227 """Test creation of systemd-bootdisk image"""
228 config = 'MACHINE_FEATURES_append = " efi"\n'
229 self.append_config(config)
230 bitbake('core-image-minimal')
231 self.remove_config(config)
232 cmd = "wic create systemd-bootdisk -e core-image-minimal -o %s" % self.resultdir
233 self.assertEqual(0, runCmd(cmd).status)
234 self.assertEqual(1, len(glob(self.resultdir + "systemd-bootdisk-*direct")))
235
236 @OETestID(1561)
237 def test_sdimage_bootpart(self):
238 """Test creation of sdimage-bootpart image"""
239 cmd = "wic create sdimage-bootpart -e core-image-minimal -o %s" % self.resultdir
240 kimgtype = get_bb_var('KERNEL_IMAGETYPE', 'core-image-minimal')
241 self.write_config('IMAGE_BOOT_FILES = "%s"\n' % kimgtype)
242 self.assertEqual(0, runCmd(cmd).status)
243 self.assertEqual(1, len(glob(self.resultdir + "sdimage-bootpart-*direct")))
244
245 @OETestID(1562)
246 @only_for_arch(['i586', 'i686', 'x86_64'])
247 def test_default_output_dir(self):
248 """Test default output location"""
249 for fname in glob("directdisk-*.direct"):
250 os.remove(fname)
251 cmd = "wic create directdisk -e core-image-minimal"
252 self.assertEqual(0, runCmd(cmd).status)
253 self.assertEqual(1, len(glob("directdisk-*.direct")))
254
255 @OETestID(1212)
256 @only_for_arch(['i586', 'i686', 'x86_64'])
257 def test_build_artifacts(self):
258 """Test wic create directdisk providing all artifacts."""
259 bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
260 'wic-tools')
261 bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
262 'core-image-minimal'))
263 bbvars = {key.lower(): value for key, value in bb_vars.items()}
264 bbvars['resultdir'] = self.resultdir
265 status = runCmd("wic create directdisk "
266 "-b %(staging_datadir)s "
267 "-k %(deploy_dir_image)s "
268 "-n %(recipe_sysroot_native)s "
269 "-r %(image_rootfs)s "
270 "-o %(resultdir)s" % bbvars).status
271 self.assertEqual(0, status)
272 self.assertEqual(1, len(glob(self.resultdir + "directdisk-*.direct")))
273
274 @OETestID(1264)
275 def test_compress_gzip(self):
276 """Test compressing an image with gzip"""
277 self.assertEqual(0, runCmd("wic create wictestdisk "
278 "--image-name core-image-minimal "
279 "-c gzip -o %s" % self.resultdir).status)
280 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct.gz")))
281
282 @OETestID(1265)
283 def test_compress_bzip2(self):
284 """Test compressing an image with bzip2"""
285 self.assertEqual(0, runCmd("wic create wictestdisk "
286 "--image-name=core-image-minimal "
287 "-c bzip2 -o %s" % self.resultdir).status)
288 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct.bz2")))
289
290 @OETestID(1266)
291 def test_compress_xz(self):
292 """Test compressing an image with xz"""
293 self.assertEqual(0, runCmd("wic create wictestdisk "
294 "--image-name=core-image-minimal "
295 "--compress-with=xz -o %s" % self.resultdir).status)
296 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct.xz")))
297
298 @OETestID(1267)
299 def test_wrong_compressor(self):
300 """Test how wic breaks if wrong compressor is provided"""
301 self.assertEqual(2, runCmd("wic create wictestdisk "
302 "--image-name=core-image-minimal "
303 "-c wrong -o %s" % self.resultdir,
304 ignore_status=True).status)
305
306 @OETestID(1558)
307 def test_debug_short(self):
308 """Test -D option"""
309 self.assertEqual(0, runCmd("wic create wictestdisk "
310 "--image-name=core-image-minimal "
311 "-D -o %s" % self.resultdir).status)
312 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
313
314 def test_debug_long(self):
315 """Test --debug option"""
316 self.assertEqual(0, runCmd("wic create wictestdisk "
317 "--image-name=core-image-minimal "
318 "--debug -o %s" % self.resultdir).status)
319 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
320
321 @OETestID(1563)
322 def test_skip_build_check_short(self):
323 """Test -s option"""
324 self.assertEqual(0, runCmd("wic create wictestdisk "
325 "--image-name=core-image-minimal "
326 "-s -o %s" % self.resultdir).status)
327 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
328
329 def test_skip_build_check_long(self):
330 """Test --skip-build-check option"""
331 self.assertEqual(0, runCmd("wic create wictestdisk "
332 "--image-name=core-image-minimal "
333 "--skip-build-check "
334 "--outdir %s" % self.resultdir).status)
335 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
336
337 @OETestID(1564)
338 def test_build_rootfs_short(self):
339 """Test -f option"""
340 self.assertEqual(0, runCmd("wic create wictestdisk "
341 "--image-name=core-image-minimal "
342 "-f -o %s" % self.resultdir).status)
343 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
344
345 def test_build_rootfs_long(self):
346 """Test --build-rootfs option"""
347 self.assertEqual(0, runCmd("wic create wictestdisk "
348 "--image-name=core-image-minimal "
349 "--build-rootfs "
350 "--outdir %s" % self.resultdir).status)
351 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*.direct")))
352
353 @OETestID(1268)
354 @only_for_arch(['i586', 'i686', 'x86_64'])
355 def test_rootfs_indirect_recipes(self):
356 """Test usage of rootfs plugin with rootfs recipes"""
357 status = runCmd("wic create directdisk-multi-rootfs "
358 "--image-name=core-image-minimal "
359 "--rootfs rootfs1=core-image-minimal "
360 "--rootfs rootfs2=core-image-minimal "
361 "--outdir %s" % self.resultdir).status
362 self.assertEqual(0, status)
363 self.assertEqual(1, len(glob(self.resultdir + "directdisk-multi-rootfs*.direct")))
364
365 @OETestID(1269)
366 @only_for_arch(['i586', 'i686', 'x86_64'])
367 def test_rootfs_artifacts(self):
368 """Test usage of rootfs plugin with rootfs paths"""
369 bb_vars = get_bb_vars(['STAGING_DATADIR', 'RECIPE_SYSROOT_NATIVE'],
370 'wic-tools')
371 bb_vars.update(get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_ROOTFS'],
372 'core-image-minimal'))
373 bbvars = {key.lower(): value for key, value in bb_vars.items()}
374 bbvars['wks'] = "directdisk-multi-rootfs"
375 bbvars['resultdir'] = self.resultdir
376 status = runCmd("wic create %(wks)s "
377 "--bootimg-dir=%(staging_datadir)s "
378 "--kernel-dir=%(deploy_dir_image)s "
379 "--native-sysroot=%(recipe_sysroot_native)s "
380 "--rootfs-dir rootfs1=%(image_rootfs)s "
381 "--rootfs-dir rootfs2=%(image_rootfs)s "
382 "--outdir %(resultdir)s" % bbvars).status
383 self.assertEqual(0, status)
384 self.assertEqual(1, len(glob(self.resultdir + "%(wks)s-*.direct" % bbvars)))
385
386 def test_exclude_path(self):
387 """Test --exclude-path wks option."""
388
389 oldpath = os.environ['PATH']
390 os.environ['PATH'] = get_bb_var("PATH", "wic-tools")
391
392 try:
393 wks_file = 'temp.wks'
394 with open(wks_file, 'w') as wks:
395 rootfs_dir = get_bb_var('IMAGE_ROOTFS', 'core-image-minimal')
396 wks.write("""
397part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr
398part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr
399part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr"""
400 % (rootfs_dir, rootfs_dir))
401 self.assertEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
402 % (wks_file, self.resultdir)).status)
403
404 os.remove(wks_file)
405 wicout = glob(self.resultdir + "%s-*direct" % 'temp')
406 self.assertEqual(1, len(wicout))
407
408 wicimg = wicout[0]
409
410 # verify partition size with wic
411 res = runCmd("parted -m %s unit b p 2>/dev/null" % wicimg)
412 self.assertEqual(0, res.status)
413
414 # parse parted output which looks like this:
415 # BYT;\n
416 # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
417 # 1:0.00MiB:200MiB:200MiB:ext4::;\n
418 partlns = res.output.splitlines()[2:]
419
420 self.assertEqual(3, len(partlns))
421
422 for part in [1, 2, 3]:
423 part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
424 partln = partlns[part-1].split(":")
425 self.assertEqual(7, len(partln))
426 start = int(partln[1].rstrip("B")) / 512
427 length = int(partln[3].rstrip("B")) / 512
428 self.assertEqual(0, runCmd("dd if=%s of=%s skip=%d count=%d" %
429 (wicimg, part_file, start, length)).status)
430
431 def extract_files(debugfs_output):
432 """
433 extract file names from the output of debugfs -R 'ls -p',
434 which looks like this:
435
436 /2/040755/0/0/.//\n
437 /2/040755/0/0/..//\n
438 /11/040700/0/0/lost+found^M//\n
439 /12/040755/1002/1002/run//\n
440 /13/040755/1002/1002/sys//\n
441 /14/040755/1002/1002/bin//\n
442 /80/040755/1002/1002/var//\n
443 /92/040755/1002/1002/tmp//\n
444 """
445 # NOTE the occasional ^M in file names
446 return [line.split('/')[5].strip() for line in \
447 debugfs_output.strip().split('/\n')]
448
449 # Test partition 1, should contain the normal root directories, except
450 # /usr.
451 res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
452 os.path.join(self.resultdir, "selftest_img.part1"))
453 self.assertEqual(0, res.status)
454 files = extract_files(res.output)
455 self.assertIn("etc", files)
456 self.assertNotIn("usr", files)
457
458 # Partition 2, should contain common directories for /usr, not root
459 # directories.
460 res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
461 os.path.join(self.resultdir, "selftest_img.part2"))
462 self.assertEqual(0, res.status)
463 files = extract_files(res.output)
464 self.assertNotIn("etc", files)
465 self.assertNotIn("usr", files)
466 self.assertIn("share", files)
467
468 # Partition 3, should contain the same as partition 2, including the bin
469 # directory, but not the files inside it.
470 res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \
471 os.path.join(self.resultdir, "selftest_img.part3"))
472 self.assertEqual(0, res.status)
473 files = extract_files(res.output)
474 self.assertNotIn("etc", files)
475 self.assertNotIn("usr", files)
476 self.assertIn("share", files)
477 self.assertIn("bin", files)
478 res = runCmd("debugfs -R 'ls -p bin' %s 2>/dev/null" % \
479 os.path.join(self.resultdir, "selftest_img.part3"))
480 self.assertEqual(0, res.status)
481 files = extract_files(res.output)
482 self.assertIn(".", files)
483 self.assertIn("..", files)
484 self.assertEqual(2, len(files))
485
486 for part in [1, 2, 3]:
487 part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part)
488 os.remove(part_file)
489
490 finally:
491 os.environ['PATH'] = oldpath
492
493 def test_exclude_path_errors(self):
494 """Test --exclude-path wks option error handling."""
495 wks_file = 'temp.wks'
496
497 # Absolute argument.
498 with open(wks_file, 'w') as wks:
499 wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path /usr")
500 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
501 % (wks_file, self.resultdir), ignore_status=True).status)
502 os.remove(wks_file)
503
504 # Argument pointing to parent directory.
505 with open(wks_file, 'w') as wks:
506 wks.write("part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path ././..")
507 self.assertNotEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
508 % (wks_file, self.resultdir), ignore_status=True).status)
509 os.remove(wks_file)
510
511 @OETestID(1496)
512 def test_bmap_short(self):
513 """Test generation of .bmap file -m option"""
514 cmd = "wic create wictestdisk -e core-image-minimal -m -o %s" % self.resultdir
515 status = runCmd(cmd).status
516 self.assertEqual(0, status)
517 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
518 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct.bmap")))
519
520 def test_bmap_long(self):
521 """Test generation of .bmap file --bmap option"""
522 cmd = "wic create wictestdisk -e core-image-minimal --bmap -o %s" % self.resultdir
523 status = runCmd(cmd).status
524 self.assertEqual(0, status)
525 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
526 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct.bmap")))
527
528 def _get_image_env_path(self, image):
529 """Generate and obtain the path to <image>.env"""
530 if image not in self.wicenv_cache:
531 self.assertEqual(0, bitbake('%s -c do_rootfs_wicenv' % image).status)
532 bb_vars = get_bb_vars(['STAGING_DIR', 'MACHINE'], image)
533 stdir = bb_vars['STAGING_DIR']
534 machine = bb_vars['MACHINE']
535 self.wicenv_cache[image] = os.path.join(stdir, machine, 'imgdata')
536 return self.wicenv_cache[image]
537
538 @OETestID(1347)
539 def test_image_env(self):
540 """Test generation of <image>.env files."""
541 image = 'core-image-minimal'
542 imgdatadir = self._get_image_env_path(image)
543
544 bb_vars = get_bb_vars(['IMAGE_BASENAME', 'WICVARS'], image)
545 basename = bb_vars['IMAGE_BASENAME']
546 self.assertEqual(basename, image)
547 path = os.path.join(imgdatadir, basename) + '.env'
548 self.assertTrue(os.path.isfile(path))
549
550 wicvars = set(bb_vars['WICVARS'].split())
551 # filter out optional variables
552 wicvars = wicvars.difference(('DEPLOY_DIR_IMAGE', 'IMAGE_BOOT_FILES',
553 'INITRD', 'INITRD_LIVE', 'ISODIR'))
554 with open(path) as envfile:
555 content = dict(line.split("=", 1) for line in envfile)
556 # test if variables used by wic present in the .env file
557 for var in wicvars:
558 self.assertTrue(var in content, "%s is not in .env file" % var)
559 self.assertTrue(content[var])
560
561 @OETestID(1559)
562 def test_image_vars_dir_short(self):
563 """Test image vars directory selection -v option"""
564 image = 'core-image-minimal'
565 imgenvdir = self._get_image_env_path(image)
566
567 self.assertEqual(0, runCmd("wic create wictestdisk "
568 "--image-name=%s -v %s -o %s"
569 % (image, imgenvdir, self.resultdir)).status)
570 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
571
572 def test_image_vars_dir_long(self):
573 """Test image vars directory selection --vars option"""
574 image = 'core-image-minimal'
575 imgenvdir = self._get_image_env_path(image)
576 self.assertEqual(0, runCmd("wic create wictestdisk "
577 "--image-name=%s "
578 "--vars %s "
579 "--outdir %s"
580 % (image, imgenvdir, self.resultdir)).status)
581 self.assertEqual(1, len(glob(self.resultdir + "wictestdisk-*direct")))
582
583 @OETestID(1351)
584 @only_for_arch(['i586', 'i686', 'x86_64'])
585 def test_wic_image_type(self):
586 """Test building wic images by bitbake"""
587 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
588 'MACHINE_FEATURES_append = " efi"\n'
589 self.append_config(config)
590 self.assertEqual(0, bitbake('wic-image-minimal').status)
591 self.remove_config(config)
592
593 bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'MACHINE'])
594 deploy_dir = bb_vars['DEPLOY_DIR_IMAGE']
595 machine = bb_vars['MACHINE']
596 prefix = os.path.join(deploy_dir, 'wic-image-minimal-%s.' % machine)
597 # check if we have result image and manifests symlinks
598 # pointing to existing files
599 for suffix in ('wic', 'manifest'):
600 path = prefix + suffix
601 self.assertTrue(os.path.islink(path))
602 self.assertTrue(os.path.isfile(os.path.realpath(path)))
603
604 @OETestID(1422)
605 @only_for_arch(['i586', 'i686', 'x86_64'])
606 def test_qemu(self):
607 """Test wic-image-minimal under qemu"""
608 config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\
609 'MACHINE_FEATURES_append = " efi"\n'
610 self.append_config(config)
611 self.assertEqual(0, bitbake('wic-image-minimal').status)
612 self.remove_config(config)
613
614 with runqemu('wic-image-minimal', ssh=False) as qemu:
615 cmd = "mount |grep '^/dev/' | cut -f1,3 -d ' '"
616 status, output = qemu.run_serial(cmd)
617 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
618 self.assertEqual(output, '/dev/root /\r\n/dev/sda3 /mnt')
619
620 @only_for_arch(['i586', 'i686', 'x86_64'])
621 def test_qemu_efi(self):
622 """Test core-image-minimal efi image under qemu"""
623 config = 'IMAGE_FSTYPES = "wic"\nWKS_FILE = "mkefidisk.wks"\n'
624 self.append_config(config)
625 self.assertEqual(0, bitbake('core-image-minimal ovmf').status)
626 self.remove_config(config)
627
628 with runqemu('core-image-minimal', ssh=False,
629 runqemuparams='ovmf', image_fstype='wic') as qemu:
630 cmd = "grep sda. /proc/partitions |wc -l"
631 status, output = qemu.run_serial(cmd)
632 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
633 self.assertEqual(output, '3')
634
635 @staticmethod
636 def _make_fixed_size_wks(size):
637 """
638 Create a wks of an image with a single partition. Size of the partition is set
639 using --fixed-size flag. Returns a tuple: (path to wks file, wks image name)
640 """
641 with NamedTemporaryFile("w", suffix=".wks", delete=False) as tempf:
642 wkspath = tempf.name
643 tempf.write("part " \
644 "--source rootfs --ondisk hda --align 4 --fixed-size %d "
645 "--fstype=ext4\n" % size)
646 wksname = os.path.splitext(os.path.basename(wkspath))[0]
647
648 return wkspath, wksname
649
650 def test_fixed_size(self):
651 """
652 Test creation of a simple image with partition size controlled through
653 --fixed-size flag
654 """
655 wkspath, wksname = Wic._make_fixed_size_wks(200)
656
657 self.assertEqual(0, runCmd("wic create %s -e core-image-minimal -o %s" \
658 % (wkspath, self.resultdir)).status)
659 os.remove(wkspath)
660 wicout = glob(self.resultdir + "%s-*direct" % wksname)
661 self.assertEqual(1, len(wicout))
662
663 wicimg = wicout[0]
664
665 # verify partition size with wic
666 res = runCmd("parted -m %s unit mib p 2>/dev/null" % wicimg,
667 ignore_status=True,
668 native_sysroot=self.native_sysroot)
669 self.assertEqual(0, res.status)
670
671 # parse parted output which looks like this:
672 # BYT;\n
673 # /var/tmp/wic/build/tmpfwvjjkf_-201611101222-hda.direct:200MiB:file:512:512:msdos::;\n
674 # 1:0.00MiB:200MiB:200MiB:ext4::;\n
675 partlns = res.output.splitlines()[2:]
676
677 self.assertEqual(1, len(partlns))
678 self.assertEqual("1:0.00MiB:200MiB:200MiB:ext4::;", partlns[0])
679
680 def test_fixed_size_error(self):
681 """
682 Test creation of a simple image with partition size controlled through
683 --fixed-size flag. The size of partition is intentionally set to 1MiB
684 in order to trigger an error in wic.
685 """
686 wkspath, wksname = Wic._make_fixed_size_wks(1)
687
688 self.assertEqual(1, runCmd("wic create %s -e core-image-minimal -o %s" \
689 % (wkspath, self.resultdir), ignore_status=True).status)
690 os.remove(wkspath)
691 wicout = glob(self.resultdir + "%s-*direct" % wksname)
692 self.assertEqual(0, len(wicout))
693
694 @only_for_arch(['i586', 'i686', 'x86_64'])
695 def test_rawcopy_plugin_qemu(self):
696 """Test rawcopy plugin in qemu"""
697 # build ext4 and wic images
698 for fstype in ("ext4", "wic"):
699 config = 'IMAGE_FSTYPES = "%s"\nWKS_FILE = "test_rawcopy_plugin.wks.in"\n' % fstype
700 self.append_config(config)
701 self.assertEqual(0, bitbake('core-image-minimal').status)
702 self.remove_config(config)
703
704 with runqemu('core-image-minimal', ssh=False, image_fstype='wic') as qemu:
705 cmd = "grep sda. /proc/partitions |wc -l"
706 status, output = qemu.run_serial(cmd)
707 self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
708 self.assertEqual(output, '2')
709
710 def test_rawcopy_plugin(self):
711 """Test rawcopy plugin"""
712 img = 'core-image-minimal'
713 machine = get_bb_var('MACHINE', img)
714 with NamedTemporaryFile("w", suffix=".wks") as wks:
715 wks.writelines(['part /boot --active --source bootimg-pcbios\n',
716 'part / --source rawcopy --sourceparams="file=%s-%s.ext4" --use-uuid\n'\
717 % (img, machine),
718 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
719 wks.flush()
720 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
721 self.assertEqual(0, runCmd(cmd).status)
722 wksname = os.path.splitext(os.path.basename(wks.name))[0]
723 out = glob(self.resultdir + "%s-*direct" % wksname)
724 self.assertEqual(1, len(out))
725
726 def test_fs_types(self):
727 """Test filesystem types for empty and not empty partitions"""
728 img = 'core-image-minimal'
729 with NamedTemporaryFile("w", suffix=".wks") as wks:
730 wks.writelines(['part ext2 --fstype ext2 --source rootfs\n',
731 'part btrfs --fstype btrfs --source rootfs --size 40M\n',
732 'part squash --fstype squashfs --source rootfs\n',
733 'part swap --fstype swap --size 1M\n',
734 'part emptyvfat --fstype vfat --size 1M\n',
735 'part emptymsdos --fstype msdos --size 1M\n',
736 'part emptyext2 --fstype ext2 --size 1M\n',
737 'part emptybtrfs --fstype btrfs --size 100M\n'])
738 wks.flush()
739 cmd = "wic create %s -e %s -o %s" % (wks.name, img, self.resultdir)
740 self.assertEqual(0, runCmd(cmd).status)
741 wksname = os.path.splitext(os.path.basename(wks.name))[0]
742 out = glob(self.resultdir + "%s-*direct" % wksname)
743 self.assertEqual(1, len(out))
744
745 def test_kickstart_parser(self):
746 """Test wks parser options"""
747 with NamedTemporaryFile("w", suffix=".wks") as wks:
748 wks.writelines(['part / --fstype ext3 --source rootfs --system-id 0xFF '\
749 '--overhead-factor 1.2 --size 100k\n'])
750 wks.flush()
751 cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir)
752 self.assertEqual(0, runCmd(cmd).status)
753 wksname = os.path.splitext(os.path.basename(wks.name))[0]
754 out = glob(self.resultdir + "%s-*direct" % wksname)
755 self.assertEqual(1, len(out))
756
757 def test_image_bootpart_globbed(self):
758 """Test globbed sources with image-bootpart plugin"""
759 img = "core-image-minimal"
760 cmd = "wic create sdimage-bootpart -e %s -o %s" % (img, self.resultdir)
761 config = 'IMAGE_BOOT_FILES = "%s*"' % get_bb_var('KERNEL_IMAGETYPE', img)
762 self.append_config(config)
763 self.assertEqual(0, runCmd(cmd).status)
764 self.remove_config(config)
765 self.assertEqual(1, len(glob(self.resultdir + "sdimage-bootpart-*direct")))
766
767 def test_sparse_copy(self):
768 """Test sparse_copy with FIEMAP and SEEK_HOLE filemap APIs"""
769 libpath = os.path.join(get_bb_var('COREBASE'), 'scripts', 'lib', 'wic')
770 sys.path.insert(0, libpath)
771 from filemap import FilemapFiemap, FilemapSeek, sparse_copy, ErrorNotSupp
772 with NamedTemporaryFile("w", suffix=".wic-sparse") as sparse:
773 src_name = sparse.name
774 src_size = 1024 * 10
775 sparse.truncate(src_size)
776 # write one byte to the file
777 with open(src_name, 'r+b') as sfile:
778 sfile.seek(1024 * 4)
779 sfile.write(b'\x00')
780 dest = sparse.name + '.out'
781 # copy src file to dest using different filemap APIs
782 for api in (FilemapFiemap, FilemapSeek, None):
783 if os.path.exists(dest):
784 os.unlink(dest)
785 try:
786 sparse_copy(sparse.name, dest, api=api)
787 except ErrorNotSupp:
788 continue # skip unsupported API
789 dest_stat = os.stat(dest)
790 self.assertEqual(dest_stat.st_size, src_size)
791 # 8 blocks is 4K (physical sector size)
792 self.assertEqual(dest_stat.st_blocks, 8)
793 os.unlink(dest)