diff options
Diffstat (limited to 'meta/lib/oeqa/selftest/cases')
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 @@ | |||
1 | import os | ||
2 | import shutil | ||
3 | |||
4 | import oeqa.utils.ftools as ftools | ||
5 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer | ||
6 | from oeqa.selftest.cases.sstate import SStateBase | ||
7 | |||
8 | |||
9 | class 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 @@ | |||
1 | import os | ||
2 | import glob | ||
3 | from oeqa.utils.commands import bitbake, get_bb_vars | ||
4 | from oeqa.selftest.case import OESelftestTestCase | ||
5 | from oeqa.core.decorator.oeid import OETestID | ||
6 | |||
7 | class 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 @@ | |||
1 | import os | ||
2 | import re | ||
3 | |||
4 | import oeqa.utils.ftools as ftools | ||
5 | from oeqa.utils.commands import runCmd, get_bb_var | ||
6 | |||
7 | from oeqa.selftest.case import OESelftestTestCase | ||
8 | from oeqa.core.decorator.oeid import OETestID | ||
9 | |||
10 | class 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 @@ | |||
1 | import os | ||
2 | import re | ||
3 | |||
4 | import oeqa.utils.ftools as ftools | ||
5 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars | ||
6 | |||
7 | from oeqa.selftest.case import OESelftestTestCase | ||
8 | from oeqa.core.decorator.oeid import OETestID | ||
9 | |||
10 | class 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\" | ||
137 | SSTATE_DIR = \"${TOPDIR}/download-selftest\" | ||
138 | INHERIT_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 \ | ||
148 | doesn'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 \ | ||
151 | doesn'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\" | ||
158 | SSTATE_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\" | ||
221 | SSTATE_DIR = \"${TOPDIR}/download-selftest\" | ||
222 | INHERIT_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 @@ | |||
1 | import os | ||
2 | import re | ||
3 | import datetime | ||
4 | |||
5 | from oeqa.selftest.case import OESelftestTestCase | ||
6 | from oeqa.utils.commands import bitbake, get_bb_vars | ||
7 | |||
8 | |||
9 | class 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 @@ | |||
1 | import os | ||
2 | import re | ||
3 | import glob as g | ||
4 | import shutil | ||
5 | import tempfile | ||
6 | from oeqa.selftest.case import OESelftestTestCase | ||
7 | from oeqa.selftest.cases.buildhistory import BuildhistoryBase | ||
8 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars | ||
9 | import oeqa.utils.ftools as ftools | ||
10 | from oeqa.core.decorator.oeid import OETestID | ||
11 | |||
12 | class 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 | |||
55 | class 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 | |||
71 | class 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(""" | ||
109 | do_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 | |||
157 | class 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 | |||
171 | class 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 @@ | |||
1 | import os | ||
2 | |||
3 | from oeqa.selftest.case import OESelftestTestCase | ||
4 | from 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 | # | ||
19 | class 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" | ||
37 | IMAGE_FSTYPES = "container" | ||
38 | PACKAGE_CLASSES = "package_ipk" | ||
39 | IMAGE_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 @@ | |||
1 | import os | ||
2 | import re | ||
3 | import shutil | ||
4 | import tempfile | ||
5 | import glob | ||
6 | import fnmatch | ||
7 | |||
8 | import oeqa.utils.ftools as ftools | ||
9 | from oeqa.selftest.case import OESelftestTestCase | ||
10 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer | ||
11 | from oeqa.utils.commands import get_bb_vars, runqemu, get_test_layer | ||
12 | from oeqa.core.decorator.oeid import OETestID | ||
13 | |||
14 | class 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 | |||
117 | class 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 @@ | |||
1 | import tempfile | ||
2 | import shutil | ||
3 | import os | ||
4 | import glob | ||
5 | from oeqa.core.decorator.oeid import OETestID | ||
6 | from oeqa.selftest.case import OESelftestTestCase | ||
7 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars | ||
8 | |||
9 | class 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=""" | ||
62 | SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS" | ||
63 | SSTATE_MIRRORS = "file://.* file://%s/PATH" | ||
64 | CORE_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=""" | ||
88 | SDK_LOCAL_CONF_WHITELIST = "SSTATE_MIRRORS" | ||
89 | SSTATE_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 @@ | |||
1 | import os | ||
2 | |||
3 | from oeqa.selftest.case import OESelftestTestCase | ||
4 | from oeqa.utils.commands import bitbake | ||
5 | |||
6 | class 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 | ||
14 | PACKAGE_INSTALL = "" | ||
15 | DISTRO_EXTRA_RDEPENDS="" | ||
16 | |||
17 | LICENSE = "MIT" | ||
18 | IMAGE_FSTYPES = "testfstype" | ||
19 | |||
20 | IMAGE_TYPES_MASKED += "testfstype" | ||
21 | IMAGE_TYPEDEP_testfstype = "tar.bz2" | ||
22 | |||
23 | inherit 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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu | ||
3 | from oeqa.core.decorator.oeid import OETestID | ||
4 | from oeqa.utils.sshcontrol import SSHControl | ||
5 | import os | ||
6 | |||
7 | class 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 @@ | |||
1 | import os | ||
2 | |||
3 | from oeqa.selftest.case import OESelftestTestCase | ||
4 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var | ||
5 | import oeqa.utils.ftools as ftools | ||
6 | from oeqa.core.decorator.oeid import OETestID | ||
7 | |||
8 | class LayerAppendTests(OESelftestTestCase): | ||
9 | layerconf = """ | ||
10 | # We have a conf and classes directory, append to BBPATH | ||
11 | BBPATH .= ":${LAYERDIR}" | ||
12 | |||
13 | # We have a recipes directory, add to BBFILES | ||
14 | BBFILES += "${LAYERDIR}/recipes*/*.bb ${LAYERDIR}/recipes*/*.bbappend" | ||
15 | |||
16 | BBFILE_COLLECTIONS += "meta-layerINT" | ||
17 | BBFILE_PATTERN_meta-layerINT := "^${LAYERDIR}/" | ||
18 | BBFILE_PRIORITY_meta-layerINT = "6" | ||
19 | """ | ||
20 | recipe = """ | ||
21 | LICENSE="CLOSED" | ||
22 | INHIBIT_DEFAULT_DEPS = "1" | ||
23 | |||
24 | python do_build() { | ||
25 | bb.plain('Building ...') | ||
26 | } | ||
27 | addtask build | ||
28 | """ | ||
29 | append = """ | ||
30 | FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | ||
31 | |||
32 | SRC_URI_append = " file://appendtest.txt" | ||
33 | |||
34 | sysroot_stage_all_append() { | ||
35 | install -m 644 ${WORKDIR}/appendtest.txt ${SYSROOT_DESTDIR}/ | ||
36 | } | ||
37 | |||
38 | """ | ||
39 | |||
40 | append2 = """ | ||
41 | FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | ||
42 | |||
43 | SRC_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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | from oeqa.utils.commands import get_bb_var, get_bb_vars, bitbake, runCmd | ||
3 | import oe.path | ||
4 | import os | ||
5 | |||
6 | class 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 @@ | |||
1 | import os | ||
2 | import tempfile | ||
3 | |||
4 | from oeqa.selftest.case import OESelftestTestCase | ||
5 | from oeqa.utils.commands import bitbake | ||
6 | from oeqa.utils import CommandError | ||
7 | from oeqa.core.decorator.oeid import OETestID | ||
8 | |||
9 | class 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', """ | ||
23 | INHIBIT_DEFAULT_DEPS = "1" | ||
24 | LIC_FILES_CHKSUM = "file://%s;md5=d41d8cd98f00b204e9800998ecf8427e" | ||
25 | SRC_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 @@ | |||
1 | import os | ||
2 | |||
3 | from oeqa.selftest.case import OESelftestTestCase | ||
4 | from oeqa.utils.commands import get_bb_var, get_bb_vars, bitbake | ||
5 | from oeqa.core.decorator.oeid import OETestID | ||
6 | |||
7 | class 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 | |||
13 | class 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 @@ | |||
1 | import os | ||
2 | from oeqa.selftest.case import OESelftestTestCase | ||
3 | import tempfile | ||
4 | from git import Repo | ||
5 | from oeqa.utils.commands import get_bb_var | ||
6 | from oe.buildhistory_analysis import blob_to_dict, compare_dict_blobs | ||
7 | |||
8 | class 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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | import oe.qa | ||
3 | |||
4 | class 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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | import oe.license | ||
3 | |||
4 | class 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 | |||
12 | class 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 | |||
40 | class 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 | |||
61 | class 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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | import oe, oe.path | ||
3 | import tempfile | ||
4 | import os | ||
5 | import errno | ||
6 | import shutil | ||
7 | |||
8 | class 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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | from oe.maketype import create | ||
3 | |||
4 | class 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 | |||
34 | class 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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | from oe.utils import packages_filter_out_system, trim_version | ||
3 | |||
4 | class 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 | |||
34 | class 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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | from oeqa.selftest.cases.buildhistory import BuildhistoryBase | ||
3 | from oeqa.utils.commands import Command, runCmd, bitbake, get_bb_var, get_test_layer | ||
4 | from oeqa.core.decorator.oeid import OETestID | ||
5 | |||
6 | class 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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | from oeqa.utils.commands import bitbake, get_bb_vars | ||
3 | import subprocess, os | ||
4 | import oe.path | ||
5 | |||
6 | class 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 @@ | |||
1 | import os | ||
2 | import tempfile | ||
3 | import fnmatch | ||
4 | |||
5 | from oeqa.selftest.case import OESelftestTestCase | ||
6 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars | ||
7 | from oeqa.core.decorator.oeid import OETestID | ||
8 | |||
9 | class 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 @@ | |||
1 | import os | ||
2 | import re | ||
3 | import shutil | ||
4 | import datetime | ||
5 | |||
6 | import oeqa.utils.ftools as ftools | ||
7 | from oeqa.selftest.case import OESelftestTestCase | ||
8 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var | ||
9 | from oeqa.core.decorator.oeid import OETestID | ||
10 | from oeqa.utils.network import get_free_port | ||
11 | |||
12 | class 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 @@ | |||
1 | import os | ||
2 | import shutil | ||
3 | import tempfile | ||
4 | import urllib.parse | ||
5 | |||
6 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var | ||
7 | from oeqa.utils.commands import get_bb_vars, create_temp_layer | ||
8 | from oeqa.core.decorator.oeid import OETestID | ||
9 | from oeqa.selftest.cases import devtool | ||
10 | |||
11 | templayerdir = None | ||
12 | |||
13 | def 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 | |||
20 | def tearDownModule(): | ||
21 | runCmd('bitbake-layers remove-layer %s' % templayerdir, ignore_status=True) | ||
22 | runCmd('rm -rf %s' % templayerdir) | ||
23 | |||
24 | |||
25 | class 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 | |||
68 | class 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 | |||
529 | class 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 | |||
621 | class 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 | |||
5 | import re | ||
6 | import logging | ||
7 | |||
8 | from oeqa.selftest.case import OESelftestTestCase | ||
9 | from oeqa.utils.commands import bitbake, runqemu, get_bb_var | ||
10 | from oeqa.core.decorator.oeid import OETestID | ||
11 | |||
12 | class 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 | """ | ||
31 | MACHINE = "%s" | ||
32 | IMAGE_FSTYPES = "%s" | ||
33 | # 10 means 1 second | ||
34 | SYSLINUX_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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu | ||
3 | from oeqa.core.decorator.oeid import OETestID | ||
4 | import os | ||
5 | import re | ||
6 | |||
7 | class 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 | |||
105 | class 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 | |||
133 | class 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 \ | ||
147 | postinst-delayed-a \ | ||
148 | postinst-delayed-b \ | ||
149 | postinst-delayed-d \ | ||
150 | postinst-delayed-p \ | ||
151 | postinst-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 @@ | |||
1 | from oeqa.selftest.case import OESelftestTestCase | ||
2 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars | ||
3 | import os | ||
4 | import glob | ||
5 | import re | ||
6 | import shutil | ||
7 | import tempfile | ||
8 | from oeqa.core.decorator.oeid import OETestID | ||
9 | from oeqa.utils.ftools import write_file | ||
10 | |||
11 | |||
12 | class 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 | |||
139 | class 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 @@ | |||
1 | import datetime | ||
2 | import unittest | ||
3 | import os | ||
4 | import re | ||
5 | import shutil | ||
6 | |||
7 | import oeqa.utils.ftools as ftools | ||
8 | from oeqa.selftest.case import OESelftestTestCase | ||
9 | from oeqa.utils.commands import runCmd, bitbake, get_bb_vars, get_test_layer | ||
10 | |||
11 | |||
12 | class 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 @@ | |||
1 | import os | ||
2 | import shutil | ||
3 | import glob | ||
4 | import subprocess | ||
5 | |||
6 | from oeqa.selftest.case import OESelftestTestCase | ||
7 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_test_layer | ||
8 | from oeqa.selftest.cases.sstate import SStateBase | ||
9 | from oeqa.core.decorator.oeid import OETestID | ||
10 | |||
11 | class 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(""" | ||
225 | MACHINE = "qemux86" | ||
226 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash" | ||
227 | BUILD_ARCH = "x86_64" | ||
228 | BUILD_OS = "linux" | ||
229 | SDKMACHINE = "x86_64" | ||
230 | PACKAGE_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(""" | ||
235 | MACHINE = "qemux86" | ||
236 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash2" | ||
237 | BUILD_ARCH = "i686" | ||
238 | BUILD_OS = "linux" | ||
239 | SDKMACHINE = "i686" | ||
240 | PACKAGE_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(""" | ||
271 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" | ||
272 | NATIVELSBSTRING = \"DistroA\" | ||
273 | """) | ||
274 | self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash") | ||
275 | bitbake("core-image-sato -S none") | ||
276 | self.write_config(""" | ||
277 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" | ||
278 | NATIVELSBSTRING = \"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 = """ | ||
304 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" | ||
305 | MACHINE = \"qemux86-64\" | ||
306 | """ | ||
307 | configB = """ | ||
308 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" | ||
309 | MACHINE = \"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 = """ | ||
322 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" | ||
323 | MACHINE = \"qemux86-64\" | ||
324 | require conf/multilib.conf | ||
325 | MULTILIBS = \"multilib:lib32\" | ||
326 | DEFAULTTUNE_virtclass-multilib-lib32 = \"x86\" | ||
327 | """ | ||
328 | configB = """ | ||
329 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" | ||
330 | MACHINE = \"qemuarm\" | ||
331 | require conf/multilib.conf | ||
332 | MULTILIBS = \"\" | ||
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(""" | ||
377 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" | ||
378 | MACHINE = \"qemux86\" | ||
379 | require conf/multilib.conf | ||
380 | MULTILIBS = "multilib:lib32" | ||
381 | DEFAULTTUNE_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(""" | ||
386 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" | ||
387 | MACHINE = \"qemux86copy\" | ||
388 | require conf/multilib.conf | ||
389 | MULTILIBS = "multilib:lib32" | ||
390 | DEFAULTTUNE_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(""" | ||
420 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash" | ||
421 | BB_NUMBER_THREADS = "1" | ||
422 | PARALLEL_MAKE = "-j 1" | ||
423 | DL_DIR = "${TOPDIR}/download1" | ||
424 | TIME = "111111" | ||
425 | DATE = "20161111" | ||
426 | INHERIT_remove = "buildstats-summary buildhistory uninative" | ||
427 | http_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(""" | ||
433 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash2" | ||
434 | BB_NUMBER_THREADS = "2" | ||
435 | PARALLEL_MAKE = "-j 2" | ||
436 | DL_DIR = "${TOPDIR}/download2" | ||
437 | TIME = "222222" | ||
438 | DATE = "20161212" | ||
439 | # Always remove uninative as we're changing proxies | ||
440 | INHERIT_remove = "uninative" | ||
441 | INHERIT += "buildstats-summary buildhistory" | ||
442 | http_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 @@ | |||
1 | import os | ||
2 | import re | ||
3 | import bb.tinfoil | ||
4 | |||
5 | from oeqa.selftest.case import OESelftestTestCase | ||
6 | from oeqa.utils.commands import runCmd | ||
7 | from oeqa.core.decorator.oeid import OETestID | ||
8 | |||
9 | class 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 | |||
26 | import os | ||
27 | import sys | ||
28 | import unittest | ||
29 | |||
30 | from glob import glob | ||
31 | from shutil import rmtree | ||
32 | from functools import wraps, lru_cache | ||
33 | from tempfile import NamedTemporaryFile | ||
34 | |||
35 | from oeqa.selftest.case import OESelftestTestCase | ||
36 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu | ||
37 | from oeqa.core.decorator.oeid import OETestID | ||
38 | |||
39 | |||
40 | @lru_cache(maxsize=32) | ||
41 | def 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 | |||
46 | def 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 | |||
64 | class 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(""" | ||
397 | part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr | ||
398 | part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr | ||
399 | part /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) | ||