diff options
Diffstat (limited to 'meta/lib/oeqa/selftest')
45 files changed, 3808 insertions, 1373 deletions
diff --git a/meta/lib/oeqa/selftest/cases/archiver.py b/meta/lib/oeqa/selftest/cases/archiver.py index 3cb888c506..612ec675a7 100644 --- a/meta/lib/oeqa/selftest/cases/archiver.py +++ b/meta/lib/oeqa/selftest/cases/archiver.py | |||
@@ -190,28 +190,28 @@ class Archiver(OESelftestTestCase): | |||
190 | Test that the archiver works with `ARCHIVER_MODE[src] = "original"`. | 190 | Test that the archiver works with `ARCHIVER_MODE[src] = "original"`. |
191 | """ | 191 | """ |
192 | 192 | ||
193 | self._test_archiver_mode('original', 'ed-1.14.1.tar.lz') | 193 | self._test_archiver_mode('original', 'ed-1.21.1.tar.lz') |
194 | 194 | ||
195 | def test_archiver_mode_patched(self): | 195 | def test_archiver_mode_patched(self): |
196 | """ | 196 | """ |
197 | Test that the archiver works with `ARCHIVER_MODE[src] = "patched"`. | 197 | Test that the archiver works with `ARCHIVER_MODE[src] = "patched"`. |
198 | """ | 198 | """ |
199 | 199 | ||
200 | self._test_archiver_mode('patched', 'selftest-ed-native-1.14.1-r0-patched.tar.xz') | 200 | self._test_archiver_mode('patched', 'selftest-ed-native-1.21.1-r0-patched.tar.xz') |
201 | 201 | ||
202 | def test_archiver_mode_configured(self): | 202 | def test_archiver_mode_configured(self): |
203 | """ | 203 | """ |
204 | Test that the archiver works with `ARCHIVER_MODE[src] = "configured"`. | 204 | Test that the archiver works with `ARCHIVER_MODE[src] = "configured"`. |
205 | """ | 205 | """ |
206 | 206 | ||
207 | self._test_archiver_mode('configured', 'selftest-ed-native-1.14.1-r0-configured.tar.xz') | 207 | self._test_archiver_mode('configured', 'selftest-ed-native-1.21.1-r0-configured.tar.xz') |
208 | 208 | ||
209 | def test_archiver_mode_recipe(self): | 209 | def test_archiver_mode_recipe(self): |
210 | """ | 210 | """ |
211 | Test that the archiver works with `ARCHIVER_MODE[recipe] = "1"`. | 211 | Test that the archiver works with `ARCHIVER_MODE[recipe] = "1"`. |
212 | """ | 212 | """ |
213 | 213 | ||
214 | self._test_archiver_mode('patched', 'selftest-ed-native-1.14.1-r0-recipe.tar.xz', | 214 | self._test_archiver_mode('patched', 'selftest-ed-native-1.21.1-r0-recipe.tar.xz', |
215 | 'ARCHIVER_MODE[recipe] = "1"\n') | 215 | 'ARCHIVER_MODE[recipe] = "1"\n') |
216 | 216 | ||
217 | def test_archiver_mode_diff(self): | 217 | def test_archiver_mode_diff(self): |
@@ -220,7 +220,7 @@ class Archiver(OESelftestTestCase): | |||
220 | Exclusions controlled by `ARCHIVER_MODE[diff-exclude]` are not yet tested. | 220 | Exclusions controlled by `ARCHIVER_MODE[diff-exclude]` are not yet tested. |
221 | """ | 221 | """ |
222 | 222 | ||
223 | self._test_archiver_mode('patched', 'selftest-ed-native-1.14.1-r0-diff.gz', | 223 | self._test_archiver_mode('patched', 'selftest-ed-native-1.21.1-r0-diff.gz', |
224 | 'ARCHIVER_MODE[diff] = "1"\n') | 224 | 'ARCHIVER_MODE[diff] = "1"\n') |
225 | 225 | ||
226 | def test_archiver_mode_dumpdata(self): | 226 | def test_archiver_mode_dumpdata(self): |
@@ -228,7 +228,7 @@ class Archiver(OESelftestTestCase): | |||
228 | Test that the archiver works with `ARCHIVER_MODE[dumpdata] = "1"`. | 228 | Test that the archiver works with `ARCHIVER_MODE[dumpdata] = "1"`. |
229 | """ | 229 | """ |
230 | 230 | ||
231 | self._test_archiver_mode('patched', 'selftest-ed-native-1.14.1-r0-showdata.dump', | 231 | self._test_archiver_mode('patched', 'selftest-ed-native-1.21.1-r0-showdata.dump', |
232 | 'ARCHIVER_MODE[dumpdata] = "1"\n') | 232 | 'ARCHIVER_MODE[dumpdata] = "1"\n') |
233 | 233 | ||
234 | def test_archiver_mode_mirror(self): | 234 | def test_archiver_mode_mirror(self): |
@@ -236,7 +236,7 @@ class Archiver(OESelftestTestCase): | |||
236 | Test that the archiver works with `ARCHIVER_MODE[src] = "mirror"`. | 236 | Test that the archiver works with `ARCHIVER_MODE[src] = "mirror"`. |
237 | """ | 237 | """ |
238 | 238 | ||
239 | self._test_archiver_mode('mirror', 'ed-1.14.1.tar.lz', | 239 | self._test_archiver_mode('mirror', 'ed-1.21.1.tar.lz', |
240 | 'BB_GENERATE_MIRROR_TARBALLS = "1"\n') | 240 | 'BB_GENERATE_MIRROR_TARBALLS = "1"\n') |
241 | 241 | ||
242 | def test_archiver_mode_mirror_excludes(self): | 242 | def test_archiver_mode_mirror_excludes(self): |
@@ -247,7 +247,7 @@ class Archiver(OESelftestTestCase): | |||
247 | """ | 247 | """ |
248 | 248 | ||
249 | target='selftest-ed' | 249 | target='selftest-ed' |
250 | target_file_name = 'ed-1.14.1.tar.lz' | 250 | target_file_name = 'ed-1.21.1.tar.lz' |
251 | 251 | ||
252 | features = 'INHERIT += "archiver"\n' | 252 | features = 'INHERIT += "archiver"\n' |
253 | features += 'ARCHIVER_MODE[src] = "mirror"\n' | 253 | features += 'ARCHIVER_MODE[src] = "mirror"\n' |
@@ -285,7 +285,7 @@ class Archiver(OESelftestTestCase): | |||
285 | bitbake('-c deploy_archives %s' % (target)) | 285 | bitbake('-c deploy_archives %s' % (target)) |
286 | 286 | ||
287 | bb_vars = get_bb_vars(['DEPLOY_DIR_SRC']) | 287 | bb_vars = get_bb_vars(['DEPLOY_DIR_SRC']) |
288 | for target_file_name in ['ed-1.14.1.tar.lz', 'hello.c']: | 288 | for target_file_name in ['ed-1.21.1.tar.lz', 'hello.c']: |
289 | glob_str = os.path.join(bb_vars['DEPLOY_DIR_SRC'], 'mirror', target_file_name) | 289 | glob_str = os.path.join(bb_vars['DEPLOY_DIR_SRC'], 'mirror', target_file_name) |
290 | glob_result = glob.glob(glob_str) | 290 | glob_result = glob.glob(glob_str) |
291 | self.assertTrue(glob_result, 'Missing archive file %s' % (target_file_name)) | 291 | self.assertTrue(glob_result, 'Missing archive file %s' % (target_file_name)) |
diff --git a/meta/lib/oeqa/selftest/cases/barebox.py b/meta/lib/oeqa/selftest/cases/barebox.py new file mode 100644 index 0000000000..3f8f232432 --- /dev/null +++ b/meta/lib/oeqa/selftest/cases/barebox.py | |||
@@ -0,0 +1,44 @@ | |||
1 | # Qemu-based barebox bootloader integration testing | ||
2 | # | ||
3 | # Copyright OpenEmbedded Contributors | ||
4 | # | ||
5 | # SPDX-License-Identifier: MIT | ||
6 | # | ||
7 | |||
8 | from oeqa.selftest.case import OESelftestTestCase | ||
9 | from oeqa.utils.commands import bitbake, runqemu | ||
10 | from oeqa.core.decorator.data import skipIfNotArch | ||
11 | from oeqa.core.decorator import OETestTag | ||
12 | |||
13 | barebox_boot_patterns = { | ||
14 | 'search_reached_prompt': r"stop autoboot", | ||
15 | 'search_login_succeeded': r"barebox@[^:]+:[^ ]+ ", | ||
16 | 'search_cmd_finished': r"barebox@[a-zA-Z0-9\-\s]+:/" | ||
17 | } | ||
18 | |||
19 | |||
20 | class BareboxTest(OESelftestTestCase): | ||
21 | |||
22 | @skipIfNotArch(['arm', 'aarch64']) | ||
23 | @OETestTag("runqemu") | ||
24 | def test_boot_barebox(self): | ||
25 | """ | ||
26 | Tests building barebox and booting it with QEMU | ||
27 | """ | ||
28 | |||
29 | self.write_config(""" | ||
30 | QB_DEFAULT_KERNEL = "barebox-dt-2nd.img" | ||
31 | PREFERRED_PROVIDER_virtual/bootloader = "barebox" | ||
32 | QEMU_USE_KVM = "False" | ||
33 | """) | ||
34 | |||
35 | bitbake("virtual/bootloader core-image-minimal") | ||
36 | |||
37 | with runqemu('core-image-minimal', ssh=False, runqemuparams='nographic', | ||
38 | boot_patterns=barebox_boot_patterns) as qemu: | ||
39 | |||
40 | # test if barebox console works | ||
41 | cmd = "version" | ||
42 | status, output = qemu.run_serial(cmd) | ||
43 | self.assertEqual(status, 1, msg=output) | ||
44 | self.assertTrue("barebox" in output, msg=output) | ||
diff --git a/meta/lib/oeqa/selftest/cases/bbclasses.py b/meta/lib/oeqa/selftest/cases/bbclasses.py new file mode 100644 index 0000000000..10545ebe65 --- /dev/null +++ b/meta/lib/oeqa/selftest/cases/bbclasses.py | |||
@@ -0,0 +1,106 @@ | |||
1 | # | ||
2 | # Copyright OpenEmbedded Contributors | ||
3 | # | ||
4 | # SPDX-License-Identifier: MIT | ||
5 | # | ||
6 | |||
7 | from oeqa.selftest.case import OESelftestTestCase | ||
8 | from oeqa.utils.commands import get_bb_vars, bitbake | ||
9 | |||
10 | class Systemd(OESelftestTestCase): | ||
11 | """ | ||
12 | Tests related to the systemd bbclass. | ||
13 | """ | ||
14 | |||
15 | def getVars(self, recipe): | ||
16 | self.bb_vars = get_bb_vars( | ||
17 | [ | ||
18 | 'BPN', | ||
19 | 'D', | ||
20 | 'INIT_D_DIR', | ||
21 | 'prefix', | ||
22 | 'systemd_system_unitdir', | ||
23 | 'sysconfdir', | ||
24 | ], | ||
25 | recipe, | ||
26 | ) | ||
27 | |||
28 | def fileExists(self, filename): | ||
29 | self.assertExists(filename.format(**self.bb_vars)) | ||
30 | |||
31 | def fileNotExists(self, filename): | ||
32 | self.assertNotExists(filename.format(**self.bb_vars)) | ||
33 | |||
34 | def test_systemd_in_distro(self): | ||
35 | """ | ||
36 | Summary: Verify that no sysvinit files are installed when the | ||
37 | systemd distro feature is enabled, but sysvinit is not. | ||
38 | Expected: Systemd service file exists, but /etc does not. | ||
39 | Product: OE-Core | ||
40 | Author: Peter Kjellerstedt <peter.kjellerstedt@axis.com> | ||
41 | """ | ||
42 | |||
43 | self.write_config(""" | ||
44 | DISTRO_FEATURES:append = " systemd usrmerge" | ||
45 | DISTRO_FEATURES:remove = "sysvinit" | ||
46 | VIRTUAL-RUNTIME_init_manager = "systemd" | ||
47 | """) | ||
48 | bitbake("systemd-only systemd-and-sysvinit -c install") | ||
49 | |||
50 | self.getVars("systemd-only") | ||
51 | self.fileExists("{D}{systemd_system_unitdir}/{BPN}.service") | ||
52 | |||
53 | self.getVars("systemd-and-sysvinit") | ||
54 | self.fileExists("{D}{systemd_system_unitdir}/{BPN}.service") | ||
55 | self.fileNotExists("{D}{sysconfdir}") | ||
56 | |||
57 | def test_systemd_and_sysvinit_in_distro(self): | ||
58 | """ | ||
59 | Summary: Verify that both systemd and sysvinit files are installed | ||
60 | when both the systemd and sysvinit distro features are | ||
61 | enabled. | ||
62 | Expected: Systemd service file and sysvinit initscript exist. | ||
63 | Product: OE-Core | ||
64 | Author: Peter Kjellerstedt <peter.kjellerstedt@axis.com> | ||
65 | """ | ||
66 | |||
67 | self.write_config(""" | ||
68 | DISTRO_FEATURES:append = " systemd sysvinit usrmerge" | ||
69 | VIRTUAL-RUNTIME_init_manager = "systemd" | ||
70 | """) | ||
71 | bitbake("systemd-only systemd-and-sysvinit -c install") | ||
72 | |||
73 | self.getVars("systemd-only") | ||
74 | self.fileExists("{D}{systemd_system_unitdir}/{BPN}.service") | ||
75 | |||
76 | self.getVars("systemd-and-sysvinit") | ||
77 | self.fileExists("{D}{systemd_system_unitdir}/{BPN}.service") | ||
78 | self.fileExists("{D}{INIT_D_DIR}/{BPN}") | ||
79 | |||
80 | def test_sysvinit_in_distro(self): | ||
81 | """ | ||
82 | Summary: Verify that no systemd service files are installed when the | ||
83 | sysvinit distro feature is enabled, but systemd is not. | ||
84 | Expected: The systemd service file does not exist, nor does /usr. | ||
85 | The sysvinit initscript exists. | ||
86 | Product: OE-Core | ||
87 | Author: Peter Kjellerstedt <peter.kjellerstedt@axis.com> | ||
88 | """ | ||
89 | |||
90 | self.write_config(""" | ||
91 | DISTRO_FEATURES:remove = "systemd" | ||
92 | DISTRO_FEATURES:append = " sysvinit usrmerge" | ||
93 | VIRTUAL-RUNTIME_init_manager = "sysvinit" | ||
94 | """) | ||
95 | bitbake("systemd-only systemd-and-sysvinit -c install") | ||
96 | |||
97 | self.getVars("systemd-only") | ||
98 | self.fileNotExists("{D}{systemd_system_unitdir}/{BPN}.service") | ||
99 | self.fileNotExists("{D}{prefix}") | ||
100 | self.fileNotExists("{D}{sysconfdir}") | ||
101 | self.fileExists("{D}") | ||
102 | |||
103 | self.getVars("systemd-and-sysvinit") | ||
104 | self.fileNotExists("{D}{systemd_system_unitdir}/{BPN}.service") | ||
105 | self.fileNotExists("{D}{prefix}") | ||
106 | self.fileExists("{D}{INIT_D_DIR}/{BPN}") | ||
diff --git a/meta/lib/oeqa/selftest/cases/bblayers.py b/meta/lib/oeqa/selftest/cases/bblayers.py index 695d17377d..68b0377720 100644 --- a/meta/lib/oeqa/selftest/cases/bblayers.py +++ b/meta/lib/oeqa/selftest/cases/bblayers.py | |||
@@ -240,3 +240,34 @@ class BitbakeLayers(OESelftestTestCase): | |||
240 | self.assertEqual(first_desc_2, '', "Describe not cleared: '{}'".format(first_desc_2)) | 240 | self.assertEqual(first_desc_2, '', "Describe not cleared: '{}'".format(first_desc_2)) |
241 | self.assertEqual(second_rev_2, second_rev_1, "Revision should not be updated: '{}'".format(second_rev_2)) | 241 | self.assertEqual(second_rev_2, second_rev_1, "Revision should not be updated: '{}'".format(second_rev_2)) |
242 | self.assertEqual(second_desc_2, second_desc_1, "Describe should not be updated: '{}'".format(second_desc_2)) | 242 | self.assertEqual(second_desc_2, second_desc_1, "Describe should not be updated: '{}'".format(second_desc_2)) |
243 | |||
244 | class BitbakeConfigBuild(OESelftestTestCase): | ||
245 | def test_enable_disable_fragments(self): | ||
246 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), None) | ||
247 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), None) | ||
248 | |||
249 | runCmd('bitbake-config-build enable-fragment selftest/test-fragment') | ||
250 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), 'somevalue') | ||
251 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), None) | ||
252 | |||
253 | runCmd('bitbake-config-build enable-fragment selftest/more-fragments-here/test-another-fragment') | ||
254 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), 'somevalue') | ||
255 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), 'someothervalue') | ||
256 | |||
257 | fragment_metadata_command = "bitbake-getvar -f {} --value {}" | ||
258 | result = runCmd(fragment_metadata_command.format("selftest/test-fragment", "BB_CONF_FRAGMENT_SUMMARY")) | ||
259 | self.assertIn("This is a configuration fragment intended for testing in oe-selftest context", result.output) | ||
260 | result = runCmd(fragment_metadata_command.format("selftest/test-fragment", "BB_CONF_FRAGMENT_DESCRIPTION")) | ||
261 | self.assertIn("It defines a variable that can be checked inside the test.", result.output) | ||
262 | result = runCmd(fragment_metadata_command.format("selftest/more-fragments-here/test-another-fragment", "BB_CONF_FRAGMENT_SUMMARY")) | ||
263 | self.assertIn("This is a second configuration fragment intended for testing in oe-selftest context", result.output) | ||
264 | result = runCmd(fragment_metadata_command.format("selftest/more-fragments-here/test-another-fragment", "BB_CONF_FRAGMENT_DESCRIPTION")) | ||
265 | self.assertIn("It defines another variable that can be checked inside the test.", result.output) | ||
266 | |||
267 | runCmd('bitbake-config-build disable-fragment selftest/test-fragment') | ||
268 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), None) | ||
269 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), 'someothervalue') | ||
270 | |||
271 | runCmd('bitbake-config-build disable-fragment selftest/more-fragments-here/test-another-fragment') | ||
272 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_VARIABLE'), None) | ||
273 | self.assertEqual(get_bb_var('SELFTEST_FRAGMENT_ANOTHER_VARIABLE'), None) | ||
diff --git a/meta/lib/oeqa/selftest/cases/bbtests.py b/meta/lib/oeqa/selftest/cases/bbtests.py index 98e9f81661..51934ef70d 100644 --- a/meta/lib/oeqa/selftest/cases/bbtests.py +++ b/meta/lib/oeqa/selftest/cases/bbtests.py | |||
@@ -233,6 +233,7 @@ INHERIT:remove = \"report-error\" | |||
233 | 233 | ||
234 | def test_non_gplv3(self): | 234 | def test_non_gplv3(self): |
235 | self.write_config('''INCOMPATIBLE_LICENSE = "GPL-3.0-or-later" | 235 | self.write_config('''INCOMPATIBLE_LICENSE = "GPL-3.0-or-later" |
236 | OVERRIDES .= ":gplv3test" | ||
236 | require conf/distro/include/no-gplv3.inc | 237 | require conf/distro/include/no-gplv3.inc |
237 | ''') | 238 | ''') |
238 | result = bitbake('selftest-ed', ignore_status=True) | 239 | result = bitbake('selftest-ed', ignore_status=True) |
@@ -241,7 +242,7 @@ require conf/distro/include/no-gplv3.inc | |||
241 | arch = get_bb_var('SSTATE_PKGARCH') | 242 | arch = get_bb_var('SSTATE_PKGARCH') |
242 | filename = os.path.join(lic_dir, arch, 'selftest-ed', 'generic_GPL-3.0-or-later') | 243 | filename = os.path.join(lic_dir, arch, 'selftest-ed', 'generic_GPL-3.0-or-later') |
243 | self.assertFalse(os.path.isfile(filename), msg="License file %s exists and shouldn't" % filename) | 244 | self.assertFalse(os.path.isfile(filename), msg="License file %s exists and shouldn't" % filename) |
244 | filename = os.path.join(lic_dir, arch, 'selftest-ed', 'generic_GPL-2.0-or-later') | 245 | filename = os.path.join(lic_dir, arch, 'selftest-ed', 'generic_GPL-2.0-only') |
245 | self.assertTrue(os.path.isfile(filename), msg="License file %s doesn't exist" % filename) | 246 | self.assertTrue(os.path.isfile(filename), msg="License file %s doesn't exist" % filename) |
246 | 247 | ||
247 | def test_setscene_only(self): | 248 | def test_setscene_only(self): |
@@ -375,3 +376,21 @@ require conf/distro/include/no-gplv3.inc | |||
375 | self.assertGreater(result.status, 0, "Build should have failed if ${ is in the path") | 376 | self.assertGreater(result.status, 0, "Build should have failed if ${ is in the path") |
376 | self.assertTrue(re.search("ERROR: Directory name /.* contains unexpanded bitbake variable. This may cause build failures and WORKDIR polution", | 377 | self.assertTrue(re.search("ERROR: Directory name /.* contains unexpanded bitbake variable. This may cause build failures and WORKDIR polution", |
377 | result.output), msg = "mkdirhier with unexpanded variable should have failed: %s" % result.output) | 378 | result.output), msg = "mkdirhier with unexpanded variable should have failed: %s" % result.output) |
379 | |||
380 | def test_bb_env_bb_getvar_equality(self): | ||
381 | """ Test if "bitbake -e" output is identical to "bitbake-getvar" output for a variable set from an anonymous function | ||
382 | """ | ||
383 | self.write_config('''INHERIT += "test_anon_func" | ||
384 | TEST_SET_FROM_ANON_FUNC ?= ""''') | ||
385 | |||
386 | result_bb_e = runCmd('bitbake -e') | ||
387 | bb_e_var_match = re.search('^TEST_SET_FROM_ANON_FUNC="(?P<value>.*)"$', result_bb_e.output, re.MULTILINE) | ||
388 | self.assertTrue(bb_e_var_match, msg = "Can't find TEST_SET_FROM_ANON_FUNC value in \"bitbake -e\" output") | ||
389 | bb_e_var_value = bb_e_var_match.group("value") | ||
390 | |||
391 | result_bb_getvar = runCmd('bitbake-getvar TEST_SET_FROM_ANON_FUNC --value') | ||
392 | bb_getvar_var_value = result_bb_getvar.output.strip() | ||
393 | self.assertEqual(bb_e_var_value, bb_getvar_var_value, | ||
394 | msg='''"bitbake -e" output differs from bitbake-getvar output for TEST_SET_FROM_ANON_FUNC (set from anonymous function) | ||
395 | bitbake -e: "%s" | ||
396 | bitbake-getvar: "%s"''' % (bb_e_var_value, bb_getvar_var_value)) | ||
diff --git a/meta/lib/oeqa/selftest/cases/binutils.py b/meta/lib/oeqa/selftest/cases/binutils.py index 1688eabe4e..5ff263d342 100644 --- a/meta/lib/oeqa/selftest/cases/binutils.py +++ b/meta/lib/oeqa/selftest/cases/binutils.py | |||
@@ -33,7 +33,7 @@ class BinutilsCrossSelfTest(OESelftestTestCase, OEPTestResultTestCase): | |||
33 | features.append('CHECK_TARGETS = "{0}"'.format(suite)) | 33 | features.append('CHECK_TARGETS = "{0}"'.format(suite)) |
34 | self.write_config("\n".join(features)) | 34 | self.write_config("\n".join(features)) |
35 | 35 | ||
36 | recipe = "binutils-cross-testsuite" | 36 | recipe = "binutils-testsuite" |
37 | bb_vars = get_bb_vars(["B", "TARGET_SYS", "T"], recipe) | 37 | bb_vars = get_bb_vars(["B", "TARGET_SYS", "T"], recipe) |
38 | builddir, target_sys, tdir = bb_vars["B"], bb_vars["TARGET_SYS"], bb_vars["T"] | 38 | builddir, target_sys, tdir = bb_vars["B"], bb_vars["TARGET_SYS"], bb_vars["T"] |
39 | 39 | ||
diff --git a/meta/lib/oeqa/selftest/cases/buildhistory.py b/meta/lib/oeqa/selftest/cases/buildhistory.py index 2d55994916..511c666554 100644 --- a/meta/lib/oeqa/selftest/cases/buildhistory.py +++ b/meta/lib/oeqa/selftest/cases/buildhistory.py | |||
@@ -9,10 +9,10 @@ import re | |||
9 | import datetime | 9 | import datetime |
10 | 10 | ||
11 | from oeqa.selftest.case import OESelftestTestCase | 11 | from oeqa.selftest.case import OESelftestTestCase |
12 | from oeqa.utils.commands import bitbake, get_bb_vars | 12 | from oeqa.utils.commands import bitbake, get_bb_vars, get_bb_var, runCmd |
13 | 13 | ||
14 | 14 | ||
15 | class BuildhistoryBase(OESelftestTestCase): | 15 | class BuildhistoryTests(OESelftestTestCase): |
16 | 16 | ||
17 | def config_buildhistory(self, tmp_bh_location=False): | 17 | def config_buildhistory(self, tmp_bh_location=False): |
18 | bb_vars = get_bb_vars(['USER_CLASSES', 'INHERIT']) | 18 | bb_vars = get_bb_vars(['USER_CLASSES', 'INHERIT']) |
@@ -48,5 +48,58 @@ class BuildhistoryBase(OESelftestTestCase): | |||
48 | else: | 48 | else: |
49 | self.assertEqual(result.status, 0, msg="Command 'bitbake %s' has failed unexpectedly: %s" % (target, result.output)) | 49 | self.assertEqual(result.status, 0, msg="Command 'bitbake %s' has failed unexpectedly: %s" % (target, result.output)) |
50 | 50 | ||
51 | # No tests should be added to the base class. | 51 | |
52 | # Please create a new class that inherit this one, or use one of those already available for adding tests. | 52 | def test_buildhistory_basic(self): |
53 | self.run_buildhistory_operation('xcursor-transparent-theme') | ||
54 | self.assertTrue(os.path.isdir(get_bb_var('BUILDHISTORY_DIR')), "buildhistory dir was not created.") | ||
55 | |||
56 | def test_buildhistory_buildtime_pr_backwards(self): | ||
57 | target = 'xcursor-transparent-theme' | ||
58 | error = "ERROR:.*QA Issue: Package version for package %s went backwards which would break package feeds \(from .*-r1.* to .*-r0.*\)" % target | ||
59 | self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True) | ||
60 | self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True, error_regex=error) | ||
61 | |||
62 | def test_fileinfo(self): | ||
63 | self.config_buildhistory() | ||
64 | bitbake('hicolor-icon-theme') | ||
65 | history_dir = get_bb_var('BUILDHISTORY_DIR_PACKAGE', 'hicolor-icon-theme') | ||
66 | self.assertTrue(os.path.isdir(history_dir), 'buildhistory dir was not created.') | ||
67 | |||
68 | def load_bh(f): | ||
69 | d = {} | ||
70 | for line in open(f): | ||
71 | split = [s.strip() for s in line.split('=', 1)] | ||
72 | if len(split) > 1: | ||
73 | d[split[0]] = split[1] | ||
74 | return d | ||
75 | |||
76 | data = load_bh(os.path.join(history_dir, 'hicolor-icon-theme', 'latest')) | ||
77 | self.assertIn('FILELIST', data) | ||
78 | self.assertEqual(data['FILELIST'], '/usr/share/icons/hicolor/index.theme') | ||
79 | self.assertGreater(int(data['PKGSIZE']), 0) | ||
80 | |||
81 | data = load_bh(os.path.join(history_dir, 'hicolor-icon-theme-dev', 'latest')) | ||
82 | if 'FILELIST' in data: | ||
83 | self.assertEqual(data['FILELIST'], '/usr/share/pkgconfig/default-icon-theme.pc') | ||
84 | self.assertGreater(int(data['PKGSIZE']), 0) | ||
85 | |||
86 | def test_buildhistory_diff(self): | ||
87 | target = 'xcursor-transparent-theme' | ||
88 | self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True) | ||
89 | self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True) | ||
90 | result = runCmd("oe-pkgdata-util read-value PKGV %s" % target) | ||
91 | pkgv = result.output.rstrip() | ||
92 | result = runCmd("buildhistory-diff -p %s" % get_bb_var('BUILDHISTORY_DIR')) | ||
93 | expected_endlines = [ | ||
94 | "xcursor-transparent-theme-dev: RRECOMMENDS: removed \"xcursor-transparent-theme (['= %s-r1'])\", added \"xcursor-transparent-theme (['= %s-r0'])\"" % (pkgv, pkgv), | ||
95 | "xcursor-transparent-theme-staticdev: RDEPENDS: removed \"xcursor-transparent-theme-dev (['= %s-r1'])\", added \"xcursor-transparent-theme-dev (['= %s-r0'])\"" % (pkgv, pkgv) | ||
96 | ] | ||
97 | for line in result.output.splitlines(): | ||
98 | for el in expected_endlines: | ||
99 | if line.endswith(el): | ||
100 | expected_endlines.remove(el) | ||
101 | break | ||
102 | else: | ||
103 | self.fail('Unexpected line:\n%s\nExpected line endings:\n %s' % (line, '\n '.join(expected_endlines))) | ||
104 | if expected_endlines: | ||
105 | self.fail('Missing expected line endings:\n %s' % '\n '.join(expected_endlines)) \ No newline at end of file | ||
diff --git a/meta/lib/oeqa/selftest/cases/buildoptions.py b/meta/lib/oeqa/selftest/cases/buildoptions.py index 31dafaa9c5..767e19bd88 100644 --- a/meta/lib/oeqa/selftest/cases/buildoptions.py +++ b/meta/lib/oeqa/selftest/cases/buildoptions.py | |||
@@ -10,7 +10,6 @@ import glob as g | |||
10 | import shutil | 10 | import shutil |
11 | import tempfile | 11 | import tempfile |
12 | from oeqa.selftest.case import OESelftestTestCase | 12 | from oeqa.selftest.case import OESelftestTestCase |
13 | from oeqa.selftest.cases.buildhistory import BuildhistoryBase | ||
14 | from oeqa.core.decorator.data import skipIfMachine | 13 | from oeqa.core.decorator.data import skipIfMachine |
15 | from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars | 14 | from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars |
16 | import oeqa.utils.ftools as ftools | 15 | import oeqa.utils.ftools as ftools |
@@ -84,7 +83,7 @@ class SanityOptionsTest(OESelftestTestCase): | |||
84 | 83 | ||
85 | self.write_config("INHERIT:remove = \"report-error\"") | 84 | self.write_config("INHERIT:remove = \"report-error\"") |
86 | if "packages-list" not in get_bb_var("ERROR_QA"): | 85 | if "packages-list" not in get_bb_var("ERROR_QA"): |
87 | self.append_config("ERROR_QA:append = \" packages-list\"") | 86 | self.append_config("ERROR_QA:append:pn-xcursor-transparent-theme = \" packages-list\"") |
88 | 87 | ||
89 | self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"') | 88 | self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"') |
90 | self.add_command_to_tearDown('bitbake -c clean xcursor-transparent-theme') | 89 | self.add_command_to_tearDown('bitbake -c clean xcursor-transparent-theme') |
@@ -94,8 +93,8 @@ class SanityOptionsTest(OESelftestTestCase): | |||
94 | self.assertTrue(line and line.startswith("ERROR:"), msg=res.output) | 93 | self.assertTrue(line and line.startswith("ERROR:"), msg=res.output) |
95 | self.assertEqual(res.status, 1, msg = "bitbake reported exit code %s. It should have been 1. Bitbake output: %s" % (str(res.status), res.output)) | 94 | self.assertEqual(res.status, 1, msg = "bitbake reported exit code %s. It should have been 1. Bitbake output: %s" % (str(res.status), res.output)) |
96 | self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"') | 95 | self.write_recipeinc('xcursor-transparent-theme', 'PACKAGES += \"${PN}-dbg\"') |
97 | self.append_config('ERROR_QA:remove = "packages-list"') | 96 | self.append_config('ERROR_QA:remove:pn-xcursor-transparent-theme = "packages-list"') |
98 | self.append_config('WARN_QA:append = " packages-list"') | 97 | self.append_config('WARN_QA:append:pn-xcursor-transparent-theme = " packages-list"') |
99 | res = bitbake("xcursor-transparent-theme -f -c package") | 98 | res = bitbake("xcursor-transparent-theme -f -c package") |
100 | self.delete_recipeinc('xcursor-transparent-theme') | 99 | self.delete_recipeinc('xcursor-transparent-theme') |
101 | line = self.getline(res, "QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors.") | 100 | line = self.getline(res, "QA Issue: xcursor-transparent-theme-dbg is listed in PACKAGES multiple times, this leads to packaging errors.") |
@@ -139,43 +138,6 @@ class SanityOptionsTest(OESelftestTestCase): | |||
139 | 138 | ||
140 | self.assertNotIn(err, ret.output) | 139 | self.assertNotIn(err, ret.output) |
141 | 140 | ||
142 | |||
143 | class BuildhistoryTests(BuildhistoryBase): | ||
144 | |||
145 | def test_buildhistory_basic(self): | ||
146 | self.run_buildhistory_operation('xcursor-transparent-theme') | ||
147 | self.assertTrue(os.path.isdir(get_bb_var('BUILDHISTORY_DIR')), "buildhistory dir was not created.") | ||
148 | |||
149 | def test_buildhistory_buildtime_pr_backwards(self): | ||
150 | target = 'xcursor-transparent-theme' | ||
151 | error = "ERROR:.*QA Issue: Package version for package %s went backwards which would break package feeds \(from .*-r1.* to .*-r0.*\)" % target | ||
152 | self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True) | ||
153 | self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True, error_regex=error) | ||
154 | |||
155 | def test_fileinfo(self): | ||
156 | self.config_buildhistory() | ||
157 | bitbake('hicolor-icon-theme') | ||
158 | history_dir = get_bb_var('BUILDHISTORY_DIR_PACKAGE', 'hicolor-icon-theme') | ||
159 | self.assertTrue(os.path.isdir(history_dir), 'buildhistory dir was not created.') | ||
160 | |||
161 | def load_bh(f): | ||
162 | d = {} | ||
163 | for line in open(f): | ||
164 | split = [s.strip() for s in line.split('=', 1)] | ||
165 | if len(split) > 1: | ||
166 | d[split[0]] = split[1] | ||
167 | return d | ||
168 | |||
169 | data = load_bh(os.path.join(history_dir, 'hicolor-icon-theme', 'latest')) | ||
170 | self.assertIn('FILELIST', data) | ||
171 | self.assertEqual(data['FILELIST'], '/usr/share/icons/hicolor/index.theme') | ||
172 | self.assertGreater(int(data['PKGSIZE']), 0) | ||
173 | |||
174 | data = load_bh(os.path.join(history_dir, 'hicolor-icon-theme-dev', 'latest')) | ||
175 | if 'FILELIST' in data: | ||
176 | self.assertEqual(data['FILELIST'], '') | ||
177 | self.assertEqual(int(data['PKGSIZE']), 0) | ||
178 | |||
179 | class ArchiverTest(OESelftestTestCase): | 141 | class ArchiverTest(OESelftestTestCase): |
180 | def test_arch_work_dir_and_export_source(self): | 142 | def test_arch_work_dir_and_export_source(self): |
181 | """ | 143 | """ |
@@ -229,11 +191,10 @@ PREMIRRORS = "\\ | |||
229 | https://.*/.* http://downloads.yoctoproject.org/mirror/sources/ \\n" | 191 | https://.*/.* http://downloads.yoctoproject.org/mirror/sources/ \\n" |
230 | """) | 192 | """) |
231 | 193 | ||
232 | bitbake("world --runall fetch") | 194 | bitbake("world --runall fetch --continue") |
233 | 195 | ||
234 | 196 | ||
235 | class Poisoning(OESelftestTestCase): | 197 | class Poisoning(OESelftestTestCase): |
236 | def test_poisoning(self): | 198 | def test_poisoning(self): |
237 | res = bitbake("poison", ignore_status=True) | 199 | # The poison recipe fails if the poisoning didn't work |
238 | self.assertNotEqual(res.status, 0) | 200 | bitbake("poison") |
239 | self.assertTrue("is unsafe for cross-compilation" in res.output) | ||
diff --git a/meta/lib/oeqa/selftest/cases/containerimage.py b/meta/lib/oeqa/selftest/cases/containerimage.py index 23c0a1408a..d1ac305a84 100644 --- a/meta/lib/oeqa/selftest/cases/containerimage.py +++ b/meta/lib/oeqa/selftest/cases/containerimage.py | |||
@@ -42,7 +42,6 @@ class ContainerImageTests(OESelftestTestCase): | |||
42 | self.write_config("""PREFERRED_PROVIDER_virtual/kernel = "linux-dummy" | 42 | self.write_config("""PREFERRED_PROVIDER_virtual/kernel = "linux-dummy" |
43 | IMAGE_FSTYPES = "container" | 43 | IMAGE_FSTYPES = "container" |
44 | PACKAGE_CLASSES = "package_ipk" | 44 | PACKAGE_CLASSES = "package_ipk" |
45 | IMAGE_FEATURES = "" | ||
46 | IMAGE_BUILDINFO_FILE = "" | 45 | IMAGE_BUILDINFO_FILE = "" |
47 | INIT_MANAGER = "sysvinit" | 46 | INIT_MANAGER = "sysvinit" |
48 | IMAGE_INSTALL:remove = "ssh-pregen-hostkeys" | 47 | IMAGE_INSTALL:remove = "ssh-pregen-hostkeys" |
@@ -55,8 +54,6 @@ IMAGE_INSTALL:remove = "ssh-pregen-hostkeys" | |||
55 | expected_files = [ | 54 | expected_files = [ |
56 | './', | 55 | './', |
57 | '.{bindir}/theapp', | 56 | '.{bindir}/theapp', |
58 | '.{sysconfdir}/default/', | ||
59 | '.{sysconfdir}/default/postinst', | ||
60 | '.{sysconfdir}/ld.so.cache', | 57 | '.{sysconfdir}/ld.so.cache', |
61 | '.{sysconfdir}/timestamp', | 58 | '.{sysconfdir}/timestamp', |
62 | '.{sysconfdir}/version', | 59 | '.{sysconfdir}/version', |
diff --git a/meta/lib/oeqa/selftest/cases/cve_check.py b/meta/lib/oeqa/selftest/cases/cve_check.py index 60cecd1328..511e4b81b4 100644 --- a/meta/lib/oeqa/selftest/cases/cve_check.py +++ b/meta/lib/oeqa/selftest/cases/cve_check.py | |||
@@ -72,6 +72,259 @@ class CVECheck(OESelftestTestCase): | |||
72 | self.assertEqual(convert_cve_version("6.2_rc8"), "6.2-rc8") | 72 | self.assertEqual(convert_cve_version("6.2_rc8"), "6.2-rc8") |
73 | self.assertEqual(convert_cve_version("6.2_rc31"), "6.2-rc31") | 73 | self.assertEqual(convert_cve_version("6.2_rc31"), "6.2-rc31") |
74 | 74 | ||
75 | def test_product_match(self): | ||
76 | from oe.cve_check import has_cve_product_match | ||
77 | |||
78 | status = {} | ||
79 | status["detail"] = "ignored" | ||
80 | status["vendor"] = "*" | ||
81 | status["product"] = "*" | ||
82 | status["description"] = "" | ||
83 | status["mapping"] = "" | ||
84 | |||
85 | self.assertEqual(has_cve_product_match(status, "some_vendor:some_product"), True) | ||
86 | self.assertEqual(has_cve_product_match(status, "*:*"), True) | ||
87 | self.assertEqual(has_cve_product_match(status, "some_product"), True) | ||
88 | self.assertEqual(has_cve_product_match(status, "glibc"), True) | ||
89 | self.assertEqual(has_cve_product_match(status, "glibca"), True) | ||
90 | self.assertEqual(has_cve_product_match(status, "aglibc"), True) | ||
91 | self.assertEqual(has_cve_product_match(status, "*"), True) | ||
92 | self.assertEqual(has_cve_product_match(status, "aglibc glibc test:test"), True) | ||
93 | |||
94 | status["product"] = "glibc" | ||
95 | self.assertEqual(has_cve_product_match(status, "some_vendor:some_product"), False) | ||
96 | # The CPE in the recipe must be defined, no * accepted | ||
97 | self.assertEqual(has_cve_product_match(status, "*:*"), False) | ||
98 | self.assertEqual(has_cve_product_match(status, "*"), False) | ||
99 | self.assertEqual(has_cve_product_match(status, "some_product"), False) | ||
100 | self.assertEqual(has_cve_product_match(status, "glibc"), True) | ||
101 | self.assertEqual(has_cve_product_match(status, "glibca"), False) | ||
102 | self.assertEqual(has_cve_product_match(status, "aglibc"), False) | ||
103 | self.assertEqual(has_cve_product_match(status, "some_vendor:glibc"), True) | ||
104 | self.assertEqual(has_cve_product_match(status, "some_vendor:glibc test"), True) | ||
105 | self.assertEqual(has_cve_product_match(status, "test some_vendor:glibc"), True) | ||
106 | |||
107 | status["vendor"] = "glibca" | ||
108 | status["product"] = "glibc" | ||
109 | self.assertEqual(has_cve_product_match(status, "some_vendor:some_product"), False) | ||
110 | # The CPE in the recipe must be defined, no * accepted | ||
111 | self.assertEqual(has_cve_product_match(status, "*:*"), False) | ||
112 | self.assertEqual(has_cve_product_match(status, "*"), False) | ||
113 | self.assertEqual(has_cve_product_match(status, "some_product"), False) | ||
114 | self.assertEqual(has_cve_product_match(status, "glibc"), False) | ||
115 | self.assertEqual(has_cve_product_match(status, "glibca"), False) | ||
116 | self.assertEqual(has_cve_product_match(status, "aglibc"), False) | ||
117 | self.assertEqual(has_cve_product_match(status, "some_vendor:glibc"), False) | ||
118 | self.assertEqual(has_cve_product_match(status, "glibca:glibc"), True) | ||
119 | self.assertEqual(has_cve_product_match(status, "test:test glibca:glibc"), True) | ||
120 | self.assertEqual(has_cve_product_match(status, "test glibca:glibc"), True) | ||
121 | self.assertEqual(has_cve_product_match(status, "glibca:glibc test"), True) | ||
122 | |||
123 | def test_parse_cve_from_patch_filename(self): | ||
124 | from oe.cve_check import parse_cve_from_filename | ||
125 | |||
126 | # Patch filename without CVE ID | ||
127 | self.assertEqual(parse_cve_from_filename("0001-test.patch"), "") | ||
128 | |||
129 | # Patch with single CVE ID | ||
130 | self.assertEqual( | ||
131 | parse_cve_from_filename("CVE-2022-12345.patch"), "CVE-2022-12345" | ||
132 | ) | ||
133 | |||
134 | # Patch with multiple CVE IDs | ||
135 | self.assertEqual( | ||
136 | parse_cve_from_filename("CVE-2022-41741-CVE-2022-41742.patch"), | ||
137 | "CVE-2022-41742", | ||
138 | ) | ||
139 | |||
140 | # Patches with CVE ID and appended text | ||
141 | self.assertEqual( | ||
142 | parse_cve_from_filename("CVE-2023-3019-0001.patch"), "CVE-2023-3019" | ||
143 | ) | ||
144 | self.assertEqual( | ||
145 | parse_cve_from_filename("CVE-2024-21886-1.patch"), "CVE-2024-21886" | ||
146 | ) | ||
147 | |||
148 | # Patch with CVE ID and prepended text | ||
149 | self.assertEqual( | ||
150 | parse_cve_from_filename("grep-CVE-2012-5667.patch"), "CVE-2012-5667" | ||
151 | ) | ||
152 | self.assertEqual( | ||
153 | parse_cve_from_filename("0001-CVE-2012-5667.patch"), "CVE-2012-5667" | ||
154 | ) | ||
155 | |||
156 | # Patch with CVE ID and both prepended and appended text | ||
157 | self.assertEqual( | ||
158 | parse_cve_from_filename( | ||
159 | "0001-tpm2_import-fix-fixed-AES-key-CVE-2021-3565-0001.patch" | ||
160 | ), | ||
161 | "CVE-2021-3565", | ||
162 | ) | ||
163 | |||
164 | # Only grab the last CVE ID in the filename | ||
165 | self.assertEqual( | ||
166 | parse_cve_from_filename("CVE-2012-5667-CVE-2012-5668.patch"), | ||
167 | "CVE-2012-5668", | ||
168 | ) | ||
169 | |||
170 | # Test invalid CVE ID with incorrect length (must be at least 4 digits) | ||
171 | self.assertEqual( | ||
172 | parse_cve_from_filename("CVE-2024-001.patch"), | ||
173 | "", | ||
174 | ) | ||
175 | |||
176 | # Test valid CVE ID with very long length | ||
177 | self.assertEqual( | ||
178 | parse_cve_from_filename("CVE-2024-0000000000000000000000001.patch"), | ||
179 | "CVE-2024-0000000000000000000000001", | ||
180 | ) | ||
181 | |||
182 | def test_parse_cve_from_patch_contents(self): | ||
183 | import textwrap | ||
184 | from oe.cve_check import parse_cves_from_patch_contents | ||
185 | |||
186 | # Standard patch file excerpt without any patches | ||
187 | self.assertEqual( | ||
188 | parse_cves_from_patch_contents( | ||
189 | textwrap.dedent("""\ | ||
190 | remove "*" for root since we don't have a /etc/shadow so far. | ||
191 | |||
192 | Upstream-Status: Inappropriate [configuration] | ||
193 | |||
194 | Signed-off-by: Scott Garman <scott.a.garman@intel.com> | ||
195 | |||
196 | --- base-passwd/passwd.master~nobash | ||
197 | +++ base-passwd/passwd.master | ||
198 | @@ -1,4 +1,4 @@ | ||
199 | -root:*:0:0:root:/root:/bin/sh | ||
200 | +root::0:0:root:/root:/bin/sh | ||
201 | daemon:*:1:1:daemon:/usr/sbin:/bin/sh | ||
202 | bin:*:2:2:bin:/bin:/bin/sh | ||
203 | sys:*:3:3:sys:/dev:/bin/sh | ||
204 | """) | ||
205 | ), | ||
206 | set(), | ||
207 | ) | ||
208 | |||
209 | # Patch file with multiple CVE IDs (space-separated) | ||
210 | self.assertEqual( | ||
211 | parse_cves_from_patch_contents( | ||
212 | textwrap.dedent("""\ | ||
213 | There is an assertion in function _cairo_arc_in_direction(). | ||
214 | |||
215 | CVE: CVE-2019-6461 CVE-2019-6462 | ||
216 | Upstream-Status: Pending | ||
217 | Signed-off-by: Ross Burton <ross.burton@intel.com> | ||
218 | |||
219 | diff --git a/src/cairo-arc.c b/src/cairo-arc.c | ||
220 | index 390397bae..1bde774a4 100644 | ||
221 | --- a/src/cairo-arc.c | ||
222 | +++ b/src/cairo-arc.c | ||
223 | @@ -186,7 +186,8 @@ _cairo_arc_in_direction (cairo_t *cr, | ||
224 | if (cairo_status (cr)) | ||
225 | return; | ||
226 | |||
227 | - assert (angle_max >= angle_min); | ||
228 | + if (angle_max < angle_min) | ||
229 | + return; | ||
230 | |||
231 | if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) { | ||
232 | angle_max = fmod (angle_max - angle_min, 2 * M_PI); | ||
233 | """), | ||
234 | ), | ||
235 | {"CVE-2019-6461", "CVE-2019-6462"}, | ||
236 | ) | ||
237 | |||
238 | # Patch file with multiple CVE IDs (comma-separated w/ both space and no space) | ||
239 | self.assertEqual( | ||
240 | parse_cves_from_patch_contents( | ||
241 | textwrap.dedent("""\ | ||
242 | There is an assertion in function _cairo_arc_in_direction(). | ||
243 | |||
244 | CVE: CVE-2019-6461,CVE-2019-6462, CVE-2019-6463 | ||
245 | Upstream-Status: Pending | ||
246 | Signed-off-by: Ross Burton <ross.burton@intel.com> | ||
247 | |||
248 | diff --git a/src/cairo-arc.c b/src/cairo-arc.c | ||
249 | index 390397bae..1bde774a4 100644 | ||
250 | --- a/src/cairo-arc.c | ||
251 | +++ b/src/cairo-arc.c | ||
252 | @@ -186,7 +186,8 @@ _cairo_arc_in_direction (cairo_t *cr, | ||
253 | if (cairo_status (cr)) | ||
254 | return; | ||
255 | |||
256 | - assert (angle_max >= angle_min); | ||
257 | + if (angle_max < angle_min) | ||
258 | + return; | ||
259 | |||
260 | if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) { | ||
261 | angle_max = fmod (angle_max - angle_min, 2 * M_PI); | ||
262 | |||
263 | """), | ||
264 | ), | ||
265 | {"CVE-2019-6461", "CVE-2019-6462", "CVE-2019-6463"}, | ||
266 | ) | ||
267 | |||
268 | # Patch file with multiple CVE IDs (&-separated) | ||
269 | self.assertEqual( | ||
270 | parse_cves_from_patch_contents( | ||
271 | textwrap.dedent("""\ | ||
272 | There is an assertion in function _cairo_arc_in_direction(). | ||
273 | |||
274 | CVE: CVE-2019-6461 & CVE-2019-6462 | ||
275 | Upstream-Status: Pending | ||
276 | Signed-off-by: Ross Burton <ross.burton@intel.com> | ||
277 | |||
278 | diff --git a/src/cairo-arc.c b/src/cairo-arc.c | ||
279 | index 390397bae..1bde774a4 100644 | ||
280 | --- a/src/cairo-arc.c | ||
281 | +++ b/src/cairo-arc.c | ||
282 | @@ -186,7 +186,8 @@ _cairo_arc_in_direction (cairo_t *cr, | ||
283 | if (cairo_status (cr)) | ||
284 | return; | ||
285 | |||
286 | - assert (angle_max >= angle_min); | ||
287 | + if (angle_max < angle_min) | ||
288 | + return; | ||
289 | |||
290 | if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) { | ||
291 | angle_max = fmod (angle_max - angle_min, 2 * M_PI); | ||
292 | """), | ||
293 | ), | ||
294 | {"CVE-2019-6461", "CVE-2019-6462"}, | ||
295 | ) | ||
296 | |||
297 | # Patch file with multiple lines with CVE IDs | ||
298 | self.assertEqual( | ||
299 | parse_cves_from_patch_contents( | ||
300 | textwrap.dedent("""\ | ||
301 | There is an assertion in function _cairo_arc_in_direction(). | ||
302 | |||
303 | CVE: CVE-2019-6461 & CVE-2019-6462 | ||
304 | |||
305 | CVE: CVE-2019-6463 & CVE-2019-6464 | ||
306 | Upstream-Status: Pending | ||
307 | Signed-off-by: Ross Burton <ross.burton@intel.com> | ||
308 | |||
309 | diff --git a/src/cairo-arc.c b/src/cairo-arc.c | ||
310 | index 390397bae..1bde774a4 100644 | ||
311 | --- a/src/cairo-arc.c | ||
312 | +++ b/src/cairo-arc.c | ||
313 | @@ -186,7 +186,8 @@ _cairo_arc_in_direction (cairo_t *cr, | ||
314 | if (cairo_status (cr)) | ||
315 | return; | ||
316 | |||
317 | - assert (angle_max >= angle_min); | ||
318 | + if (angle_max < angle_min) | ||
319 | + return; | ||
320 | |||
321 | if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) { | ||
322 | angle_max = fmod (angle_max - angle_min, 2 * M_PI); | ||
323 | |||
324 | """), | ||
325 | ), | ||
326 | {"CVE-2019-6461", "CVE-2019-6462", "CVE-2019-6463", "CVE-2019-6464"}, | ||
327 | ) | ||
75 | 328 | ||
76 | def test_recipe_report_json(self): | 329 | def test_recipe_report_json(self): |
77 | config = """ | 330 | config = """ |
@@ -217,9 +470,10 @@ CVE_CHECK_REPORT_PATCHED = "1" | |||
217 | # m4 CVE should not be in logrotate | 470 | # m4 CVE should not be in logrotate |
218 | self.assertNotIn("CVE-2008-1687", found_cves) | 471 | self.assertNotIn("CVE-2008-1687", found_cves) |
219 | # logrotate has both Patched and Ignored CVEs | 472 | # logrotate has both Patched and Ignored CVEs |
473 | detail = "version-not-in-range" | ||
220 | self.assertIn("CVE-2011-1098", found_cves) | 474 | self.assertIn("CVE-2011-1098", found_cves) |
221 | self.assertEqual(found_cves["CVE-2011-1098"]["status"], "Patched") | 475 | self.assertEqual(found_cves["CVE-2011-1098"]["status"], "Patched") |
222 | self.assertEqual(len(found_cves["CVE-2011-1098"]["detail"]), 0) | 476 | self.assertEqual(found_cves["CVE-2011-1098"]["detail"], detail) |
223 | self.assertEqual(len(found_cves["CVE-2011-1098"]["description"]), 0) | 477 | self.assertEqual(len(found_cves["CVE-2011-1098"]["description"]), 0) |
224 | detail = "not-applicable-platform" | 478 | detail = "not-applicable-platform" |
225 | description = "CVE is debian, gentoo or SUSE specific on the way logrotate was installed/used" | 479 | description = "CVE is debian, gentoo or SUSE specific on the way logrotate was installed/used" |
diff --git a/meta/lib/oeqa/selftest/cases/debuginfod.py b/meta/lib/oeqa/selftest/cases/debuginfod.py index 505b4be837..46c0cd87bb 100644 --- a/meta/lib/oeqa/selftest/cases/debuginfod.py +++ b/meta/lib/oeqa/selftest/cases/debuginfod.py | |||
@@ -62,7 +62,7 @@ class Debuginfod(OESelftestTestCase): | |||
62 | 62 | ||
63 | raise TimeoutError("Cannot connect debuginfod, still %d scan jobs running" % latest) | 63 | raise TimeoutError("Cannot connect debuginfod, still %d scan jobs running" % latest) |
64 | 64 | ||
65 | def start_debuginfod(self): | 65 | def start_debuginfod(self, feed_dir): |
66 | # We assume that the caller has already bitbake'd elfutils-native:do_addto_recipe_sysroot | 66 | # We assume that the caller has already bitbake'd elfutils-native:do_addto_recipe_sysroot |
67 | 67 | ||
68 | # Save some useful paths for later | 68 | # Save some useful paths for later |
@@ -82,7 +82,7 @@ class Debuginfod(OESelftestTestCase): | |||
82 | # Disable rescanning, this is a one-shot test | 82 | # Disable rescanning, this is a one-shot test |
83 | "--rescan-time=0", | 83 | "--rescan-time=0", |
84 | "--groom-time=0", | 84 | "--groom-time=0", |
85 | get_bb_var("DEPLOY_DIR"), | 85 | feed_dir, |
86 | ] | 86 | ] |
87 | 87 | ||
88 | format = get_bb_var("PACKAGE_CLASSES").split()[0] | 88 | format = get_bb_var("PACKAGE_CLASSES").split()[0] |
@@ -114,11 +114,12 @@ class Debuginfod(OESelftestTestCase): | |||
114 | self.write_config(""" | 114 | self.write_config(""" |
115 | TMPDIR = "${TOPDIR}/tmp-debuginfod" | 115 | TMPDIR = "${TOPDIR}/tmp-debuginfod" |
116 | DISTRO_FEATURES:append = " debuginfod" | 116 | DISTRO_FEATURES:append = " debuginfod" |
117 | INHERIT += "localpkgfeed" | ||
117 | """) | 118 | """) |
118 | bitbake("elfutils-native:do_addto_recipe_sysroot xz xz:do_package") | 119 | bitbake("elfutils-native:do_addto_recipe_sysroot xz xz:do_package xz:do_localpkgfeed") |
119 | 120 | ||
120 | try: | 121 | try: |
121 | self.start_debuginfod() | 122 | self.start_debuginfod(get_bb_var("LOCALPKGFEED_DIR", "xz")) |
122 | 123 | ||
123 | env = os.environ.copy() | 124 | env = os.environ.copy() |
124 | env["DEBUGINFOD_URLS"] = "http://localhost:%d/" % self.port | 125 | env["DEBUGINFOD_URLS"] = "http://localhost:%d/" % self.port |
@@ -141,12 +142,13 @@ DISTRO_FEATURES:append = " debuginfod" | |||
141 | self.write_config(""" | 142 | self.write_config(""" |
142 | TMPDIR = "${TOPDIR}/tmp-debuginfod" | 143 | TMPDIR = "${TOPDIR}/tmp-debuginfod" |
143 | DISTRO_FEATURES:append = " debuginfod" | 144 | DISTRO_FEATURES:append = " debuginfod" |
145 | INHERIT += "localpkgfeed" | ||
144 | CORE_IMAGE_EXTRA_INSTALL += "elfutils xz" | 146 | CORE_IMAGE_EXTRA_INSTALL += "elfutils xz" |
145 | """) | 147 | """) |
146 | bitbake("core-image-minimal elfutils-native:do_addto_recipe_sysroot") | 148 | bitbake("core-image-minimal elfutils-native:do_addto_recipe_sysroot xz:do_localpkgfeed") |
147 | 149 | ||
148 | try: | 150 | try: |
149 | self.start_debuginfod() | 151 | self.start_debuginfod(get_bb_var("LOCALPKGFEED_DIR", "xz")) |
150 | 152 | ||
151 | with runqemu("core-image-minimal", runqemuparams="nographic") as qemu: | 153 | with runqemu("core-image-minimal", runqemuparams="nographic") as qemu: |
152 | cmd = "DEBUGINFOD_URLS=http://%s:%d/ debuginfod-find debuginfo /usr/bin/xz" % (qemu.server_ip, self.port) | 154 | cmd = "DEBUGINFOD_URLS=http://%s:%d/ debuginfod-find debuginfo /usr/bin/xz" % (qemu.server_ip, self.port) |
diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py index bc1e40ef83..05f228f03e 100644 --- a/meta/lib/oeqa/selftest/cases/devtool.py +++ b/meta/lib/oeqa/selftest/cases/devtool.py | |||
@@ -64,11 +64,15 @@ def setUpModule(): | |||
64 | # under COREBASE and we don't want to copy that, so we have | 64 | # under COREBASE and we don't want to copy that, so we have |
65 | # to be selective. | 65 | # to be selective. |
66 | result = runCmd('git status --porcelain', cwd=oldreporoot) | 66 | result = runCmd('git status --porcelain', cwd=oldreporoot) |
67 | |||
68 | # Also copy modifications to the 'scripts/' directory | ||
69 | canonical_layerpath_scripts = os.path.normpath(canonical_layerpath + "../scripts") | ||
70 | |||
67 | for line in result.output.splitlines(): | 71 | for line in result.output.splitlines(): |
68 | if line.startswith(' M ') or line.startswith('?? '): | 72 | if line.startswith(' M ') or line.startswith('?? '): |
69 | relpth = line.split()[1] | 73 | relpth = line.split()[1] |
70 | pth = os.path.join(oldreporoot, relpth) | 74 | pth = os.path.join(oldreporoot, relpth) |
71 | if pth.startswith(canonical_layerpath): | 75 | if pth.startswith(canonical_layerpath) or pth.startswith(canonical_layerpath_scripts): |
72 | if relpth.endswith('/'): | 76 | if relpth.endswith('/'): |
73 | destdir = os.path.join(corecopydir, relpth) | 77 | destdir = os.path.join(corecopydir, relpth) |
74 | # avoid race condition by not copying .pyc files YPBZ#13421,13803 | 78 | # avoid race condition by not copying .pyc files YPBZ#13421,13803 |
@@ -150,7 +154,7 @@ class DevtoolTestCase(OESelftestTestCase): | |||
150 | value = invalue | 154 | value = invalue |
151 | invar = None | 155 | invar = None |
152 | elif '=' in line: | 156 | elif '=' in line: |
153 | splitline = line.split('=', 1) | 157 | splitline = re.split(r"[?+:]*=[+]?", line, 1) |
154 | var = splitline[0].rstrip() | 158 | var = splitline[0].rstrip() |
155 | value = splitline[1].strip().strip('"') | 159 | value = splitline[1].strip().strip('"') |
156 | if value.endswith('\\'): | 160 | if value.endswith('\\'): |
@@ -286,10 +290,13 @@ class DevtoolTestCase(OESelftestTestCase): | |||
286 | else: | 290 | else: |
287 | self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test') | 291 | self.skipTest('No tap devices found - you must set up tap devices with scripts/runqemu-gen-tapdevs before running this test') |
288 | 292 | ||
289 | def _test_devtool_add_git_url(self, git_url, version, pn, resulting_src_uri): | 293 | def _test_devtool_add_git_url(self, git_url, version, pn, resulting_src_uri, srcrev=None): |
290 | self.track_for_cleanup(self.workspacedir) | 294 | self.track_for_cleanup(self.workspacedir) |
291 | self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') | 295 | self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') |
292 | result = runCmd('devtool add --version %s %s %s' % (version, pn, git_url)) | 296 | command = 'devtool add --version %s %s %s' % (version, pn, git_url) |
297 | if srcrev : | ||
298 | command += ' --srcrev %s' %srcrev | ||
299 | result = runCmd(command) | ||
293 | self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created') | 300 | self.assertExists(os.path.join(self.workspacedir, 'conf', 'layer.conf'), 'Workspace directory not created') |
294 | # Check the recipe name is correct | 301 | # Check the recipe name is correct |
295 | recipefile = get_bb_var('FILE', pn) | 302 | recipefile = get_bb_var('FILE', pn) |
@@ -314,7 +321,7 @@ class DevtoolBase(DevtoolTestCase): | |||
314 | cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate | 321 | cls.sstate_conf = 'SSTATE_DIR = "%s"\n' % cls.devtool_sstate |
315 | cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n' | 322 | cls.sstate_conf += ('SSTATE_MIRRORS += "file://.* file:///%s/PATH"\n' |
316 | % cls.original_sstate) | 323 | % cls.original_sstate) |
317 | cls.sstate_conf += ('BB_HASHSERVE_UPSTREAM = "hashserv.yocto.io:8687"\n') | 324 | cls.sstate_conf += ('BB_HASHSERVE_UPSTREAM = "hashserv.yoctoproject.org:8686"\n') |
318 | 325 | ||
319 | @classmethod | 326 | @classmethod |
320 | def tearDownClass(cls): | 327 | def tearDownClass(cls): |
@@ -462,7 +469,7 @@ class DevtoolAddTests(DevtoolBase): | |||
462 | checkvars = {} | 469 | checkvars = {} |
463 | checkvars['LICENSE'] = 'GPL-2.0-only' | 470 | checkvars['LICENSE'] = 'GPL-2.0-only' |
464 | checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263' | 471 | checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=b234ee4d69f5fce4486a80fdaf4a4263' |
465 | checkvars['S'] = '${WORKDIR}/git' | 472 | checkvars['S'] = None |
466 | checkvars['PV'] = '0.1+git' | 473 | checkvars['PV'] = '0.1+git' |
467 | checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https;branch=master' | 474 | checkvars['SRC_URI'] = 'git://git.yoctoproject.org/git/dbus-wait;protocol=https;branch=master' |
468 | checkvars['SRCREV'] = srcrev | 475 | checkvars['SRCREV'] = srcrev |
@@ -479,11 +486,12 @@ class DevtoolAddTests(DevtoolBase): | |||
479 | 486 | ||
480 | def test_devtool_add_git_style2(self): | 487 | def test_devtool_add_git_style2(self): |
481 | version = 'v3.1.0' | 488 | version = 'v3.1.0' |
489 | srcrev = 'v3.1.0' | ||
482 | pn = 'mbedtls' | 490 | pn = 'mbedtls' |
483 | # this will trigger reformat_git_uri with branch parameter in url | 491 | # this will trigger reformat_git_uri with branch parameter in url |
484 | git_url = "'git://git@github.com/ARMmbed/mbedtls.git;protocol=https'" | 492 | git_url = "'git://git@github.com/ARMmbed/mbedtls.git;protocol=https'" |
485 | resulting_src_uri = "gitsm://git@github.com/ARMmbed/mbedtls.git;protocol=https;branch=master" | 493 | resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;protocol=https;branch=master" |
486 | self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri) | 494 | self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri, srcrev) |
487 | 495 | ||
488 | def test_devtool_add_library(self): | 496 | def test_devtool_add_library(self): |
489 | # Fetch source | 497 | # Fetch source |
@@ -557,7 +565,7 @@ class DevtoolAddTests(DevtoolBase): | |||
557 | recipefile = get_bb_var('FILE', testrecipe) | 565 | recipefile = get_bb_var('FILE', testrecipe) |
558 | self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named') | 566 | self.assertIn('%s_%s.bb' % (testrecipe, testver), recipefile, 'Recipe file incorrectly named') |
559 | checkvars = {} | 567 | checkvars = {} |
560 | checkvars['S'] = '${WORKDIR}/MarkupSafe-${PV}' | 568 | checkvars['S'] = '${UNPACKDIR}/MarkupSafe-${PV}' |
561 | checkvars['SRC_URI'] = url.replace(testver, '${PV}') | 569 | checkvars['SRC_URI'] = url.replace(testver, '${PV}') |
562 | self._test_recipe_contents(recipefile, checkvars, []) | 570 | self._test_recipe_contents(recipefile, checkvars, []) |
563 | # Try with version specified | 571 | # Try with version specified |
@@ -574,7 +582,7 @@ class DevtoolAddTests(DevtoolBase): | |||
574 | recipefile = get_bb_var('FILE', testrecipe) | 582 | recipefile = get_bb_var('FILE', testrecipe) |
575 | self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named') | 583 | self.assertIn('%s_%s.bb' % (testrecipe, fakever), recipefile, 'Recipe file incorrectly named') |
576 | checkvars = {} | 584 | checkvars = {} |
577 | checkvars['S'] = '${WORKDIR}/MarkupSafe-%s' % testver | 585 | checkvars['S'] = '${UNPACKDIR}/MarkupSafe-%s' % testver |
578 | checkvars['SRC_URI'] = url | 586 | checkvars['SRC_URI'] = url |
579 | self._test_recipe_contents(recipefile, checkvars, []) | 587 | self._test_recipe_contents(recipefile, checkvars, []) |
580 | 588 | ||
@@ -601,7 +609,7 @@ class DevtoolAddTests(DevtoolBase): | |||
601 | recipefile = get_bb_var('FILE', testrecipe) | 609 | recipefile = get_bb_var('FILE', testrecipe) |
602 | self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') | 610 | self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') |
603 | checkvars = {} | 611 | checkvars = {} |
604 | checkvars['S'] = '${WORKDIR}/git' | 612 | checkvars['S'] = None |
605 | checkvars['PV'] = '1.0+git' | 613 | checkvars['PV'] = '1.0+git' |
606 | checkvars['SRC_URI'] = url_branch | 614 | checkvars['SRC_URI'] = url_branch |
607 | checkvars['SRCREV'] = '${AUTOREV}' | 615 | checkvars['SRCREV'] = '${AUTOREV}' |
@@ -620,7 +628,7 @@ class DevtoolAddTests(DevtoolBase): | |||
620 | recipefile = get_bb_var('FILE', testrecipe) | 628 | recipefile = get_bb_var('FILE', testrecipe) |
621 | self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') | 629 | self.assertIn('_git.bb', recipefile, 'Recipe file incorrectly named') |
622 | checkvars = {} | 630 | checkvars = {} |
623 | checkvars['S'] = '${WORKDIR}/git' | 631 | checkvars['S'] = None |
624 | checkvars['PV'] = '1.5+git' | 632 | checkvars['PV'] = '1.5+git' |
625 | checkvars['SRC_URI'] = url_branch | 633 | checkvars['SRC_URI'] = url_branch |
626 | checkvars['SRCREV'] = checkrev | 634 | checkvars['SRCREV'] = checkrev |
@@ -749,6 +757,25 @@ class DevtoolModifyTests(DevtoolBase): | |||
749 | result = runCmd('devtool status') | 757 | result = runCmd('devtool status') |
750 | self.assertNotIn('mdadm', result.output) | 758 | self.assertNotIn('mdadm', result.output) |
751 | 759 | ||
760 | def test_devtool_modify_go(self): | ||
761 | import oe.path | ||
762 | from tempfile import TemporaryDirectory | ||
763 | with TemporaryDirectory(prefix='devtoolqa') as tempdir: | ||
764 | self.track_for_cleanup(self.workspacedir) | ||
765 | self.add_command_to_tearDown('bitbake -c clean go-helloworld') | ||
766 | self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') | ||
767 | result = runCmd('devtool modify go-helloworld -x %s' % tempdir) | ||
768 | self.assertExists( | ||
769 | oe.path.join(tempdir, 'src', 'golang.org', 'x', 'example', 'go.mod'), | ||
770 | 'Extracted source could not be found' | ||
771 | ) | ||
772 | self.assertExists( | ||
773 | oe.path.join(self.workspacedir, 'conf', 'layer.conf'), | ||
774 | 'Workspace directory not created' | ||
775 | ) | ||
776 | matches = glob.glob(oe.path.join(self.workspacedir, 'appends', 'go-helloworld_*.bbappend')) | ||
777 | self.assertTrue(matches, 'bbappend not created %s' % result.output) | ||
778 | |||
752 | def test_devtool_buildclean(self): | 779 | def test_devtool_buildclean(self): |
753 | def assertFile(path, *paths): | 780 | def assertFile(path, *paths): |
754 | f = os.path.join(path, *paths) | 781 | f = os.path.join(path, *paths) |
@@ -875,13 +902,8 @@ class DevtoolModifyTests(DevtoolBase): | |||
875 | self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) | 902 | self.add_command_to_tearDown('bitbake -c clean %s' % testrecipe) |
876 | self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') | 903 | self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') |
877 | result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) | 904 | result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) |
878 | srcfile = os.path.join(tempdir, 'oe-local-files/share/dot.bashrc') | 905 | srcfile = os.path.join(tempdir, 'share/dot.bashrc') |
879 | srclink = os.path.join(tempdir, 'share/dot.bashrc') | ||
880 | self.assertExists(srcfile, 'Extracted source could not be found') | 906 | self.assertExists(srcfile, 'Extracted source could not be found') |
881 | if os.path.islink(srclink) and os.path.exists(srclink) and os.path.samefile(srcfile, srclink): | ||
882 | correct_symlink = True | ||
883 | self.assertTrue(correct_symlink, 'Source symlink to oe-local-files is broken') | ||
884 | |||
885 | matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe)) | 907 | matches = glob.glob(os.path.join(self.workspacedir, 'appends', '%s_*.bbappend' % testrecipe)) |
886 | self.assertTrue(matches, 'bbappend not created') | 908 | self.assertTrue(matches, 'bbappend not created') |
887 | # Test devtool status | 909 | # Test devtool status |
@@ -952,9 +974,9 @@ class DevtoolModifyTests(DevtoolBase): | |||
952 | # others git:// in SRC_URI | 974 | # others git:// in SRC_URI |
953 | # cointains a patch | 975 | # cointains a patch |
954 | testrecipe = 'hello-rs' | 976 | testrecipe = 'hello-rs' |
955 | bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'WORKDIR', 'CARGO_HOME'], testrecipe) | 977 | bb_vars = get_bb_vars(['SRC_URI', 'FILE', 'UNPACKDIR', 'CARGO_HOME'], testrecipe) |
956 | recipefile = bb_vars['FILE'] | 978 | recipefile = bb_vars['FILE'] |
957 | workdir = bb_vars['WORKDIR'] | 979 | unpackdir = bb_vars['UNPACKDIR'] |
958 | cargo_home = bb_vars['CARGO_HOME'] | 980 | cargo_home = bb_vars['CARGO_HOME'] |
959 | src_uri = bb_vars['SRC_URI'].split() | 981 | src_uri = bb_vars['SRC_URI'].split() |
960 | self.assertTrue(src_uri[0].startswith('git://'), | 982 | self.assertTrue(src_uri[0].startswith('git://'), |
@@ -1005,7 +1027,7 @@ class DevtoolModifyTests(DevtoolBase): | |||
1005 | # Configure the recipe to check that the git dependencies are correctly patched in cargo config | 1027 | # Configure the recipe to check that the git dependencies are correctly patched in cargo config |
1006 | bitbake('-c configure %s' % testrecipe) | 1028 | bitbake('-c configure %s' % testrecipe) |
1007 | 1029 | ||
1008 | cargo_config_path = os.path.join(cargo_home, 'config') | 1030 | cargo_config_path = os.path.join(cargo_home, 'config.toml') |
1009 | with open(cargo_config_path, "r") as f: | 1031 | with open(cargo_config_path, "r") as f: |
1010 | cargo_config_contents = [line.strip('\n') for line in f.readlines()] | 1032 | cargo_config_contents = [line.strip('\n') for line in f.readlines()] |
1011 | 1033 | ||
@@ -1025,7 +1047,7 @@ class DevtoolModifyTests(DevtoolBase): | |||
1025 | self.assertEqual(parms['type'], 'git-dependency', 'git dependencies uri should have "type=git-dependency"') | 1047 | self.assertEqual(parms['type'], 'git-dependency', 'git dependencies uri should have "type=git-dependency"') |
1026 | raw_url = raw_url.replace("git://", '%s://' % parms['protocol']) | 1048 | raw_url = raw_url.replace("git://", '%s://' % parms['protocol']) |
1027 | patch_line = '[patch."%s"]' % raw_url | 1049 | patch_line = '[patch."%s"]' % raw_url |
1028 | path_patched = os.path.join(workdir, parms['destsuffix']) | 1050 | path_patched = os.path.join(unpackdir, parms['destsuffix']) |
1029 | path_override_line = '%s = { path = "%s" }' % (parms['name'], path_patched) | 1051 | path_override_line = '%s = { path = "%s" }' % (parms['name'], path_patched) |
1030 | # Would have been better to use tomllib to read this file :/ | 1052 | # Would have been better to use tomllib to read this file :/ |
1031 | self.assertIn(patch_line, cargo_config_contents) | 1053 | self.assertIn(patch_line, cargo_config_contents) |
@@ -1163,13 +1185,16 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1163 | result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir) | 1185 | result = runCmd('echo "A new file" > devtool-new-file', cwd=tempdir) |
1164 | result = runCmd('git add devtool-new-file', cwd=tempdir) | 1186 | result = runCmd('git add devtool-new-file', cwd=tempdir) |
1165 | result = runCmd('git commit -m "Add a new file"', cwd=tempdir) | 1187 | result = runCmd('git commit -m "Add a new file"', cwd=tempdir) |
1166 | self.add_command_to_tearDown('cd %s; rm %s/*.patch; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) | 1188 | cleanup_cmd = 'cd %s; rm %s/*.patch; git add %s; git checkout %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile)) |
1189 | self.add_command_to_tearDown(cleanup_cmd) | ||
1167 | result = runCmd('devtool update-recipe %s' % testrecipe) | 1190 | result = runCmd('devtool update-recipe %s' % testrecipe) |
1168 | result = runCmd('git add minicom', cwd=os.path.dirname(recipefile)) | 1191 | result = runCmd('git add minicom', cwd=os.path.dirname(recipefile)) |
1169 | expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), | 1192 | expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), |
1170 | ('A ', '.*/0001-Change-the-README.patch$'), | 1193 | ('A ', '.*/0001-Change-the-README.patch$'), |
1171 | ('A ', '.*/0002-Add-a-new-file.patch$')] | 1194 | ('A ', '.*/0002-Add-a-new-file.patch$')] |
1172 | self._check_repo_status(os.path.dirname(recipefile), expected_status) | 1195 | self._check_repo_status(os.path.dirname(recipefile), expected_status) |
1196 | result = runCmd(cleanup_cmd) | ||
1197 | self._check_repo_status(os.path.dirname(recipefile), []) | ||
1173 | 1198 | ||
1174 | def test_devtool_update_recipe_git(self): | 1199 | def test_devtool_update_recipe_git(self): |
1175 | # Check preconditions | 1200 | # Check preconditions |
@@ -1226,7 +1251,7 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1226 | 1251 | ||
1227 | def test_devtool_update_recipe_append(self): | 1252 | def test_devtool_update_recipe_append(self): |
1228 | # Check preconditions | 1253 | # Check preconditions |
1229 | testrecipe = 'mdadm' | 1254 | testrecipe = 'minicom' |
1230 | bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) | 1255 | bb_vars = get_bb_vars(['FILE', 'SRC_URI'], testrecipe) |
1231 | recipefile = bb_vars['FILE'] | 1256 | recipefile = bb_vars['FILE'] |
1232 | src_uri = bb_vars['SRC_URI'] | 1257 | src_uri = bb_vars['SRC_URI'] |
@@ -1244,7 +1269,7 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1244 | # Check git repo | 1269 | # Check git repo |
1245 | self._check_src_repo(tempsrcdir) | 1270 | self._check_src_repo(tempsrcdir) |
1246 | # Add a commit | 1271 | # Add a commit |
1247 | result = runCmd("sed 's!\\(#define VERSION\\W*\"[^\"]*\\)\"!\\1-custom\"!' -i ReadMe.c", cwd=tempsrcdir) | 1272 | result = runCmd('echo "Additional line" >> README', cwd=tempsrcdir) |
1248 | result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) | 1273 | result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) |
1249 | self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe)) | 1274 | self.add_command_to_tearDown('cd %s; rm -f %s/*.patch; git checkout .' % (os.path.dirname(recipefile), testrecipe)) |
1250 | # Create a temporary layer and add it to bblayers.conf | 1275 | # Create a temporary layer and add it to bblayers.conf |
@@ -1274,7 +1299,7 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1274 | with open(bbappendfile, 'r') as f: | 1299 | with open(bbappendfile, 'r') as f: |
1275 | self.assertEqual(expectedlines, f.readlines()) | 1300 | self.assertEqual(expectedlines, f.readlines()) |
1276 | # Drop new commit and check patch gets deleted | 1301 | # Drop new commit and check patch gets deleted |
1277 | result = runCmd('git reset HEAD^', cwd=tempsrcdir) | 1302 | result = runCmd('git reset HEAD^ --hard', cwd=tempsrcdir) |
1278 | result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) | 1303 | result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) |
1279 | self.assertNotExists(patchfile, 'Patch file not deleted') | 1304 | self.assertNotExists(patchfile, 'Patch file not deleted') |
1280 | expectedlines2 = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', | 1305 | expectedlines2 = ['FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"\n', |
@@ -1283,6 +1308,7 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1283 | self.assertEqual(expectedlines2, f.readlines()) | 1308 | self.assertEqual(expectedlines2, f.readlines()) |
1284 | # Put commit back and check we can run it if layer isn't in bblayers.conf | 1309 | # Put commit back and check we can run it if layer isn't in bblayers.conf |
1285 | os.remove(bbappendfile) | 1310 | os.remove(bbappendfile) |
1311 | result = runCmd('echo "Additional line" >> README', cwd=tempsrcdir) | ||
1286 | result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) | 1312 | result = runCmd('git commit -a -m "Add our custom version"', cwd=tempsrcdir) |
1287 | result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) | 1313 | result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) |
1288 | result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) | 1314 | result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) |
@@ -1357,7 +1383,7 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1357 | with open(bbappendfile, 'r') as f: | 1383 | with open(bbappendfile, 'r') as f: |
1358 | self.assertEqual(expectedlines, set(f.readlines())) | 1384 | self.assertEqual(expectedlines, set(f.readlines())) |
1359 | # Drop new commit and check SRCREV changes | 1385 | # Drop new commit and check SRCREV changes |
1360 | result = runCmd('git reset HEAD^', cwd=tempsrcdir) | 1386 | result = runCmd('git reset HEAD^ --hard', cwd=tempsrcdir) |
1361 | result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) | 1387 | result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) |
1362 | self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created') | 1388 | self.assertNotExists(os.path.join(appenddir, testrecipe), 'Patch directory should not be created') |
1363 | result = runCmd('git rev-parse HEAD', cwd=tempsrcdir) | 1389 | result = runCmd('git rev-parse HEAD', cwd=tempsrcdir) |
@@ -1369,6 +1395,7 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1369 | self.assertEqual(expectedlines, set(f.readlines())) | 1395 | self.assertEqual(expectedlines, set(f.readlines())) |
1370 | # Put commit back and check we can run it if layer isn't in bblayers.conf | 1396 | # Put commit back and check we can run it if layer isn't in bblayers.conf |
1371 | os.remove(bbappendfile) | 1397 | os.remove(bbappendfile) |
1398 | result = runCmd('echo "# Additional line" >> Makefile.am', cwd=tempsrcdir) | ||
1372 | result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir) | 1399 | result = runCmd('git commit -a -m "Change the Makefile"', cwd=tempsrcdir) |
1373 | result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) | 1400 | result = runCmd('bitbake-layers remove-layer %s' % templayerdir, cwd=self.builddir) |
1374 | result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) | 1401 | result = runCmd('devtool update-recipe -m srcrev %s -a %s' % (testrecipe, templayerdir)) |
@@ -1400,22 +1427,39 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1400 | # Try building just to ensure we haven't broken that | 1427 | # Try building just to ensure we haven't broken that |
1401 | bitbake("%s" % testrecipe) | 1428 | bitbake("%s" % testrecipe) |
1402 | # Edit / commit local source | 1429 | # Edit / commit local source |
1403 | runCmd('echo "/* Foobar */" >> oe-local-files/makedevs.c', cwd=tempdir) | 1430 | runCmd('echo "/* Foobar */" >> makedevs.c', cwd=tempdir) |
1404 | runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir) | 1431 | runCmd('echo "Foo" > new-local', cwd=tempdir) |
1405 | runCmd('echo "Bar" > new-file', cwd=tempdir) | 1432 | runCmd('echo "Bar" > new-file', cwd=tempdir) |
1406 | runCmd('git add new-file', cwd=tempdir) | 1433 | runCmd('git add new-file', cwd=tempdir) |
1407 | runCmd('git commit -m "Add new file"', cwd=tempdir) | 1434 | runCmd('git commit -m "Add new file"', cwd=tempdir) |
1408 | self.add_command_to_tearDown('cd %s; git clean -fd .; git checkout .' % | 1435 | runCmd('git add new-local', cwd=tempdir) |
1409 | os.path.dirname(recipefile)) | ||
1410 | runCmd('devtool update-recipe %s' % testrecipe) | 1436 | runCmd('devtool update-recipe %s' % testrecipe) |
1411 | expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), | 1437 | expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), |
1412 | (' M', '.*/makedevs/makedevs.c$'), | 1438 | (' M', '.*/makedevs/makedevs.c$'), |
1413 | ('??', '.*/makedevs/new-local$'), | 1439 | ('??', '.*/makedevs/new-local$'), |
1414 | ('??', '.*/makedevs/0001-Add-new-file.patch$')] | 1440 | ('??', '.*/makedevs/0001-Add-new-file.patch$')] |
1415 | self._check_repo_status(os.path.dirname(recipefile), expected_status) | 1441 | self._check_repo_status(os.path.dirname(recipefile), expected_status) |
1416 | 1442 | # Now try to update recipe in another layer, so first, clean it | |
1417 | def test_devtool_update_recipe_local_files_2(self): | 1443 | runCmd('cd %s; git clean -fd .; git checkout .' % os.path.dirname(recipefile)) |
1418 | """Check local source files support when oe-local-files is in Git""" | 1444 | # Create a temporary layer and add it to bblayers.conf |
1445 | self._create_temp_layer(templayerdir, True, 'templayer') | ||
1446 | # Update recipe in templayer | ||
1447 | result = runCmd('devtool update-recipe %s -a %s' % (testrecipe, templayerdir)) | ||
1448 | self.assertNotIn('WARNING:', result.output) | ||
1449 | # Check recipe is still clean | ||
1450 | self._check_repo_status(os.path.dirname(recipefile), []) | ||
1451 | splitpath = os.path.dirname(recipefile).split(os.sep) | ||
1452 | appenddir = os.path.join(templayerdir, splitpath[-2], splitpath[-1]) | ||
1453 | bbappendfile = self._check_bbappend(testrecipe, recipefile, appenddir) | ||
1454 | patchfile = os.path.join(appenddir, testrecipe, '0001-Add-new-file.patch') | ||
1455 | new_local_file = os.path.join(appenddir, testrecipe, 'new_local') | ||
1456 | local_file = os.path.join(appenddir, testrecipe, 'makedevs.c') | ||
1457 | self.assertExists(patchfile, 'Patch file 0001-Add-new-file.patch not created') | ||
1458 | self.assertExists(local_file, 'File makedevs.c not created') | ||
1459 | self.assertExists(patchfile, 'File new_local not created') | ||
1460 | |||
1461 | def _test_devtool_update_recipe_local_files_2(self): | ||
1462 | """Check local source files support when editing local files in Git""" | ||
1419 | testrecipe = 'devtool-test-local' | 1463 | testrecipe = 'devtool-test-local' |
1420 | recipefile = get_bb_var('FILE', testrecipe) | 1464 | recipefile = get_bb_var('FILE', testrecipe) |
1421 | recipedir = os.path.dirname(recipefile) | 1465 | recipedir = os.path.dirname(recipefile) |
@@ -1430,17 +1474,13 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1430 | result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) | 1474 | result = runCmd('devtool modify %s -x %s' % (testrecipe, tempdir)) |
1431 | # Check git repo | 1475 | # Check git repo |
1432 | self._check_src_repo(tempdir) | 1476 | self._check_src_repo(tempdir) |
1433 | # Add oe-local-files to Git | ||
1434 | runCmd('rm oe-local-files/.gitignore', cwd=tempdir) | ||
1435 | runCmd('git add oe-local-files', cwd=tempdir) | ||
1436 | runCmd('git commit -m "Add local sources"', cwd=tempdir) | ||
1437 | # Edit / commit local sources | 1477 | # Edit / commit local sources |
1438 | runCmd('echo "# Foobar" >> oe-local-files/file1', cwd=tempdir) | 1478 | runCmd('echo "# Foobar" >> file1', cwd=tempdir) |
1439 | runCmd('git commit -am "Edit existing file"', cwd=tempdir) | 1479 | runCmd('git commit -am "Edit existing file"', cwd=tempdir) |
1440 | runCmd('git rm oe-local-files/file2', cwd=tempdir) | 1480 | runCmd('git rm file2', cwd=tempdir) |
1441 | runCmd('git commit -m"Remove file"', cwd=tempdir) | 1481 | runCmd('git commit -m"Remove file"', cwd=tempdir) |
1442 | runCmd('echo "Foo" > oe-local-files/new-local', cwd=tempdir) | 1482 | runCmd('echo "Foo" > new-local', cwd=tempdir) |
1443 | runCmd('git add oe-local-files/new-local', cwd=tempdir) | 1483 | runCmd('git add new-local', cwd=tempdir) |
1444 | runCmd('git commit -m "Add new local file"', cwd=tempdir) | 1484 | runCmd('git commit -m "Add new local file"', cwd=tempdir) |
1445 | runCmd('echo "Gar" > new-file', cwd=tempdir) | 1485 | runCmd('echo "Gar" > new-file', cwd=tempdir) |
1446 | runCmd('git add new-file', cwd=tempdir) | 1486 | runCmd('git add new-file', cwd=tempdir) |
@@ -1449,7 +1489,7 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1449 | os.path.dirname(recipefile)) | 1489 | os.path.dirname(recipefile)) |
1450 | # Checkout unmodified file to working copy -> devtool should still pick | 1490 | # Checkout unmodified file to working copy -> devtool should still pick |
1451 | # the modified version from HEAD | 1491 | # the modified version from HEAD |
1452 | runCmd('git checkout HEAD^ -- oe-local-files/file1', cwd=tempdir) | 1492 | runCmd('git checkout HEAD^ -- file1', cwd=tempdir) |
1453 | runCmd('devtool update-recipe %s' % testrecipe) | 1493 | runCmd('devtool update-recipe %s' % testrecipe) |
1454 | expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), | 1494 | expected_status = [(' M', '.*/%s$' % os.path.basename(recipefile)), |
1455 | (' M', '.*/file1$'), | 1495 | (' M', '.*/file1$'), |
@@ -1524,7 +1564,7 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1524 | # (don't bother with cleaning the recipe on teardown, we won't be building it) | 1564 | # (don't bother with cleaning the recipe on teardown, we won't be building it) |
1525 | result = runCmd('devtool modify %s' % testrecipe) | 1565 | result = runCmd('devtool modify %s' % testrecipe) |
1526 | # Modify one file | 1566 | # Modify one file |
1527 | runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe, 'oe-local-files')) | 1567 | runCmd('echo "Another line" >> file2', cwd=os.path.join(self.workspacedir, 'sources', testrecipe)) |
1528 | self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) | 1568 | self.add_command_to_tearDown('cd %s; rm %s/*; git checkout %s %s' % (os.path.dirname(recipefile), testrecipe, testrecipe, os.path.basename(recipefile))) |
1529 | result = runCmd('devtool update-recipe %s' % testrecipe) | 1569 | result = runCmd('devtool update-recipe %s' % testrecipe) |
1530 | expected_status = [(' M', '.*/%s/file2$' % testrecipe)] | 1570 | expected_status = [(' M', '.*/%s/file2$' % testrecipe)] |
@@ -1587,12 +1627,12 @@ class DevtoolUpdateTests(DevtoolBase): | |||
1587 | # Check preconditions | 1627 | # Check preconditions |
1588 | testrecipe = 'dos2unix' | 1628 | testrecipe = 'dos2unix' |
1589 | self.append_config('ERROR_QA:remove:pn-dos2unix = "patch-status"\n') | 1629 | self.append_config('ERROR_QA:remove:pn-dos2unix = "patch-status"\n') |
1590 | bb_vars = get_bb_vars(['SRC_URI', 'S', 'WORKDIR', 'FILE'], testrecipe) | 1630 | bb_vars = get_bb_vars(['SRC_URI', 'S', 'UNPACKDIR', 'FILE', 'BB_GIT_DEFAULT_DESTSUFFIX'], testrecipe) |
1591 | self.assertIn('git://', bb_vars['SRC_URI'], 'This test expects the %s recipe to be a git recipe' % testrecipe) | 1631 | self.assertIn('git://', bb_vars['SRC_URI'], 'This test expects the %s recipe to be a git recipe' % testrecipe) |
1592 | workdir_git = '%s/git/' % bb_vars['WORKDIR'] | 1632 | unpackdir_git = '%s/%s/' % (bb_vars['UNPACKDIR'], bb_vars['BB_GIT_DEFAULT_DESTSUFFIX']) |
1593 | if not bb_vars['S'].startswith(workdir_git): | 1633 | if not bb_vars['S'].startswith(unpackdir_git): |
1594 | self.fail('This test expects the %s recipe to be building from a subdirectory of the git repo' % testrecipe) | 1634 | self.fail('This test expects the %s recipe to be building from a subdirectory of the git repo' % testrecipe) |
1595 | subdir = bb_vars['S'].split(workdir_git, 1)[1] | 1635 | subdir = bb_vars['S'].split(unpackdir_git, 1)[1] |
1596 | # Clean up anything in the workdir/sysroot/sstate cache | 1636 | # Clean up anything in the workdir/sysroot/sstate cache |
1597 | bitbake('%s -c cleansstate' % testrecipe) | 1637 | bitbake('%s -c cleansstate' % testrecipe) |
1598 | # Try modifying a recipe | 1638 | # Try modifying a recipe |
@@ -1720,6 +1760,8 @@ class DevtoolExtractTests(DevtoolBase): | |||
1720 | self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found') | 1760 | self.assertExists(os.path.join(tempdir, 'Makefile.am'), 'Extracted source could not be found') |
1721 | self._check_src_repo(tempdir) | 1761 | self._check_src_repo(tempdir) |
1722 | 1762 | ||
1763 | class DevtoolResetTests(DevtoolBase): | ||
1764 | |||
1723 | def test_devtool_reset_all(self): | 1765 | def test_devtool_reset_all(self): |
1724 | tempdir = tempfile.mkdtemp(prefix='devtoolqa') | 1766 | tempdir = tempfile.mkdtemp(prefix='devtoolqa') |
1725 | self.track_for_cleanup(tempdir) | 1767 | self.track_for_cleanup(tempdir) |
@@ -1746,6 +1788,21 @@ class DevtoolExtractTests(DevtoolBase): | |||
1746 | matches2 = glob.glob(stampprefix2 + '*') | 1788 | matches2 = glob.glob(stampprefix2 + '*') |
1747 | self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2) | 1789 | self.assertFalse(matches2, 'Stamp files exist for recipe %s that should have been cleaned' % testrecipe2) |
1748 | 1790 | ||
1791 | def test_devtool_reset_re_plus_plus(self): | ||
1792 | tempdir = tempfile.mkdtemp(prefix='devtoolqa') | ||
1793 | self.track_for_cleanup(tempdir) | ||
1794 | self.track_for_cleanup(self.workspacedir) | ||
1795 | self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') | ||
1796 | testrecipe = 'devtool-test-reset-re++' | ||
1797 | result = runCmd('devtool modify %s' % testrecipe) | ||
1798 | result = runCmd('devtool reset -n %s' % testrecipe) | ||
1799 | self.assertIn(testrecipe, result.output) | ||
1800 | result = runCmd('devtool status') | ||
1801 | self.assertNotIn(testrecipe, result.output) | ||
1802 | self.assertNotExists(os.path.join(self.workspacedir, 'recipes', testrecipe), 'Recipe directory should not exist after resetting') | ||
1803 | |||
1804 | class DevtoolDeployTargetTests(DevtoolBase): | ||
1805 | |||
1749 | @OETestTag("runqemu") | 1806 | @OETestTag("runqemu") |
1750 | def test_devtool_deploy_target(self): | 1807 | def test_devtool_deploy_target(self): |
1751 | self._check_runqemu_prerequisites() | 1808 | self._check_runqemu_prerequisites() |
@@ -1753,6 +1810,8 @@ class DevtoolExtractTests(DevtoolBase): | |||
1753 | # Definitions | 1810 | # Definitions |
1754 | testrecipe = 'mdadm' | 1811 | testrecipe = 'mdadm' |
1755 | testfile = '/sbin/mdadm' | 1812 | testfile = '/sbin/mdadm' |
1813 | if "usrmerge" in get_bb_var('DISTRO_FEATURES'): | ||
1814 | testfile = '/usr/sbin/mdadm' | ||
1756 | testimage = 'oe-selftest-image' | 1815 | testimage = 'oe-selftest-image' |
1757 | testcommand = '/sbin/mdadm --help' | 1816 | testcommand = '/sbin/mdadm --help' |
1758 | # Build an image to run | 1817 | # Build an image to run |
@@ -1811,6 +1870,8 @@ class DevtoolExtractTests(DevtoolBase): | |||
1811 | result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True) | 1870 | result = runCmd('ssh %s root@%s %s' % (sshargs, qemu.ip, testcommand), ignore_status=True) |
1812 | self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have') | 1871 | self.assertNotEqual(result, 0, 'undeploy-target did not remove command as it should have') |
1813 | 1872 | ||
1873 | class DevtoolBuildImageTests(DevtoolBase): | ||
1874 | |||
1814 | def test_devtool_build_image(self): | 1875 | def test_devtool_build_image(self): |
1815 | """Test devtool build-image plugin""" | 1876 | """Test devtool build-image plugin""" |
1816 | # Check preconditions | 1877 | # Check preconditions |
@@ -1982,6 +2043,52 @@ class DevtoolUpgradeTests(DevtoolBase): | |||
1982 | newlines = f.readlines() | 2043 | newlines = f.readlines() |
1983 | self.assertEqual(desiredlines, newlines) | 2044 | self.assertEqual(desiredlines, newlines) |
1984 | 2045 | ||
2046 | def test_devtool_upgrade_recipe_upgrade_extra_tasks(self): | ||
2047 | # Check preconditions | ||
2048 | self.assertTrue(not os.path.exists(self.workspacedir), 'This test cannot be run with a workspace directory under the build directory') | ||
2049 | self.track_for_cleanup(self.workspacedir) | ||
2050 | self.add_command_to_tearDown('bitbake-layers remove-layer */workspace') | ||
2051 | recipe = 'python3-guessing-game' | ||
2052 | version = '0.2.0' | ||
2053 | commit = '40cf004c2772ffa20ea803fa3be1528a75be3e98' | ||
2054 | oldrecipefile = get_bb_var('FILE', recipe) | ||
2055 | oldcratesincfile = os.path.join(os.path.dirname(oldrecipefile), os.path.basename(oldrecipefile).strip('_git.bb') + '-crates.inc') | ||
2056 | tempdir = tempfile.mkdtemp(prefix='devtoolqa') | ||
2057 | self.track_for_cleanup(tempdir) | ||
2058 | # Check that recipe is not already under devtool control | ||
2059 | result = runCmd('devtool status') | ||
2060 | self.assertNotIn(recipe, result.output) | ||
2061 | # Check upgrade | ||
2062 | result = runCmd('devtool upgrade %s %s --version %s --srcrev %s' % (recipe, tempdir, version, commit)) | ||
2063 | # Check if srctree at least is populated | ||
2064 | self.assertTrue(len(os.listdir(tempdir)) > 0, 'srctree (%s) should be populated with new (%s) source code' % (tempdir, commit)) | ||
2065 | # Check new recipe file and new -crates.inc files are present | ||
2066 | newrecipefile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldrecipefile)) | ||
2067 | newcratesincfile = os.path.join(self.workspacedir, 'recipes', recipe, os.path.basename(oldcratesincfile)) | ||
2068 | self.assertExists(newrecipefile, 'Recipe file should exist after upgrade') | ||
2069 | self.assertExists(newcratesincfile, 'Recipe crates.inc file should exist after upgrade') | ||
2070 | # Check devtool status and make sure recipe is present | ||
2071 | result = runCmd('devtool status') | ||
2072 | self.assertIn(recipe, result.output) | ||
2073 | self.assertIn(tempdir, result.output) | ||
2074 | # Check recipe got changed as expected | ||
2075 | with open(oldrecipefile + '.upgraded', 'r') as f: | ||
2076 | desiredlines = f.readlines() | ||
2077 | with open(newrecipefile, 'r') as f: | ||
2078 | newlines = f.readlines() | ||
2079 | self.assertEqual(desiredlines, newlines) | ||
2080 | # Check crates.inc got changed as expected | ||
2081 | with open(oldcratesincfile + '.upgraded', 'r') as f: | ||
2082 | desiredlines = f.readlines() | ||
2083 | with open(newcratesincfile, 'r') as f: | ||
2084 | newlines = f.readlines() | ||
2085 | self.assertEqual(desiredlines, newlines) | ||
2086 | # Check devtool reset recipe | ||
2087 | result = runCmd('devtool reset %s -n' % recipe) | ||
2088 | result = runCmd('devtool status') | ||
2089 | self.assertNotIn(recipe, result.output) | ||
2090 | self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipe), 'Recipe directory should not exist after resetting') | ||
2091 | |||
1985 | def test_devtool_layer_plugins(self): | 2092 | def test_devtool_layer_plugins(self): |
1986 | """Test that devtool can use plugins from other layers. | 2093 | """Test that devtool can use plugins from other layers. |
1987 | 2094 | ||
@@ -2307,7 +2414,7 @@ class DevtoolUpgradeTests(DevtoolBase): | |||
2307 | newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename) | 2414 | newsrctree = os.path.join(self.workspacedir, 'sources', newrecipename) |
2308 | self.assertExists(newsrctree, 'Source directory not renamed') | 2415 | self.assertExists(newsrctree, 'Source directory not renamed') |
2309 | checkvars = {} | 2416 | checkvars = {} |
2310 | checkvars['S'] = '${WORKDIR}/%s-%s' % (recipename, recipever) | 2417 | checkvars['S'] = '${UNPACKDIR}/%s-%s' % (recipename, recipever) |
2311 | checkvars['SRC_URI'] = url | 2418 | checkvars['SRC_URI'] = url |
2312 | self._test_recipe_contents(newrecipefile, checkvars, []) | 2419 | self._test_recipe_contents(newrecipefile, checkvars, []) |
2313 | # Try again - change just name this time | 2420 | # Try again - change just name this time |
@@ -2319,7 +2426,7 @@ class DevtoolUpgradeTests(DevtoolBase): | |||
2319 | self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists') | 2426 | self.assertNotExists(os.path.join(self.workspacedir, 'recipes', recipename), 'Old recipe directory still exists') |
2320 | self.assertExists(os.path.join(self.workspacedir, 'sources', newrecipename), 'Source directory not renamed') | 2427 | self.assertExists(os.path.join(self.workspacedir, 'sources', newrecipename), 'Source directory not renamed') |
2321 | checkvars = {} | 2428 | checkvars = {} |
2322 | checkvars['S'] = '${WORKDIR}/%s-${PV}' % recipename | 2429 | checkvars['S'] = '${UNPACKDIR}/%s-${PV}' % recipename |
2323 | checkvars['SRC_URI'] = url.replace(recipever, '${PV}') | 2430 | checkvars['SRC_URI'] = url.replace(recipever, '${PV}') |
2324 | self._test_recipe_contents(newrecipefile, checkvars, []) | 2431 | self._test_recipe_contents(newrecipefile, checkvars, []) |
2325 | # Try again - change just version this time | 2432 | # Try again - change just version this time |
@@ -2330,7 +2437,7 @@ class DevtoolUpgradeTests(DevtoolBase): | |||
2330 | self.assertExists(newrecipefile, 'Recipe file not renamed') | 2437 | self.assertExists(newrecipefile, 'Recipe file not renamed') |
2331 | self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory no longer exists') | 2438 | self.assertExists(os.path.join(self.workspacedir, 'sources', recipename), 'Source directory no longer exists') |
2332 | checkvars = {} | 2439 | checkvars = {} |
2333 | checkvars['S'] = '${WORKDIR}/${BPN}-%s' % recipever | 2440 | checkvars['S'] = '${UNPACKDIR}/${BPN}-%s' % recipever |
2334 | checkvars['SRC_URI'] = url | 2441 | checkvars['SRC_URI'] = url |
2335 | self._test_recipe_contents(newrecipefile, checkvars, []) | 2442 | self._test_recipe_contents(newrecipefile, checkvars, []) |
2336 | 2443 | ||
@@ -2452,7 +2559,7 @@ class DevtoolIdeSdkTests(DevtoolBase): | |||
2452 | self.track_for_cleanup(tempdir) | 2559 | self.track_for_cleanup(tempdir) |
2453 | self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name) | 2560 | self.add_command_to_tearDown('bitbake -c clean %s' % recipe_name) |
2454 | 2561 | ||
2455 | result = runCmd('devtool modify %s -x %s' % (recipe_name, tempdir)) | 2562 | result = runCmd('devtool modify %s -x %s --debug-build' % (recipe_name, tempdir)) |
2456 | self.assertExists(os.path.join(tempdir, build_file), | 2563 | self.assertExists(os.path.join(tempdir, build_file), |
2457 | 'Extracted source could not be found') | 2564 | 'Extracted source could not be found') |
2458 | self.assertExists(os.path.join(self.workspacedir, 'conf', | 2565 | self.assertExists(os.path.join(self.workspacedir, 'conf', |
@@ -2504,11 +2611,6 @@ class DevtoolIdeSdkTests(DevtoolBase): | |||
2504 | i_and_d_script_path = os.path.join( | 2611 | i_and_d_script_path = os.path.join( |
2505 | self._workspace_scripts_dir(recipe_name), i_and_d_script) | 2612 | self._workspace_scripts_dir(recipe_name), i_and_d_script) |
2506 | self.assertExists(i_and_d_script_path) | 2613 | self.assertExists(i_and_d_script_path) |
2507 | del_script = "delete_package_dirs_" + recipe_id | ||
2508 | del_script_path = os.path.join( | ||
2509 | self._workspace_scripts_dir(recipe_name), del_script) | ||
2510 | self.assertExists(del_script_path) | ||
2511 | runCmd(del_script_path, cwd=tempdir) | ||
2512 | 2614 | ||
2513 | def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe): | 2615 | def _devtool_ide_sdk_qemu(self, tempdir, qemu, recipe_name, example_exe): |
2514 | """Verify deployment and execution in Qemu system work for one recipe. | 2616 | """Verify deployment and execution in Qemu system work for one recipe. |
diff --git a/meta/lib/oeqa/selftest/cases/distrodata.py b/meta/lib/oeqa/selftest/cases/distrodata.py index ad952c004b..f2c6124d70 100644 --- a/meta/lib/oeqa/selftest/cases/distrodata.py +++ b/meta/lib/oeqa/selftest/cases/distrodata.py | |||
@@ -20,10 +20,10 @@ class Distrodata(OESelftestTestCase): | |||
20 | feature = 'LICENSE_FLAGS_ACCEPTED += " commercial"\n' | 20 | feature = 'LICENSE_FLAGS_ACCEPTED += " commercial"\n' |
21 | self.write_config(feature) | 21 | self.write_config(feature) |
22 | 22 | ||
23 | pkgs = oe.recipeutils.get_recipe_upgrade_status() | 23 | pkggroups = oe.recipeutils.get_recipe_upgrade_status() |
24 | 24 | ||
25 | regressed_failures = [pkg[0] for pkg in pkgs if pkg[1] == 'UNKNOWN_BROKEN'] | 25 | regressed_failures = [pkg['pn'] for pkgs in pkggroups for pkg in pkgs if pkg['status'] == 'UNKNOWN_BROKEN'] |
26 | regressed_successes = [pkg[0] for pkg in pkgs if pkg[1] == 'KNOWN_BROKEN'] | 26 | regressed_successes = [pkg['pn'] for pkgs in pkggroups for pkg in pkgs if pkg['status'] == 'KNOWN_BROKEN'] |
27 | msg = "" | 27 | msg = "" |
28 | if len(regressed_failures) > 0: | 28 | if len(regressed_failures) > 0: |
29 | msg = msg + """ | 29 | msg = msg + """ |
@@ -55,8 +55,8 @@ but their recipes claim otherwise by setting UPSTREAM_VERSION_UNKNOWN. Please re | |||
55 | return False | 55 | return False |
56 | 56 | ||
57 | def is_maintainer_exception(entry): | 57 | def is_maintainer_exception(entry): |
58 | exceptions = ["musl", "newlib", "linux-yocto", "linux-dummy", "mesa-gl", "libgfortran", "libx11-compose-data", | 58 | exceptions = ["musl", "newlib", "picolibc", "linux-yocto", "linux-dummy", "mesa-gl", "libgfortran", "libx11-compose-data", |
59 | "cve-update-nvd2-native",] | 59 | "cve-update-nvd2-native", "barebox", "libglvnd"] |
60 | for i in exceptions: | 60 | for i in exceptions: |
61 | if i in entry: | 61 | if i in entry: |
62 | return True | 62 | return True |
@@ -115,3 +115,15 @@ The list of oe-core recipes with maintainers is empty. This may indicate that th | |||
115 | self.fail(""" | 115 | self.fail(""" |
116 | Unable to find recipes for the following entries in maintainers.inc: | 116 | Unable to find recipes for the following entries in maintainers.inc: |
117 | """ + "\n".join(['%s' % i for i in missing_recipes])) | 117 | """ + "\n".join(['%s' % i for i in missing_recipes])) |
118 | |||
119 | def test_common_include_recipes(self): | ||
120 | """ | ||
121 | Summary: Test that obtaining recipes that share includes between them returns a sane result | ||
122 | Expected: At least cmake and qemu entries are present in the output | ||
123 | Product: oe-core | ||
124 | Author: Alexander Kanavin <alex.kanavin@gmail.com> | ||
125 | """ | ||
126 | recipes = oe.recipeutils.get_common_include_recipes() | ||
127 | |||
128 | self.assertIn({'qemu-system-native', 'qemu', 'qemu-native'}, recipes) | ||
129 | self.assertIn({'cmake-native', 'cmake'}, recipes) | ||
diff --git a/meta/lib/oeqa/selftest/cases/efibootpartition.py b/meta/lib/oeqa/selftest/cases/efibootpartition.py index fa74103dec..fcfcdaf7e4 100644 --- a/meta/lib/oeqa/selftest/cases/efibootpartition.py +++ b/meta/lib/oeqa/selftest/cases/efibootpartition.py | |||
@@ -6,7 +6,7 @@ | |||
6 | # | 6 | # |
7 | 7 | ||
8 | from oeqa.selftest.case import OESelftestTestCase | 8 | from oeqa.selftest.case import OESelftestTestCase |
9 | from oeqa.utils.commands import bitbake, runqemu | 9 | from oeqa.utils.commands import bitbake, runqemu, get_bb_var |
10 | from oeqa.core.decorator.data import skipIfNotMachine | 10 | from oeqa.core.decorator.data import skipIfNotMachine |
11 | import oe.types | 11 | import oe.types |
12 | 12 | ||
@@ -14,17 +14,18 @@ class GenericEFITest(OESelftestTestCase): | |||
14 | """EFI booting test class""" | 14 | """EFI booting test class""" |
15 | @skipIfNotMachine("qemux86-64", "test is qemux86-64 specific currently") | 15 | @skipIfNotMachine("qemux86-64", "test is qemux86-64 specific currently") |
16 | def test_boot_efi(self): | 16 | def test_boot_efi(self): |
17 | cmd = "runqemu nographic serial wic ovmf" | 17 | image = "core-image-minimal" |
18 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', image) or "" | ||
19 | cmd = "runqemu %s nographic serial wic ovmf" % (runqemu_params) | ||
18 | if oe.types.qemu_use_kvm(self.td.get('QEMU_USE_KVM', 0), self.td["TARGET_ARCH"]): | 20 | if oe.types.qemu_use_kvm(self.td.get('QEMU_USE_KVM', 0), self.td["TARGET_ARCH"]): |
19 | cmd += " kvm" | 21 | cmd += " kvm" |
20 | image = "core-image-minimal" | ||
21 | 22 | ||
22 | self.write_config(""" | 23 | self.write_config(""" |
23 | EFI_PROVIDER = "systemd-boot" | 24 | EFI_PROVIDER = "grub-efi" |
24 | IMAGE_FSTYPES:pn-%s:append = " wic" | 25 | IMAGE_FSTYPES:pn-%s:append = " wic" |
25 | MACHINE_FEATURES:append = " efi" | 26 | MACHINE_FEATURES:append = " efi" |
26 | WKS_FILE = "efi-bootdisk.wks.in" | 27 | WKS_FILE = "efi-bootdisk.wks.in" |
27 | IMAGE_INSTALL:append = " grub-efi systemd-boot kernel-image-bzimage" | 28 | IMAGE_INSTALL:append = " grub-efi kernel-image-bzimage" |
28 | """ | 29 | """ |
29 | % (image)) | 30 | % (image)) |
30 | 31 | ||
diff --git a/meta/lib/oeqa/selftest/cases/esdk.py b/meta/lib/oeqa/selftest/cases/esdk.py index 9f5de2cde7..7a5fe00a08 100644 --- a/meta/lib/oeqa/selftest/cases/esdk.py +++ b/meta/lib/oeqa/selftest/cases/esdk.py | |||
@@ -27,11 +27,7 @@ class oeSDKExtSelfTest(OESelftestTestCase): | |||
27 | return glob.glob(pattern)[0] | 27 | return glob.glob(pattern)[0] |
28 | 28 | ||
29 | @staticmethod | 29 | @staticmethod |
30 | def run_esdk_cmd(env_eSDK, tmpdir_eSDKQA, cmd, postconfig=None, **options): | 30 | def run_esdk_cmd(env_eSDK, tmpdir_eSDKQA, cmd, **options): |
31 | if postconfig: | ||
32 | esdk_conf_file = os.path.join(tmpdir_eSDKQA, 'conf', 'local.conf') | ||
33 | with open(esdk_conf_file, 'a+') as f: | ||
34 | f.write(postconfig) | ||
35 | if not options: | 31 | if not options: |
36 | options = {} | 32 | options = {} |
37 | if not 'shell' in options: | 33 | if not 'shell' in options: |
diff --git a/meta/lib/oeqa/selftest/cases/fetch.py b/meta/lib/oeqa/selftest/cases/fetch.py index 44099176fc..1beef5cfed 100644 --- a/meta/lib/oeqa/selftest/cases/fetch.py +++ b/meta/lib/oeqa/selftest/cases/fetch.py | |||
@@ -74,8 +74,8 @@ class Dependencies(OESelftestTestCase): | |||
74 | tinfoil.prepare(config_only=False, quiet=2) | 74 | tinfoil.prepare(config_only=False, quiet=2) |
75 | 75 | ||
76 | r = """ | 76 | r = """ |
77 | LICENSE="CLOSED" | 77 | LICENSE = "CLOSED" |
78 | SRC_URI="http://example.com/tarball.zip" | 78 | SRC_URI = "http://example.com/tarball.zip" |
79 | """ | 79 | """ |
80 | f = self.write_recipe(textwrap.dedent(r), tempdir) | 80 | f = self.write_recipe(textwrap.dedent(r), tempdir) |
81 | d = tinfoil.parse_recipe_file(f) | 81 | d = tinfoil.parse_recipe_file(f) |
@@ -84,8 +84,8 @@ class Dependencies(OESelftestTestCase): | |||
84 | 84 | ||
85 | # Verify that the downloadfilename overrides the URI | 85 | # Verify that the downloadfilename overrides the URI |
86 | r = """ | 86 | r = """ |
87 | LICENSE="CLOSED" | 87 | LICENSE = "CLOSED" |
88 | SRC_URI="https://example.com/tarball;downloadfilename=something.zip" | 88 | SRC_URI = "https://example.com/tarball;downloadfilename=something.zip" |
89 | """ | 89 | """ |
90 | f = self.write_recipe(textwrap.dedent(r), tempdir) | 90 | f = self.write_recipe(textwrap.dedent(r), tempdir) |
91 | d = tinfoil.parse_recipe_file(f) | 91 | d = tinfoil.parse_recipe_file(f) |
@@ -93,8 +93,8 @@ class Dependencies(OESelftestTestCase): | |||
93 | self.assertIn("unzip-native", d.getVarFlag("do_unpack", "depends") or "") | 93 | self.assertIn("unzip-native", d.getVarFlag("do_unpack", "depends") or "") |
94 | 94 | ||
95 | r = """ | 95 | r = """ |
96 | LICENSE="CLOSED" | 96 | LICENSE = "CLOSED" |
97 | SRC_URI="ftp://example.com/tarball.lz" | 97 | SRC_URI = "ftp://example.com/tarball.lz" |
98 | """ | 98 | """ |
99 | f = self.write_recipe(textwrap.dedent(r), tempdir) | 99 | f = self.write_recipe(textwrap.dedent(r), tempdir) |
100 | d = tinfoil.parse_recipe_file(f) | 100 | d = tinfoil.parse_recipe_file(f) |
@@ -102,8 +102,8 @@ class Dependencies(OESelftestTestCase): | |||
102 | self.assertIn("lzip-native", d.getVarFlag("do_unpack", "depends")) | 102 | self.assertIn("lzip-native", d.getVarFlag("do_unpack", "depends")) |
103 | 103 | ||
104 | r = """ | 104 | r = """ |
105 | LICENSE="CLOSED" | 105 | LICENSE = "CLOSED" |
106 | SRC_URI="git://example.com/repo;branch=master;rev=ffffffffffffffffffffffffffffffffffffffff" | 106 | SRC_URI = "git://example.com/repo;branch=master;rev=ffffffffffffffffffffffffffffffffffffffff" |
107 | """ | 107 | """ |
108 | f = self.write_recipe(textwrap.dedent(r), tempdir) | 108 | f = self.write_recipe(textwrap.dedent(r), tempdir) |
109 | d = tinfoil.parse_recipe_file(f) | 109 | d = tinfoil.parse_recipe_file(f) |
diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py index 347c065377..3c40857747 100644 --- a/meta/lib/oeqa/selftest/cases/fitimage.py +++ b/meta/lib/oeqa/selftest/cases/fitimage.py | |||
@@ -4,12 +4,740 @@ | |||
4 | # SPDX-License-Identifier: MIT | 4 | # SPDX-License-Identifier: MIT |
5 | # | 5 | # |
6 | 6 | ||
7 | from oeqa.selftest.case import OESelftestTestCase | ||
8 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars | ||
9 | import os | 7 | import os |
10 | import re | 8 | import re |
9 | import shlex | ||
10 | import logging | ||
11 | import pprint | ||
12 | import tempfile | ||
13 | |||
14 | import oe.fitimage | ||
15 | |||
16 | from oeqa.selftest.case import OESelftestTestCase | ||
17 | from oeqa.utils.commands import runCmd, bitbake, get_bb_vars, get_bb_var | ||
18 | |||
19 | |||
20 | class BbVarsMockGenKeys: | ||
21 | def __init__(self, keydir, gen_keys="0", sign_enabled="0", keyname="", sign_ind="0", img_keyname=""): | ||
22 | self.bb_vars = { | ||
23 | 'FIT_GENERATE_KEYS': gen_keys, | ||
24 | 'FIT_KEY_GENRSA_ARGS': "-F4", | ||
25 | 'FIT_KEY_REQ_ARGS': "-batch -new", | ||
26 | 'FIT_KEY_SIGN_PKCS': "-x509", | ||
27 | 'FIT_SIGN_INDIVIDUAL': sign_ind, | ||
28 | 'FIT_SIGN_NUMBITS': "2048", | ||
29 | 'UBOOT_SIGN_ENABLE': sign_enabled, | ||
30 | 'UBOOT_SIGN_IMG_KEYNAME': img_keyname, | ||
31 | 'UBOOT_SIGN_KEYDIR': keydir, | ||
32 | 'UBOOT_SIGN_KEYNAME': keyname, | ||
33 | } | ||
34 | |||
35 | def getVar(self, var): | ||
36 | return self.bb_vars[var] | ||
37 | |||
38 | class FitImageTestCase(OESelftestTestCase): | ||
39 | """Test functions usable for testing kernel-fitimage.bbclass and uboot-sign.bbclass | ||
40 | |||
41 | A brief summary showing the structure of a test case: | ||
42 | |||
43 | self._test_fitimage() | ||
44 | # Generate a local.conf file and bitbake the bootloader or the kernel | ||
45 | self._bitbake_fit_image() | ||
46 | |||
47 | # Check if the its file contains the expected paths and attributes. | ||
48 | # The _get_req_* functions are implemented by more specific chield classes. | ||
49 | self._check_its_file() | ||
50 | req_its_paths = self._get_req_its_paths() | ||
51 | req_sigvalues_config = self._get_req_sigvalues_config() | ||
52 | req_sigvalues_image = self._get_req_sigvalues_image() | ||
53 | # Compare the its file against req_its_paths, req_sigvalues_config, req_sigvalues_image | ||
54 | |||
55 | # Call the dumpimage utiliy and check that it prints all the expected paths and attributes | ||
56 | # The _get_req_* functions are implemented by more specific chield classes. | ||
57 | self._check_fitimage() | ||
58 | self._get_req_sections() | ||
59 | # Compare the output of the dumpimage utility against | ||
60 | """ | ||
61 | |||
62 | MKIMAGE_HASH_LENGTHS = { 'sha256': 64, 'sha384': 96, 'sha512': 128 } | ||
63 | MKIMAGE_SIGNATURE_LENGTHS = { 'rsa2048': 512 } | ||
64 | |||
65 | def _gen_signing_key(self, bb_vars): | ||
66 | """Generate a key pair and a singing certificate | ||
67 | |||
68 | Generate a UBOOT_SIGN_KEYNAME in the UBOOT_SIGN_KEYDIR similar to what | ||
69 | the FIT_GENERATE_KEYS feature does. However, having a static key is | ||
70 | probably a more realistic use case than generating a random key with | ||
71 | each clean build. So this needs to be tested as well. | ||
72 | The FIT_GENERATE_KEYS generates 2 keys: The UBOOT_SIGN_KEYNAME and the | ||
73 | UBOOT_SIGN_IMG_KEYNAME. The UBOOT_SIGN_IMG_KEYNAME is used by the | ||
74 | FIT_SIGN_INDIVIDUAL feature only. Testing if everything is working if | ||
75 | there is only one key available is important as well. Therefore this | ||
76 | function generates only the keys which are really needed, not just two. | ||
77 | """ | ||
78 | |||
79 | # Define some variables which are usually defined by the kernel-fitimage.bbclass. | ||
80 | # But for testing purpose check if the uboot-sign.bbclass is independent from | ||
81 | # the kernel-fitimage.bbclass | ||
82 | fit_sign_numbits = bb_vars.get('FIT_SIGN_NUMBITS', "2048") | ||
83 | fit_key_genrsa_args = bb_vars.get('FIT_KEY_GENRSA_ARGS', "-F4") | ||
84 | fit_key_req_args = bb_vars.get('FIT_KEY_REQ_ARGS', "-batch -new") | ||
85 | fit_key_sign_pkcs = bb_vars.get('FIT_KEY_SIGN_PKCS', "-x509") | ||
86 | |||
87 | uboot_sign_keydir = bb_vars['UBOOT_SIGN_KEYDIR'] | ||
88 | sign_keys = [bb_vars['UBOOT_SIGN_KEYNAME']] | ||
89 | if bb_vars['FIT_SIGN_INDIVIDUAL'] == "1": | ||
90 | sign_keys.append(bb_vars['UBOOT_SIGN_IMG_KEYNAME']) | ||
91 | for sign_key in sign_keys: | ||
92 | sing_key_path = os.path.join(uboot_sign_keydir, sign_key) | ||
93 | if not os.path.isdir(uboot_sign_keydir): | ||
94 | os.makedirs(uboot_sign_keydir) | ||
95 | openssl_bindir = FitImageTestCase._setup_native('openssl-native') | ||
96 | openssl_path = os.path.join(openssl_bindir, 'openssl') | ||
97 | runCmd("%s genrsa %s -out %s.key %s" % ( | ||
98 | openssl_path, | ||
99 | fit_key_genrsa_args, | ||
100 | sing_key_path, | ||
101 | fit_sign_numbits | ||
102 | )) | ||
103 | runCmd("%s req %s %s -key %s.key -out %s.crt" % ( | ||
104 | openssl_path, | ||
105 | fit_key_req_args, | ||
106 | fit_key_sign_pkcs, | ||
107 | sing_key_path, | ||
108 | sing_key_path | ||
109 | )) | ||
110 | |||
111 | @staticmethod | ||
112 | def _gen_random_file(file_path, num_bytes=65536): | ||
113 | with open(file_path, 'wb') as file_out: | ||
114 | file_out.write(os.urandom(num_bytes)) | ||
115 | |||
116 | @staticmethod | ||
117 | def _setup_native(native_recipe): | ||
118 | """Build a native recipe and return the path to its bindir in RECIPE_SYSROOT_NATIVE""" | ||
119 | bitbake(native_recipe + " -c addto_recipe_sysroot") | ||
120 | vars = get_bb_vars(['RECIPE_SYSROOT_NATIVE', 'bindir'], native_recipe) | ||
121 | return os.path.join(vars['RECIPE_SYSROOT_NATIVE'], vars['bindir']) | ||
122 | |||
123 | def _verify_fit_image_signature(self, uboot_tools_bindir, fitimage_path, dtb_path, conf_name=None): | ||
124 | """Verify the signature of a fit configuration | ||
125 | |||
126 | The fit_check_sign utility from u-boot-tools-native is called. | ||
127 | uboot-fit_check_sign -f fitImage -k $dtb_path -c conf-$dtb_name | ||
128 | dtb_path refers to a binary device tree containing the public key. | ||
129 | """ | ||
130 | fit_check_sign_path = os.path.join(uboot_tools_bindir, 'uboot-fit_check_sign') | ||
131 | cmd = '%s -f %s -k %s' % (fit_check_sign_path, fitimage_path, dtb_path) | ||
132 | if conf_name: | ||
133 | cmd += ' -c %s' % conf_name | ||
134 | result = runCmd(cmd) | ||
135 | self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) | ||
136 | self.assertIn("Signature check OK", result.output) | ||
137 | |||
138 | def _verify_dtb_property(self, dtc_bindir, dtb_path, node_path, property_name, req_property, absent=False): | ||
139 | """Verify device tree properties | ||
140 | |||
141 | The fdtget utility from dtc-native is called and the property is compared. | ||
142 | """ | ||
143 | fdtget_path = os.path.join(dtc_bindir, 'fdtget') | ||
144 | cmd = '%s %s %s %s' % (fdtget_path, dtb_path, node_path, property_name) | ||
145 | if absent: | ||
146 | result = runCmd(cmd, ignore_status=True) | ||
147 | self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) | ||
148 | self.assertIn("FDT_ERR_NOTFOUND", result.output) | ||
149 | else: | ||
150 | result = runCmd(cmd) | ||
151 | self.logger.debug("%s\nreturned: %s\n%s", cmd, str(result.status), result.output) | ||
152 | self.assertEqual(req_property, result.output.strip()) | ||
153 | |||
154 | @staticmethod | ||
155 | def _find_string_in_bin_file(file_path, search_string): | ||
156 | """find strings in a binary file | ||
157 | |||
158 | Shell equivalent: strings "$1" | grep "$2" | wc -l | ||
159 | return number of matches | ||
160 | """ | ||
161 | found_positions = 0 | ||
162 | with open(file_path, 'rb') as file: | ||
163 | content = file.read().decode('ascii', errors='ignore') | ||
164 | found_positions = content.count(search_string) | ||
165 | return found_positions | ||
166 | |||
167 | @staticmethod | ||
168 | def _get_uboot_mkimage_sign_args(uboot_mkimage_sign_args): | ||
169 | """Retrive the string passed via -c to the mkimage command | ||
170 | |||
171 | Example: If a build configutation defines | ||
172 | UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" | ||
173 | this function returns "a smart comment" | ||
174 | """ | ||
175 | a_comment = None | ||
176 | if uboot_mkimage_sign_args: | ||
177 | mkimage_args = shlex.split(uboot_mkimage_sign_args) | ||
178 | try: | ||
179 | c_index = mkimage_args.index('-c') | ||
180 | a_comment = mkimage_args[c_index+1] | ||
181 | except ValueError: | ||
182 | pass | ||
183 | return a_comment | ||
184 | |||
185 | @staticmethod | ||
186 | def _get_dtb_files(bb_vars): | ||
187 | """Return a list of devicetree names | ||
188 | |||
189 | The list should be used to check the dtb and conf nodes in the FIT image or its file. | ||
190 | In addition to the entries from KERNEL_DEVICETREE, the external devicetree and the | ||
191 | external devicetree overlay added by the test recipe bbb-dtbs-as-ext are handled as well. | ||
192 | """ | ||
193 | kernel_devicetree = bb_vars.get('KERNEL_DEVICETREE') | ||
194 | all_dtbs = [] | ||
195 | dtb_symlinks = [] | ||
196 | if kernel_devicetree: | ||
197 | all_dtbs += [os.path.basename(dtb) for dtb in kernel_devicetree.split()] | ||
198 | # Support only the test recipe which provides 1 devicetree and 1 devicetree overlay | ||
199 | pref_prov_dtb = bb_vars.get('PREFERRED_PROVIDER_virtual/dtb') | ||
200 | if pref_prov_dtb == "bbb-dtbs-as-ext": | ||
201 | all_dtbs += ["am335x-bonegreen-ext.dtb", "BBORG_RELAY-00A2.dtbo"] | ||
202 | dtb_symlinks.append("am335x-bonegreen-ext-alias.dtb") | ||
203 | return (all_dtbs, dtb_symlinks) | ||
204 | |||
205 | def _is_req_dict_in_dict(self, found_dict, req_dict): | ||
206 | """ | ||
207 | Check if all key-value pairs in the required dictionary are present in the found dictionary. | ||
208 | |||
209 | This function recursively checks if the required dictionary (`req_dict`) is a subset of the found dictionary (`found_dict`). | ||
210 | It supports nested dictionaries, strings, lists, and sets as values. | ||
211 | |||
212 | Args: | ||
213 | found_dict (dict): The dictionary to search within. | ||
214 | req_dict (dict): The dictionary containing the required key-value pairs. | ||
215 | """ | ||
216 | for key, value in req_dict.items(): | ||
217 | self.assertIn(key, found_dict) | ||
218 | if isinstance(value, dict): | ||
219 | self._is_req_dict_in_dict(found_dict[key], value) | ||
220 | elif isinstance(value, str): | ||
221 | self.assertIn(value, found_dict[key]) | ||
222 | elif isinstance(value, list): | ||
223 | self.assertLessEqual(set(value), set(found_dict[key])) | ||
224 | elif isinstance(value, set): | ||
225 | self.assertLessEqual(value, found_dict[key]) | ||
226 | else: | ||
227 | self.assertEqual(value, found_dict[key]) | ||
228 | |||
229 | def _check_its_file(self, bb_vars, its_file_path): | ||
230 | """Check if the its file contains the expected sections and fields""" | ||
231 | # print the its file for debugging | ||
232 | if logging.DEBUG >= self.logger.level: | ||
233 | with open(its_file_path) as its_file: | ||
234 | self.logger.debug("its file: %s" % its_file.read()) | ||
235 | |||
236 | # Generate a list of expected paths in the its file | ||
237 | req_its_paths = self._get_req_its_paths(bb_vars) | ||
238 | self.logger.debug("req_its_paths:\n%s\n" % pprint.pformat(req_its_paths, indent=4)) | ||
239 | |||
240 | # Generate a dict of expected configuration signature nodes | ||
241 | req_sigvalues_config = self._get_req_sigvalues_config(bb_vars) | ||
242 | self.logger.debug("req_sigvalues_config:\n%s\n" % pprint.pformat(req_sigvalues_config, indent=4)) | ||
243 | |||
244 | # Generate a dict of expected image signature nodes | ||
245 | req_sigvalues_image = self._get_req_sigvalues_image(bb_vars) | ||
246 | self.logger.debug("req_sigvalues_image:\n%s\n" % pprint.pformat(req_sigvalues_image, indent=4)) | ||
247 | |||
248 | # Parse the its file for paths and signatures | ||
249 | its_path = [] | ||
250 | its_paths = [] | ||
251 | linect = 0 | ||
252 | sigs = {} | ||
253 | with open(its_file_path) as its_file: | ||
254 | for line in its_file: | ||
255 | linect += 1 | ||
256 | line = line.strip() | ||
257 | if line.endswith('};'): | ||
258 | its_path.pop() | ||
259 | elif line.endswith('{'): | ||
260 | its_path.append(line[:-1].strip()) | ||
261 | its_paths.append(its_path[:]) | ||
262 | # kernel-fitimage uses signature-1, uboot-sign uses signature | ||
263 | elif its_path and (its_path[-1] == 'signature-1' or its_path[-1] == 'signature'): | ||
264 | itsdotpath = '.'.join(its_path) | ||
265 | if not itsdotpath in sigs: | ||
266 | sigs[itsdotpath] = {} | ||
267 | if not '=' in line or not line.endswith(';'): | ||
268 | self.fail('Unexpected formatting in %s sigs section line %d:%s' % (its_file_path, linect, line)) | ||
269 | key, value = line.split('=', 1) | ||
270 | sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') | ||
271 | |||
272 | # Check if all expected paths are found in the its file | ||
273 | self.logger.debug("itspaths:\n%s\n" % pprint.pformat(its_paths, indent=4)) | ||
274 | for req_path in req_its_paths: | ||
275 | if not req_path in its_paths: | ||
276 | self.fail('Missing path in its file: %s (%s)' % (req_path, its_file_path)) | ||
277 | |||
278 | # Check if all the expected singnature nodes (images and configurations) are found | ||
279 | self.logger.debug("sigs:\n%s\n" % pprint.pformat(sigs, indent=4)) | ||
280 | if req_sigvalues_config or req_sigvalues_image: | ||
281 | for its_path, values in sigs.items(): | ||
282 | if bb_vars.get('FIT_CONF_PREFIX', "conf-") in its_path: | ||
283 | reqsigvalues = req_sigvalues_config | ||
284 | else: | ||
285 | reqsigvalues = req_sigvalues_image | ||
286 | for reqkey, reqvalue in reqsigvalues.items(): | ||
287 | value = values.get(reqkey, None) | ||
288 | if value is None: | ||
289 | self.fail('Missing key "%s" in its file signature section %s (%s)' % (reqkey, its_path, its_file_path)) | ||
290 | self.assertEqual(value, reqvalue) | ||
291 | |||
292 | # Generate a list of expected fields in the its file | ||
293 | req_its_fields = self._get_req_its_fields(bb_vars) | ||
294 | self.logger.debug("req_its_fields:\n%s\n" % pprint.pformat(req_its_fields, indent=4)) | ||
295 | |||
296 | # Check if all expected fields are in the its file | ||
297 | if req_its_fields: | ||
298 | field_index = 0 | ||
299 | field_index_last = len(req_its_fields) - 1 | ||
300 | with open(its_file_path) as its_file: | ||
301 | for line in its_file: | ||
302 | if req_its_fields[field_index] in line: | ||
303 | if field_index < field_index_last: | ||
304 | field_index +=1 | ||
305 | else: | ||
306 | break | ||
307 | self.assertEqual(field_index, field_index_last, | ||
308 | "Fields in Image Tree Source File %s did not match, error in finding %s" | ||
309 | % (its_file_path, req_its_fields[field_index])) | ||
310 | |||
311 | def _check_fitimage(self, bb_vars, fitimage_path, uboot_tools_bindir): | ||
312 | """Run dumpimage on the final FIT image and parse the output into a dict""" | ||
313 | dumpimage_path = os.path.join(uboot_tools_bindir, 'dumpimage') | ||
314 | cmd = '%s -l %s' % (dumpimage_path, fitimage_path) | ||
315 | self.logger.debug("Analyzing output from dumpimage: %s" % cmd) | ||
316 | dumpimage_result = runCmd(cmd) | ||
317 | in_section = None | ||
318 | sections = {} | ||
319 | self.logger.debug("dumpimage output: %s" % dumpimage_result.output) | ||
320 | for line in dumpimage_result.output.splitlines(): | ||
321 | # Find potentially hashed and signed sections | ||
322 | if line.startswith((' Configuration', ' Image')): | ||
323 | in_section = re.search(r'\((.*)\)', line).groups()[0] | ||
324 | # Key value lines start with two spaces otherwise the section ended | ||
325 | elif not line.startswith(" "): | ||
326 | in_section = None | ||
327 | # Handle key value lines of this section | ||
328 | elif in_section: | ||
329 | if not in_section in sections: | ||
330 | sections[in_section] = {} | ||
331 | try: | ||
332 | key, value = line.split(':', 1) | ||
333 | key = key.strip() | ||
334 | value = value.strip() | ||
335 | except ValueError as val_err: | ||
336 | # Handle multiple entries as e.g. for Loadables as a list | ||
337 | if key and line.startswith(" "): | ||
338 | value = sections[in_section][key] + "," + line.strip() | ||
339 | else: | ||
340 | raise ValueError(f"Error processing line: '{line}'. Original error: {val_err}") | ||
341 | sections[in_section][key] = value | ||
342 | |||
343 | # Check if the requested dictionary is a subset of the parsed dictionary | ||
344 | req_sections, num_signatures = self._get_req_sections(bb_vars) | ||
345 | self.logger.debug("req_sections: \n%s\n" % pprint.pformat(req_sections, indent=4)) | ||
346 | self.logger.debug("dumpimage sections: \n%s\n" % pprint.pformat(sections, indent=4)) | ||
347 | self._is_req_dict_in_dict(sections, req_sections) | ||
348 | |||
349 | # Call the signing related checks if the function is provided by a inherited class | ||
350 | self._check_signing(bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path) | ||
351 | |||
352 | def _get_req_its_paths(self, bb_vars): | ||
353 | self.logger.error("This function needs to be implemented") | ||
354 | return [] | ||
355 | |||
356 | def _get_req_its_fields(self, bb_vars): | ||
357 | self.logger.error("This function needs to be implemented") | ||
358 | return [] | ||
359 | |||
360 | def _get_req_sigvalues_config(self, bb_vars): | ||
361 | self.logger.error("This function needs to be implemented") | ||
362 | return {} | ||
363 | |||
364 | def _get_req_sigvalues_image(self, bb_vars): | ||
365 | self.logger.error("This function needs to be implemented") | ||
366 | return {} | ||
367 | |||
368 | def _get_req_sections(self, bb_vars): | ||
369 | self.logger.error("This function needs to be implemented") | ||
370 | return ({}, 0) | ||
371 | |||
372 | def _check_signing(self, bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path): | ||
373 | """Verify the signatures in the FIT image.""" | ||
374 | self.fail("Function needs to be implemented by inheriting classes") | ||
375 | |||
376 | def _bitbake_fit_image(self, bb_vars): | ||
377 | """Bitbake the FIT image and return the paths to the its file and the FIT image""" | ||
378 | self.fail("Function needs to be implemented by inheriting classes") | ||
379 | |||
380 | def _test_fitimage(self, bb_vars): | ||
381 | """Check if the its file and the FIT image are created and signed correctly""" | ||
382 | fitimage_its_path, fitimage_path = self._bitbake_fit_image(bb_vars) | ||
383 | self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) | ||
384 | self.assertExists(fitimage_path, "%s FIT image doesn't exist" % (fitimage_path)) | ||
385 | |||
386 | self.logger.debug("Checking its: %s" % fitimage_its_path) | ||
387 | self._check_its_file(bb_vars, fitimage_its_path) | ||
388 | |||
389 | # Setup u-boot-tools-native | ||
390 | uboot_tools_bindir = FitImageTestCase._setup_native('u-boot-tools-native') | ||
391 | |||
392 | # Verify the FIT image | ||
393 | self._check_fitimage(bb_vars, fitimage_path, uboot_tools_bindir) | ||
394 | |||
395 | class KernelFitImageBase(FitImageTestCase): | ||
396 | """Test cases for the linux-yocto-fitimage recipe""" | ||
397 | |||
398 | def _fit_get_bb_vars(self, additional_vars=[]): | ||
399 | """Retrieve BitBake variables specific to the test case. | ||
400 | |||
401 | Call the get_bb_vars function once and get all variables needed by the test case. | ||
402 | """ | ||
403 | internal_used = { | ||
404 | 'DEPLOY_DIR_IMAGE', | ||
405 | 'FIT_CONF_DEFAULT_DTB', | ||
406 | 'FIT_CONF_PREFIX', | ||
407 | 'FIT_DESC', | ||
408 | 'FIT_HASH_ALG', | ||
409 | 'FIT_KERNEL_COMP_ALG', | ||
410 | 'FIT_SIGN_ALG', | ||
411 | 'FIT_SIGN_INDIVIDUAL', | ||
412 | 'FIT_UBOOT_ENV', | ||
413 | 'INITRAMFS_IMAGE_BUNDLE', | ||
414 | 'INITRAMFS_IMAGE_NAME', | ||
415 | 'INITRAMFS_IMAGE', | ||
416 | 'KERNEL_DEPLOYSUBDIR', | ||
417 | 'KERNEL_DEVICETREE', | ||
418 | 'KERNEL_FIT_LINK_NAME', | ||
419 | 'MACHINE', | ||
420 | 'PREFERRED_PROVIDER_virtual/dtb', | ||
421 | 'UBOOT_ARCH', | ||
422 | 'UBOOT_ENTRYPOINT', | ||
423 | 'UBOOT_LOADADDRESS', | ||
424 | 'UBOOT_MKIMAGE_KERNEL_TYPE', | ||
425 | 'UBOOT_MKIMAGE_SIGN_ARGS', | ||
426 | 'UBOOT_RD_ENTRYPOINT', | ||
427 | 'UBOOT_RD_LOADADDRESS', | ||
428 | 'UBOOT_SIGN_ENABLE', | ||
429 | 'UBOOT_SIGN_IMG_KEYNAME', | ||
430 | 'UBOOT_SIGN_KEYDIR', | ||
431 | 'UBOOT_SIGN_KEYNAME', | ||
432 | } | ||
433 | bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), self.kernel_recipe) | ||
434 | self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4)) | ||
435 | return bb_vars | ||
436 | |||
437 | def _config_add_kernel_classes(self, config): | ||
438 | config += '# Use kernel-fit-extra-artifacts.bbclass for the creation of the vmlinux artifact' + os.linesep | ||
439 | config += 'KERNEL_CLASSES = "kernel-fit-extra-artifacts"' + os.linesep | ||
440 | return config | ||
441 | |||
442 | @property | ||
443 | def kernel_recipe(self): | ||
444 | return "linux-yocto-fitimage" | ||
445 | |||
446 | def _config_add_uboot_env(self, config): | ||
447 | """Generate an u-boot environment | ||
11 | 448 | ||
12 | class FitImageTests(OESelftestTestCase): | 449 | Create a boot.cmd file that is packed into the FIT image as a source-able text file. |
450 | Updates the configuration to include the boot.cmd file. | ||
451 | """ | ||
452 | fit_uenv_file = "boot.cmd" | ||
453 | test_files_dir = "test-files" | ||
454 | fit_uenv_path = os.path.join(self.builddir, test_files_dir, fit_uenv_file) | ||
455 | |||
456 | config += '# Add an u-boot script to the fitImage' + os.linesep | ||
457 | config += 'FIT_UBOOT_ENV = "%s"' % fit_uenv_file + os.linesep | ||
458 | config += 'FILESEXTRAPATHS:prepend := "${TOPDIR}/%s:"' % test_files_dir + os.linesep | ||
459 | config += 'SRC_URI:append:pn-%s = " file://${FIT_UBOOT_ENV}"' % self.kernel_recipe + os.linesep | ||
460 | |||
461 | if not os.path.isdir(test_files_dir): | ||
462 | os.makedirs(test_files_dir) | ||
463 | self.logger.debug("Writing to: %s" % fit_uenv_path) | ||
464 | with open(fit_uenv_path, "w") as f: | ||
465 | f.write('echo "hello world"') | ||
466 | |||
467 | return config | ||
468 | |||
469 | def _bitbake_fit_image(self, bb_vars): | ||
470 | """Bitbake the kernel and return the paths to the its file and the FIT image""" | ||
471 | bitbake(self.kernel_recipe) | ||
472 | |||
473 | # Find the right its file and the final fitImage and check if both files are available | ||
474 | deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE'] | ||
475 | initramfs_image = bb_vars['INITRAMFS_IMAGE'] | ||
476 | initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] | ||
477 | initramfs_image_name = bb_vars['INITRAMFS_IMAGE_NAME'] | ||
478 | kernel_fit_link_name = bb_vars['KERNEL_FIT_LINK_NAME'] | ||
479 | if not initramfs_image and initramfs_image_bundle != "1": | ||
480 | fitimage_its_name = "fitImage-its-%s" % kernel_fit_link_name | ||
481 | fitimage_name = "fitImage" | ||
482 | elif initramfs_image and initramfs_image_bundle != "1": | ||
483 | fitimage_its_name = "fitImage-its-%s-%s" % (initramfs_image_name, kernel_fit_link_name) | ||
484 | fitimage_name = "fitImage-%s-%s" % (initramfs_image_name, kernel_fit_link_name) | ||
485 | elif initramfs_image and initramfs_image_bundle == "1": | ||
486 | fitimage_its_name = "fitImage-its-%s-%s" % (initramfs_image_name, kernel_fit_link_name) | ||
487 | fitimage_name = "fitImage" # or fitImage-${KERNEL_IMAGE_LINK_NAME}${KERNEL_IMAGE_BIN_EXT} | ||
488 | else: | ||
489 | self.fail('Invalid configuration: INITRAMFS_IMAGE_BUNDLE = "1" and not INITRAMFS_IMAGE') | ||
490 | kernel_deploysubdir = bb_vars['KERNEL_DEPLOYSUBDIR'] | ||
491 | if kernel_deploysubdir: | ||
492 | fitimage_its_path = os.path.realpath(os.path.join(deploy_dir_image, kernel_deploysubdir, fitimage_its_name)) | ||
493 | fitimage_path = os.path.realpath(os.path.join(deploy_dir_image, kernel_deploysubdir, fitimage_name)) | ||
494 | else: | ||
495 | fitimage_its_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_its_name)) | ||
496 | fitimage_path = os.path.realpath(os.path.join(deploy_dir_image, fitimage_name)) | ||
497 | return (fitimage_its_path, fitimage_path) | ||
498 | |||
499 | def _get_req_its_paths(self, bb_vars): | ||
500 | """Generate a list of expected paths in the its file | ||
501 | |||
502 | Example: | ||
503 | [ | ||
504 | ['/', 'images', 'kernel-1', 'hash-1'], | ||
505 | ['/', 'images', 'kernel-1', 'signature-1'], | ||
506 | ] | ||
507 | """ | ||
508 | dtb_files, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars) | ||
509 | fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL'] | ||
510 | fit_uboot_env = bb_vars['FIT_UBOOT_ENV'] | ||
511 | initramfs_image = bb_vars['INITRAMFS_IMAGE'] | ||
512 | initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] | ||
513 | uboot_sign_enable = bb_vars.get('UBOOT_SIGN_ENABLE') | ||
514 | |||
515 | # image nodes | ||
516 | images = [ 'kernel-1' ] | ||
517 | if dtb_files: | ||
518 | images += [ 'fdt-' + dtb for dtb in dtb_files ] | ||
519 | if fit_uboot_env: | ||
520 | images.append('bootscr-' + fit_uboot_env) | ||
521 | if bb_vars['MACHINE'] == "qemux86-64": # Not really the right if | ||
522 | images.append('setup-1') | ||
523 | if initramfs_image and initramfs_image_bundle != "1": | ||
524 | images.append('ramdisk-1') | ||
525 | |||
526 | # configuration nodes (one per DTB and also one per symlink) | ||
527 | if dtb_files: | ||
528 | configurations = [bb_vars['FIT_CONF_PREFIX'] + conf for conf in dtb_files + dtb_symlinks] | ||
529 | else: | ||
530 | configurations = [bb_vars['FIT_CONF_PREFIX'] + '1'] | ||
531 | |||
532 | # Create a list of paths for all image and configuration nodes | ||
533 | req_its_paths = [] | ||
534 | for image in images: | ||
535 | req_its_paths.append(['/', 'images', image, 'hash-1']) | ||
536 | if uboot_sign_enable == "1" and fit_sign_individual == "1": | ||
537 | req_its_paths.append(['/', 'images', image, 'signature-1']) | ||
538 | for configuration in configurations: | ||
539 | req_its_paths.append(['/', 'configurations', configuration, 'hash-1']) | ||
540 | if uboot_sign_enable == "1": | ||
541 | req_its_paths.append(['/', 'configurations', configuration, 'signature-1']) | ||
542 | return req_its_paths | ||
543 | |||
544 | def _get_req_its_fields(self, bb_vars): | ||
545 | initramfs_image = bb_vars['INITRAMFS_IMAGE'] | ||
546 | initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] | ||
547 | uboot_rd_loadaddress = bb_vars.get('UBOOT_RD_LOADADDRESS') | ||
548 | uboot_rd_entrypoint = bb_vars.get('UBOOT_RD_ENTRYPOINT') | ||
549 | |||
550 | its_field_check = [ | ||
551 | 'description = "%s";' % bb_vars['FIT_DESC'], | ||
552 | 'description = "Linux kernel";', | ||
553 | 'type = "' + str(bb_vars['UBOOT_MKIMAGE_KERNEL_TYPE']) + '";', | ||
554 | # 'compression = "' + str(bb_vars['FIT_KERNEL_COMP_ALG']) + '";', defined based on files in TMPDIR, not ideal... | ||
555 | 'data = /incbin/("linux.bin");', | ||
556 | 'arch = "' + str(bb_vars['UBOOT_ARCH']) + '";', | ||
557 | 'os = "linux";', | ||
558 | 'load = <' + str(bb_vars['UBOOT_LOADADDRESS']) + '>;', | ||
559 | 'entry = <' + str(bb_vars['UBOOT_ENTRYPOINT']) + '>;', | ||
560 | ] | ||
561 | if initramfs_image and initramfs_image_bundle != "1": | ||
562 | its_field_check.append('type = "ramdisk";') | ||
563 | if uboot_rd_loadaddress: | ||
564 | its_field_check.append("load = <%s>;" % uboot_rd_loadaddress) | ||
565 | if uboot_rd_entrypoint: | ||
566 | its_field_check.append("entry = <%s>;" % uboot_rd_entrypoint) | ||
567 | |||
568 | fit_conf_default_dtb = bb_vars.get('FIT_CONF_DEFAULT_DTB') | ||
569 | if fit_conf_default_dtb: | ||
570 | fit_conf_prefix = bb_vars.get('FIT_CONF_PREFIX', "conf-") | ||
571 | its_field_check.append('default = "' + fit_conf_prefix + fit_conf_default_dtb + '";') | ||
572 | |||
573 | its_field_check.append('kernel = "kernel-1";') | ||
574 | |||
575 | if initramfs_image and initramfs_image_bundle != "1": | ||
576 | its_field_check.append('ramdisk = "ramdisk-1";') | ||
577 | |||
578 | return its_field_check | ||
579 | |||
580 | def _get_req_sigvalues_config(self, bb_vars): | ||
581 | """Generate a dictionary of expected configuration signature nodes""" | ||
582 | if bb_vars.get('UBOOT_SIGN_ENABLE') != "1": | ||
583 | return {} | ||
584 | sign_images = '"kernel", "fdt"' | ||
585 | if bb_vars['INITRAMFS_IMAGE'] and bb_vars['INITRAMFS_IMAGE_BUNDLE'] != "1": | ||
586 | sign_images += ', "ramdisk"' | ||
587 | if bb_vars['FIT_UBOOT_ENV']: | ||
588 | sign_images += ', "bootscr"' | ||
589 | req_sigvalues_config = { | ||
590 | 'algo': '"%s,%s"' % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']), | ||
591 | 'key-name-hint': '"%s"' % bb_vars['UBOOT_SIGN_KEYNAME'], | ||
592 | 'sign-images': sign_images, | ||
593 | } | ||
594 | return req_sigvalues_config | ||
595 | |||
596 | def _get_req_sigvalues_image(self, bb_vars): | ||
597 | """Generate a dictionary of expected image signature nodes""" | ||
598 | if bb_vars['FIT_SIGN_INDIVIDUAL'] != "1": | ||
599 | return {} | ||
600 | req_sigvalues_image = { | ||
601 | 'algo': '"%s,%s"' % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']), | ||
602 | 'key-name-hint': '"%s"' % bb_vars['UBOOT_SIGN_IMG_KEYNAME'], | ||
603 | } | ||
604 | return req_sigvalues_image | ||
605 | |||
606 | def _get_req_sections(self, bb_vars): | ||
607 | """Generate a dictionary of expected sections in the output of dumpimage""" | ||
608 | dtb_files, dtb_symlinks = FitImageTestCase._get_dtb_files(bb_vars) | ||
609 | fit_hash_alg = bb_vars['FIT_HASH_ALG'] | ||
610 | fit_sign_alg = bb_vars['FIT_SIGN_ALG'] | ||
611 | fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL'] | ||
612 | fit_uboot_env = bb_vars['FIT_UBOOT_ENV'] | ||
613 | initramfs_image = bb_vars['INITRAMFS_IMAGE'] | ||
614 | initramfs_image_bundle = bb_vars['INITRAMFS_IMAGE_BUNDLE'] | ||
615 | uboot_sign_enable = bb_vars['UBOOT_SIGN_ENABLE'] | ||
616 | uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] | ||
617 | uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] | ||
618 | num_signatures = 0 | ||
619 | req_sections = { | ||
620 | "kernel-1": { | ||
621 | "Type": "Kernel Image", | ||
622 | "OS": "Linux", | ||
623 | "Load Address": bb_vars['UBOOT_LOADADDRESS'], | ||
624 | "Entry Point": bb_vars['UBOOT_ENTRYPOINT'], | ||
625 | } | ||
626 | } | ||
627 | # Create one section per DTB | ||
628 | for dtb in dtb_files: | ||
629 | req_sections['fdt-' + dtb] = { | ||
630 | "Type": "Flat Device Tree", | ||
631 | } | ||
632 | # Add a script section if there is a script | ||
633 | if fit_uboot_env: | ||
634 | req_sections['bootscr-' + fit_uboot_env] = { "Type": "Script" } | ||
635 | # Add the initramfs | ||
636 | if initramfs_image and initramfs_image_bundle != "1": | ||
637 | req_sections['ramdisk-1'] = { | ||
638 | "Type": "RAMDisk Image", | ||
639 | "Load Address": bb_vars['UBOOT_RD_LOADADDRESS'], | ||
640 | "Entry Point": bb_vars['UBOOT_RD_ENTRYPOINT'] | ||
641 | } | ||
642 | # Create a configuration section for each DTB | ||
643 | if dtb_files: | ||
644 | for dtb in dtb_files + dtb_symlinks: | ||
645 | conf_name = bb_vars['FIT_CONF_PREFIX'] + dtb | ||
646 | # Assume that DTBs with an "-alias" in its name are symlink DTBs created e.g. by the | ||
647 | # bbb-dtbs-as-ext test recipe. Make the configuration node pointing to the real DTB. | ||
648 | real_dtb = dtb.replace("-alias", "") | ||
649 | # dtb overlays do not refer to a kernel (yet?) | ||
650 | if dtb.endswith('.dtbo'): | ||
651 | req_sections[conf_name] = { | ||
652 | "FDT": 'fdt-' + real_dtb, | ||
653 | } | ||
654 | else: | ||
655 | req_sections[conf_name] = { | ||
656 | "Kernel": "kernel-1", | ||
657 | "FDT": 'fdt-' + real_dtb, | ||
658 | } | ||
659 | if initramfs_image and initramfs_image_bundle != "1": | ||
660 | req_sections[conf_name]['Init Ramdisk'] = "ramdisk-1" | ||
661 | else: | ||
662 | conf_name = bb_vars['FIT_CONF_PREFIX'] + '1' | ||
663 | req_sections[conf_name] = { | ||
664 | "Kernel": "kernel-1" | ||
665 | } | ||
666 | if initramfs_image and initramfs_image_bundle != "1": | ||
667 | req_sections[conf_name]['Init Ramdisk'] = "ramdisk-1" | ||
668 | |||
669 | # Add signing related properties if needed | ||
670 | if uboot_sign_enable == "1": | ||
671 | for section in req_sections: | ||
672 | req_sections[section]['Hash algo'] = fit_hash_alg | ||
673 | if section.startswith(bb_vars['FIT_CONF_PREFIX']): | ||
674 | req_sections[section]['Hash value'] = "unavailable" | ||
675 | req_sections[section]['Sign algo'] = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_keyname) | ||
676 | num_signatures += 1 | ||
677 | elif fit_sign_individual == "1": | ||
678 | req_sections[section]['Sign algo'] = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_img_keyname) | ||
679 | num_signatures += 1 | ||
680 | return (req_sections, num_signatures) | ||
681 | |||
682 | def _check_signing(self, bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path): | ||
683 | """Verify the signature nodes in the FIT image""" | ||
684 | if bb_vars['UBOOT_SIGN_ENABLE'] == "1": | ||
685 | self.logger.debug("Verifying signatures in the FIT image") | ||
686 | else: | ||
687 | self.logger.debug("FIT image is not signed. Signature verification is not needed.") | ||
688 | return | ||
689 | |||
690 | fit_hash_alg = bb_vars['FIT_HASH_ALG'] | ||
691 | fit_sign_alg = bb_vars['FIT_SIGN_ALG'] | ||
692 | uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] | ||
693 | uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] | ||
694 | deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE'] | ||
695 | kernel_deploysubdir = bb_vars['KERNEL_DEPLOYSUBDIR'] | ||
696 | fit_sign_individual = bb_vars['FIT_SIGN_INDIVIDUAL'] | ||
697 | fit_hash_alg_len = FitImageTestCase.MKIMAGE_HASH_LENGTHS[fit_hash_alg] | ||
698 | fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[fit_sign_alg] | ||
699 | for section, values in sections.items(): | ||
700 | # Configuration nodes are always signed with UBOOT_SIGN_KEYNAME (if UBOOT_SIGN_ENABLE = "1") | ||
701 | if section.startswith(bb_vars['FIT_CONF_PREFIX']): | ||
702 | sign_algo = values.get('Sign algo', None) | ||
703 | req_sign_algo = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_keyname) | ||
704 | self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section) | ||
705 | sign_value = values.get('Sign value', None) | ||
706 | self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section) | ||
707 | dtb_file_name = section.replace(bb_vars['FIT_CONF_PREFIX'], '') | ||
708 | dtb_path = os.path.join(deploy_dir_image, dtb_file_name) | ||
709 | if kernel_deploysubdir: | ||
710 | dtb_path = os.path.join(deploy_dir_image, kernel_deploysubdir, dtb_file_name) | ||
711 | # External devicetrees created by devicetree.bbclass are in a subfolder and have priority | ||
712 | dtb_path_ext = os.path.join(deploy_dir_image, "devicetree", dtb_file_name) | ||
713 | if os.path.exists(dtb_path_ext): | ||
714 | dtb_path = dtb_path_ext | ||
715 | self._verify_fit_image_signature(uboot_tools_bindir, fitimage_path, dtb_path, section) | ||
716 | else: | ||
717 | # Image nodes always need a hash which gets indirectly signed by the config signature | ||
718 | hash_algo = values.get('Hash algo', None) | ||
719 | self.assertEqual(hash_algo, fit_hash_alg) | ||
720 | hash_value = values.get('Hash value', None) | ||
721 | self.assertEqual(len(hash_value), fit_hash_alg_len, 'Hash value for section %s not expected length' % section) | ||
722 | # Optionally, if FIT_SIGN_INDIVIDUAL = 1 also the image nodes have a signature (which is redundant but possible) | ||
723 | if fit_sign_individual == "1": | ||
724 | sign_algo = values.get('Sign algo', None) | ||
725 | req_sign_algo = "%s,%s:%s" % (fit_hash_alg, fit_sign_alg, uboot_sign_img_keyname) | ||
726 | self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section) | ||
727 | sign_value = values.get('Sign value', None) | ||
728 | self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section) | ||
729 | |||
730 | # Search for the string passed to mkimage in each signed section of the FIT image. | ||
731 | # Looks like mkimage supports to add a comment but does not support to read it back. | ||
732 | a_comment = FitImageTestCase._get_uboot_mkimage_sign_args(bb_vars['UBOOT_MKIMAGE_SIGN_ARGS']) | ||
733 | self.logger.debug("a_comment: %s" % a_comment) | ||
734 | if a_comment: | ||
735 | found_comments = FitImageTestCase._find_string_in_bin_file(fitimage_path, a_comment) | ||
736 | self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." % | ||
737 | (num_signatures, a_comment)) | ||
738 | |||
739 | class KernelFitImageRecipeTests(KernelFitImageBase): | ||
740 | """Test cases for the kernel-fitimage bbclass""" | ||
13 | 741 | ||
14 | def test_fit_image(self): | 742 | def test_fit_image(self): |
15 | """ | 743 | """ |
@@ -25,10 +753,7 @@ class FitImageTests(OESelftestTestCase): | |||
25 | Author: Usama Arif <usama.arif@arm.com> | 753 | Author: Usama Arif <usama.arif@arm.com> |
26 | """ | 754 | """ |
27 | config = """ | 755 | config = """ |
28 | # Enable creation of fitImage | ||
29 | KERNEL_IMAGETYPE = "Image" | 756 | KERNEL_IMAGETYPE = "Image" |
30 | KERNEL_IMAGETYPES += " fitImage " | ||
31 | KERNEL_CLASSES = " kernel-fitimage " | ||
32 | 757 | ||
33 | # RAM disk variables including load address and entrypoint for kernel and RAM disk | 758 | # RAM disk variables including load address and entrypoint for kernel and RAM disk |
34 | IMAGE_FSTYPES += "cpio.gz" | 759 | IMAGE_FSTYPES += "cpio.gz" |
@@ -40,79 +765,145 @@ UBOOT_RD_ENTRYPOINT = "0x88000000" | |||
40 | UBOOT_LOADADDRESS = "0x80080000" | 765 | UBOOT_LOADADDRESS = "0x80080000" |
41 | UBOOT_ENTRYPOINT = "0x80080000" | 766 | UBOOT_ENTRYPOINT = "0x80080000" |
42 | FIT_DESC = "A model description" | 767 | FIT_DESC = "A model description" |
768 | FIT_CONF_PREFIX = "foo-" | ||
43 | """ | 769 | """ |
770 | config = self._config_add_kernel_classes(config) | ||
44 | self.write_config(config) | 771 | self.write_config(config) |
772 | bb_vars = self._fit_get_bb_vars() | ||
773 | self._test_fitimage(bb_vars) | ||
45 | 774 | ||
46 | # fitImage is created as part of linux recipe | 775 | def test_get_compatible_from_dtb(self): |
47 | image = "virtual/kernel" | 776 | """Test the oe.fitimage.get_compatible_from_dtb function |
48 | bitbake(image) | ||
49 | bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'INITRAMFS_IMAGE_NAME', 'KERNEL_FIT_LINK_NAME'], image) | ||
50 | |||
51 | fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], | ||
52 | "fitImage-its-%s-%s" % (bb_vars['INITRAMFS_IMAGE_NAME'], bb_vars['KERNEL_FIT_LINK_NAME'])) | ||
53 | fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], | ||
54 | "fitImage-%s-%s" % (bb_vars['INITRAMFS_IMAGE_NAME'], bb_vars['KERNEL_FIT_LINK_NAME'])) | ||
55 | |||
56 | self.assertTrue(os.path.exists(fitimage_its_path), | ||
57 | "%s image tree source doesn't exist" % (fitimage_its_path)) | ||
58 | self.assertTrue(os.path.exists(fitimage_path), | ||
59 | "%s FIT image doesn't exist" % (fitimage_path)) | ||
60 | |||
61 | # Check that the type, load address, entrypoint address and default | ||
62 | # values for kernel and ramdisk in Image Tree Source are as expected. | ||
63 | # The order of fields in the below array is important. Not all the | ||
64 | # fields are tested, only the key fields that wont vary between | ||
65 | # different architectures. | ||
66 | its_field_check = [ | ||
67 | 'description = "A model description";', | ||
68 | 'type = "kernel";', | ||
69 | 'load = <0x80080000>;', | ||
70 | 'entry = <0x80080000>;', | ||
71 | 'type = "ramdisk";', | ||
72 | 'load = <0x88000000>;', | ||
73 | 'entry = <0x88000000>;', | ||
74 | 'default = "conf-1";', | ||
75 | 'kernel = "kernel-1";', | ||
76 | 'ramdisk = "ramdisk-1";' | ||
77 | ] | ||
78 | 777 | ||
79 | with open(fitimage_its_path) as its_file: | 778 | 1. bitbake bbb-dtbs-as-ext |
80 | field_index = 0 | 779 | 2. Check if symlink_points_below returns the path to the DTB |
81 | for line in its_file: | 780 | 3. Check if the expected compatible string is found by get_compatible_from_dtb() |
82 | if field_index == len(its_field_check): | 781 | """ |
83 | break | 782 | DTB_RECIPE = "bbb-dtbs-as-ext" |
84 | if its_field_check[field_index] in line: | 783 | DTB_FILE = "am335x-bonegreen-ext.dtb" |
85 | field_index +=1 | 784 | DTB_SYMLINK = "am335x-bonegreen-ext-alias.dtb" |
785 | DTBO_FILE = "BBORG_RELAY-00A2.dtbo" | ||
786 | EXPECTED_COMP = ["ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"] | ||
86 | 787 | ||
87 | if field_index != len(its_field_check): # if its equal, the test passed | 788 | config = """ |
88 | self.assertTrue(field_index == len(its_field_check), | 789 | DISTRO = "poky" |
89 | "Fields in Image Tree Source File %s did not match, error in finding %s" | 790 | MACHINE = "beaglebone-yocto" |
90 | % (fitimage_its_path, its_field_check[field_index])) | 791 | """ |
792 | self.write_config(config) | ||
793 | |||
794 | # Provide the fdtget command called by get_compatible_from_dtb | ||
795 | dtc_bindir = FitImageTestCase._setup_native('dtc-native') | ||
796 | fdtget_path = os.path.join(dtc_bindir, "fdtget") | ||
797 | self.assertExists(fdtget_path) | ||
798 | |||
799 | # bitbake an external DTB with a symlink to it and a DTB overlay | ||
800 | bitbake(DTB_RECIPE) | ||
801 | deploy_dir_image = get_bb_var("DEPLOY_DIR_IMAGE", DTB_RECIPE) | ||
802 | devicetree_dir = os.path.join(deploy_dir_image, "devicetree") | ||
803 | dtb_path = os.path.join(devicetree_dir, DTB_FILE) | ||
804 | dtb_alias_path = os.path.join(devicetree_dir, DTB_SYMLINK) | ||
805 | dtbo_file = os.path.join(devicetree_dir, DTBO_FILE) | ||
806 | self.assertExists(dtb_path) | ||
807 | self.assertExists(dtb_alias_path) | ||
808 | self.assertExists(dtbo_file) | ||
809 | |||
810 | # Test symlink_points_below | ||
811 | linked_dtb = oe.fitimage.symlink_points_below(dtb_alias_path, devicetree_dir) | ||
812 | self.assertEqual(linked_dtb, DTB_FILE) | ||
813 | |||
814 | # Check if get_compatible_from_dtb finds the expected compatible string in the DTBs | ||
815 | comp = oe.fitimage.get_compatible_from_dtb(dtb_path, fdtget_path) | ||
816 | self.assertEqual(comp, EXPECTED_COMP) | ||
817 | comp_alias = oe.fitimage.get_compatible_from_dtb(dtb_alias_path, fdtget_path) | ||
818 | self.assertEqual(comp_alias, EXPECTED_COMP) | ||
819 | # The alias is a symlink, therefore the compatible string is equal | ||
820 | self.assertEqual(comp_alias, comp) | ||
821 | |||
822 | def test_fit_image_ext_dtb_dtbo(self): | ||
823 | """ | ||
824 | Summary: Check if FIT image and Image Tree Source (its) are created correctly. | ||
825 | Expected: 1) its and FIT image are built successfully | ||
826 | 2) The its file contains also the external devicetree overlay | ||
827 | 3) Dumping the FIT image indicates the devicetree overlay | ||
828 | """ | ||
829 | config = """ | ||
830 | # Enable creation of fitImage | ||
831 | MACHINE = "beaglebone-yocto" | ||
832 | # Add a devicetree overlay which does not need kernel sources | ||
833 | PREFERRED_PROVIDER_virtual/dtb = "bbb-dtbs-as-ext" | ||
834 | """ | ||
835 | config = self._config_add_kernel_classes(config) | ||
836 | config = self._config_add_uboot_env(config) | ||
837 | self.write_config(config) | ||
838 | bb_vars = self._fit_get_bb_vars() | ||
839 | self._test_fitimage(bb_vars) | ||
840 | |||
841 | |||
842 | def test_sign_fit_image_configurations(self): | ||
843 | """ | ||
844 | Summary: Check if FIT image and Image Tree Source (its) are created | ||
845 | and the configuration nodes are signed correctly. | ||
846 | Expected: 1) its and FIT image are built successfully | ||
847 | 2) Scanning the its file indicates signing is enabled | ||
848 | as requested by UBOOT_SIGN_ENABLE | ||
849 | 3) Dumping the FIT image indicates signature values | ||
850 | are present (only for the configuration nodes as | ||
851 | FIT_SIGN_INDIVIDUAL is disabled) | ||
852 | 4) Verify the FIT image contains the comments passed via | ||
853 | UBOOT_MKIMAGE_SIGN_ARGS once per configuration node. | ||
854 | """ | ||
855 | # Generate a configuration section which gets included into the local.conf file | ||
856 | config = """ | ||
857 | # Enable creation of fitImage | ||
858 | MACHINE = "beaglebone-yocto" | ||
859 | UBOOT_SIGN_ENABLE = "1" | ||
860 | UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" | ||
861 | UBOOT_SIGN_KEYNAME = "dev" | ||
862 | UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" | ||
863 | FIT_CONF_DEFAULT_DTB = "am335x-bonegreen.dtb" | ||
864 | """ | ||
865 | config = self._config_add_kernel_classes(config) | ||
866 | config = self._config_add_uboot_env(config) | ||
867 | self.write_config(config) | ||
868 | |||
869 | # Retrieve some variables from bitbake | ||
870 | bb_vars = self._fit_get_bb_vars([ | ||
871 | 'FIT_KEY_GENRSA_ARGS', | ||
872 | 'FIT_KEY_REQ_ARGS', | ||
873 | 'FIT_KEY_SIGN_PKCS', | ||
874 | 'FIT_SIGN_NUMBITS', | ||
875 | 'UBOOT_SIGN_KEYDIR', | ||
876 | ]) | ||
91 | 877 | ||
878 | self._gen_signing_key(bb_vars) | ||
879 | self._test_fitimage(bb_vars) | ||
92 | 880 | ||
93 | def test_sign_fit_image(self): | 881 | def test_sign_fit_image_individual(self): |
94 | """ | 882 | """ |
95 | Summary: Check if FIT image and Image Tree Source (its) are created | 883 | Summary: Check if FIT image and Image Tree Source (its) are created |
96 | and signed correctly. | 884 | and all nodes are signed correctly. |
97 | Expected: 1) its and FIT image are built successfully | 885 | Expected: 1) its and FIT image are built successfully |
98 | 2) Scanning the its file indicates signing is enabled | 886 | 2) Scanning the its file indicates signing is enabled |
99 | as requested by UBOOT_SIGN_ENABLE (using keys generated | 887 | as requested by UBOOT_SIGN_ENABLE |
100 | via FIT_GENERATE_KEYS) | ||
101 | 3) Dumping the FIT image indicates signature values | 888 | 3) Dumping the FIT image indicates signature values |
102 | are present (including for images as enabled via | 889 | are present (including for images as enabled via |
103 | FIT_SIGN_INDIVIDUAL) | 890 | FIT_SIGN_INDIVIDUAL) |
104 | 4) Examination of the do_assemble_fitimage runfile/logfile | 891 | This also implies that FIT_GENERATE_KEYS = "1" works. |
105 | indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN and | 892 | 4) Verify the FIT image contains the comments passed via |
106 | UBOOT_MKIMAGE_SIGN_ARGS are working as expected. | 893 | UBOOT_MKIMAGE_SIGN_ARGS once per image and per |
894 | configuration node. | ||
895 | Note: This test is mostly for backward compatibility. | ||
896 | The recommended approach is to sign the configuration nodes | ||
897 | which include also the hashes of all the images. Signing | ||
898 | all the images individually is therefore redundant. | ||
107 | Product: oe-core | 899 | Product: oe-core |
108 | Author: Paul Eggleton <paul.eggleton@microsoft.com> based upon | 900 | Author: Paul Eggleton <paul.eggleton@microsoft.com> based upon |
109 | work by Usama Arif <usama.arif@arm.com> | 901 | work by Usama Arif <usama.arif@arm.com> |
110 | """ | 902 | """ |
903 | # Generate a configuration section which gets included into the local.conf file | ||
111 | config = """ | 904 | config = """ |
112 | # Enable creation of fitImage | 905 | # Enable creation of fitImage |
113 | MACHINE = "beaglebone-yocto" | 906 | MACHINE = "beaglebone-yocto" |
114 | KERNEL_IMAGETYPES += " fitImage " | ||
115 | KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper " | ||
116 | UBOOT_SIGN_ENABLE = "1" | 907 | UBOOT_SIGN_ENABLE = "1" |
117 | FIT_GENERATE_KEYS = "1" | 908 | FIT_GENERATE_KEYS = "1" |
118 | UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" | 909 | UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" |
@@ -121,211 +912,494 @@ UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" | |||
121 | FIT_SIGN_INDIVIDUAL = "1" | 912 | FIT_SIGN_INDIVIDUAL = "1" |
122 | UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" | 913 | UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" |
123 | """ | 914 | """ |
915 | config = self._config_add_kernel_classes(config) | ||
916 | config = self._config_add_uboot_env(config) | ||
124 | self.write_config(config) | 917 | self.write_config(config) |
918 | bb_vars = self._fit_get_bb_vars() | ||
125 | 919 | ||
126 | # fitImage is created as part of linux recipe | 920 | # Ensure new keys are generated and FIT_GENERATE_KEYS = "1" is tested |
127 | image = "virtual/kernel" | 921 | bitbake("kernel-signing-keys-native -c compile -f") |
128 | bitbake(image) | ||
129 | bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'KERNEL_FIT_LINK_NAME'], image) | ||
130 | |||
131 | fitimage_its_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], | ||
132 | "fitImage-its-%s" % (bb_vars['KERNEL_FIT_LINK_NAME'])) | ||
133 | fitimage_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], | ||
134 | "fitImage-%s.bin" % (bb_vars['KERNEL_FIT_LINK_NAME'])) | ||
135 | |||
136 | self.assertTrue(os.path.exists(fitimage_its_path), | ||
137 | "%s image tree source doesn't exist" % (fitimage_its_path)) | ||
138 | self.assertTrue(os.path.exists(fitimage_path), | ||
139 | "%s FIT image doesn't exist" % (fitimage_path)) | ||
140 | |||
141 | req_itspaths = [ | ||
142 | ['/', 'images', 'kernel-1'], | ||
143 | ['/', 'images', 'kernel-1', 'signature-1'], | ||
144 | ['/', 'images', 'fdt-am335x-boneblack.dtb'], | ||
145 | ['/', 'images', 'fdt-am335x-boneblack.dtb', 'signature-1'], | ||
146 | ['/', 'configurations', 'conf-am335x-boneblack.dtb'], | ||
147 | ['/', 'configurations', 'conf-am335x-boneblack.dtb', 'signature-1'], | ||
148 | ] | ||
149 | 922 | ||
150 | itspath = [] | 923 | self._test_fitimage(bb_vars) |
151 | itspaths = [] | ||
152 | linect = 0 | ||
153 | sigs = {} | ||
154 | with open(fitimage_its_path) as its_file: | ||
155 | linect += 1 | ||
156 | for line in its_file: | ||
157 | line = line.strip() | ||
158 | if line.endswith('};'): | ||
159 | itspath.pop() | ||
160 | elif line.endswith('{'): | ||
161 | itspath.append(line[:-1].strip()) | ||
162 | itspaths.append(itspath[:]) | ||
163 | elif itspath and itspath[-1] == 'signature-1': | ||
164 | itsdotpath = '.'.join(itspath) | ||
165 | if not itsdotpath in sigs: | ||
166 | sigs[itsdotpath] = {} | ||
167 | if not '=' in line or not line.endswith(';'): | ||
168 | self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) | ||
169 | key, value = line.split('=', 1) | ||
170 | sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') | ||
171 | 924 | ||
172 | for reqpath in req_itspaths: | 925 | def test_fit_image_sign_initramfs(self): |
173 | if not reqpath in itspaths: | ||
174 | self.fail('Missing section in its file: %s' % reqpath) | ||
175 | |||
176 | reqsigvalues_image = { | ||
177 | 'algo': '"sha256,rsa2048"', | ||
178 | 'key-name-hint': '"img-oe-selftest"', | ||
179 | } | ||
180 | reqsigvalues_config = { | ||
181 | 'algo': '"sha256,rsa2048"', | ||
182 | 'key-name-hint': '"cfg-oe-selftest"', | ||
183 | 'sign-images': '"kernel", "fdt"', | ||
184 | } | ||
185 | |||
186 | for itspath, values in sigs.items(): | ||
187 | if 'conf-' in itspath: | ||
188 | reqsigvalues = reqsigvalues_config | ||
189 | else: | ||
190 | reqsigvalues = reqsigvalues_image | ||
191 | for reqkey, reqvalue in reqsigvalues.items(): | ||
192 | value = values.get(reqkey, None) | ||
193 | if value is None: | ||
194 | self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) | ||
195 | self.assertEqual(value, reqvalue) | ||
196 | |||
197 | # Dump the image to see if it really got signed | ||
198 | bitbake("u-boot-tools-native -c addto_recipe_sysroot") | ||
199 | result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=') | ||
200 | recipe_sysroot_native = result.output.split('=')[1].strip('"') | ||
201 | dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage') | ||
202 | result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path)) | ||
203 | in_signed = None | ||
204 | signed_sections = {} | ||
205 | for line in result.output.splitlines(): | ||
206 | if line.startswith((' Configuration', ' Image')): | ||
207 | in_signed = re.search(r'\((.*)\)', line).groups()[0] | ||
208 | elif re.match('^ *', line) in (' ', ''): | ||
209 | in_signed = None | ||
210 | elif in_signed: | ||
211 | if not in_signed in signed_sections: | ||
212 | signed_sections[in_signed] = {} | ||
213 | key, value = line.split(':', 1) | ||
214 | signed_sections[in_signed][key.strip()] = value.strip() | ||
215 | self.assertIn('kernel-1', signed_sections) | ||
216 | self.assertIn('fdt-am335x-boneblack.dtb', signed_sections) | ||
217 | self.assertIn('conf-am335x-boneblack.dtb', signed_sections) | ||
218 | for signed_section, values in signed_sections.items(): | ||
219 | value = values.get('Sign algo', None) | ||
220 | if signed_section.startswith("conf"): | ||
221 | self.assertEqual(value, 'sha256,rsa2048:cfg-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) | ||
222 | else: | ||
223 | self.assertEqual(value, 'sha256,rsa2048:img-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) | ||
224 | value = values.get('Sign value', None) | ||
225 | self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) | ||
226 | |||
227 | # Check for UBOOT_MKIMAGE_SIGN_ARGS | ||
228 | result = runCmd('bitbake -e virtual/kernel | grep ^T=') | ||
229 | tempdir = result.output.split('=', 1)[1].strip().strip('') | ||
230 | result = runCmd('grep "a smart comment" %s/run.do_assemble_fitimage' % tempdir, ignore_status=True) | ||
231 | self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN_ARGS value did not get used') | ||
232 | |||
233 | # Check for evidence of test-mkimage-wrapper class | ||
234 | result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_assemble_fitimage' % tempdir, ignore_status=True) | ||
235 | self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work') | ||
236 | result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_assemble_fitimage' % tempdir, ignore_status=True) | ||
237 | self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work') | ||
238 | |||
239 | def test_uboot_fit_image(self): | ||
240 | """ | 926 | """ |
241 | Summary: Check if Uboot FIT image and Image Tree Source | 927 | Summary: Verifies the content of the initramfs node in the FIT Image Tree Source (its) |
242 | (its) are built and the Image Tree Source has the | 928 | The FIT settings are set by the test case. |
243 | correct fields. | 929 | The machine used is beaglebone-yocto. |
244 | Expected: 1. u-boot-fitImage and u-boot-its can be built | 930 | Expected: 1. The ITS is generated with initramfs support |
245 | 2. The type, load address, entrypoint address and | 931 | 2. All the fields in the kernel node are as expected (matching the |
246 | default values of U-boot image are correct in the | 932 | conf settings) |
247 | Image Tree Source. Not all the fields are tested, | 933 | 3. The kernel is included in all the available configurations and |
248 | only the key fields that wont vary between | 934 | its hash is included in the configuration signature |
249 | different architectures. | 935 | |
250 | Product: oe-core | 936 | Product: oe-core |
251 | Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> | 937 | Author: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> |
252 | based on work by Usama Arif <usama.arif@arm.com> | ||
253 | """ | 938 | """ |
939 | |||
254 | config = """ | 940 | config = """ |
255 | # We need at least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set | 941 | DISTRO = "poky" |
256 | MACHINE = "qemuarm" | 942 | MACHINE = "beaglebone-yocto" |
257 | UBOOT_MACHINE = "am57xx_evm_defconfig" | 943 | INITRAMFS_IMAGE = "core-image-minimal-initramfs" |
258 | SPL_BINARY = "MLO" | 944 | INITRAMFS_SCRIPTS = "" |
945 | UBOOT_MACHINE = "am335x_evm_defconfig" | ||
946 | UBOOT_SIGN_ENABLE = "1" | ||
947 | UBOOT_SIGN_KEYNAME = "beaglebonekey" | ||
948 | UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}" | ||
949 | UBOOT_DTB_BINARY = "u-boot.dtb" | ||
950 | UBOOT_ENTRYPOINT = "0x80000000" | ||
951 | UBOOT_LOADADDRESS = "0x80000000" | ||
952 | UBOOT_RD_LOADADDRESS = "0x88000000" | ||
953 | UBOOT_RD_ENTRYPOINT = "0x88000000" | ||
954 | UBOOT_DTB_LOADADDRESS = "0x82000000" | ||
955 | UBOOT_ARCH = "arm" | ||
956 | UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" | ||
957 | UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" | ||
958 | UBOOT_EXTLINUX = "0" | ||
959 | KERNEL_IMAGETYPE_REPLACEMENT = "zImage" | ||
960 | FIT_KERNEL_COMP_ALG = "none" | ||
961 | FIT_HASH_ALG = "sha256" | ||
962 | """ | ||
963 | config = self._config_add_kernel_classes(config) | ||
964 | config = self._config_add_uboot_env(config) | ||
965 | self.write_config(config) | ||
259 | 966 | ||
260 | # Enable creation of the U-Boot fitImage | 967 | # Retrieve some variables from bitbake |
261 | UBOOT_FITIMAGE_ENABLE = "1" | 968 | bb_vars = self._fit_get_bb_vars([ |
969 | 'FIT_KEY_GENRSA_ARGS', | ||
970 | 'FIT_KEY_REQ_ARGS', | ||
971 | 'FIT_KEY_SIGN_PKCS', | ||
972 | 'FIT_SIGN_NUMBITS', | ||
973 | 'UBOOT_SIGN_KEYDIR', | ||
974 | ]) | ||
262 | 975 | ||
263 | # (U-boot) fitImage properties | 976 | self._gen_signing_key(bb_vars) |
264 | UBOOT_LOADADDRESS = "0x80080000" | 977 | self._test_fitimage(bb_vars) |
265 | UBOOT_ENTRYPOINT = "0x80080000" | 978 | |
266 | UBOOT_FIT_DESC = "A model description" | 979 | def test_fit_image_sign_initramfs_bundle(self): |
980 | """ | ||
981 | Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its) | ||
982 | The FIT settings are set by the test case. | ||
983 | The machine used is beaglebone-yocto. | ||
984 | Expected: 1. The ITS is generated with initramfs bundle support | ||
985 | 2. All the fields in the kernel node are as expected (matching the | ||
986 | conf settings) | ||
987 | 3. The kernel is included in all the available configurations and | ||
988 | its hash is included in the configuration signature | ||
989 | |||
990 | Product: oe-core | ||
991 | Author: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> | ||
992 | """ | ||
267 | 993 | ||
268 | # Enable creation of Kernel fitImage | 994 | config = """ |
269 | KERNEL_IMAGETYPES += " fitImage " | 995 | DISTRO = "poky" |
270 | KERNEL_CLASSES = " kernel-fitimage" | 996 | MACHINE = "beaglebone-yocto" |
997 | INITRAMFS_IMAGE_BUNDLE = "1" | ||
998 | INITRAMFS_IMAGE = "core-image-minimal-initramfs" | ||
999 | INITRAMFS_SCRIPTS = "" | ||
1000 | UBOOT_MACHINE = "am335x_evm_defconfig" | ||
271 | UBOOT_SIGN_ENABLE = "1" | 1001 | UBOOT_SIGN_ENABLE = "1" |
272 | FIT_GENERATE_KEYS = "1" | 1002 | UBOOT_SIGN_KEYNAME = "beaglebonekey" |
273 | UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" | 1003 | UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}" |
274 | UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" | 1004 | UBOOT_DTB_BINARY = "u-boot.dtb" |
275 | UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" | 1005 | UBOOT_ENTRYPOINT = "0x80000000" |
276 | FIT_SIGN_INDIVIDUAL = "1" | 1006 | UBOOT_LOADADDRESS = "0x80000000" |
1007 | UBOOT_DTB_LOADADDRESS = "0x82000000" | ||
1008 | UBOOT_ARCH = "arm" | ||
1009 | UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" | ||
1010 | UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" | ||
1011 | UBOOT_EXTLINUX = "0" | ||
1012 | KERNEL_IMAGETYPE_REPLACEMENT = "zImage" | ||
1013 | FIT_KERNEL_COMP_ALG = "none" | ||
1014 | FIT_HASH_ALG = "sha256" | ||
277 | """ | 1015 | """ |
1016 | config = self._config_add_kernel_classes(config) | ||
1017 | config = self._config_add_uboot_env(config) | ||
278 | self.write_config(config) | 1018 | self.write_config(config) |
1019 | bb_vars = self._fit_get_bb_vars() | ||
1020 | self._gen_signing_key(bb_vars) | ||
1021 | self._test_fitimage(bb_vars) | ||
1022 | |||
1023 | class FitImagePyTests(KernelFitImageBase): | ||
1024 | """Test cases for the fitimage.py module without calling bitbake""" | ||
1025 | |||
1026 | def _test_fitimage_py(self, bb_vars_overrides=None): | ||
1027 | topdir = os.path.join(os.environ['BUILDDIR']) | ||
1028 | fitimage_its_path = os.path.join(topdir, self._testMethodName + '.its') | ||
1029 | |||
1030 | # Provide variables without calling bitbake | ||
1031 | bb_vars = { | ||
1032 | # image-fitimage.conf | ||
1033 | 'FIT_DESC': "Kernel fitImage for a dummy distro", | ||
1034 | 'FIT_HASH_ALG': "sha256", | ||
1035 | 'FIT_SIGN_ALG': "rsa2048", | ||
1036 | 'FIT_PAD_ALG': "pkcs-1.5", | ||
1037 | 'FIT_GENERATE_KEYS': "0", | ||
1038 | 'FIT_SIGN_NUMBITS': "2048", | ||
1039 | 'FIT_KEY_GENRSA_ARGS': "-F4", | ||
1040 | 'FIT_KEY_REQ_ARGS': "-batch -new", | ||
1041 | 'FIT_KEY_SIGN_PKCS': "-x509", | ||
1042 | 'FIT_SIGN_INDIVIDUAL': "0", | ||
1043 | 'FIT_CONF_PREFIX': "conf-", | ||
1044 | 'FIT_SUPPORTED_INITRAMFS_FSTYPES': "cpio.lz4 cpio.lzo cpio.lzma cpio.xz cpio.zst cpio.gz ext2.gz cpio", | ||
1045 | 'FIT_CONF_DEFAULT_DTB': "", | ||
1046 | 'FIT_ADDRESS_CELLS': "1", | ||
1047 | 'FIT_UBOOT_ENV': "", | ||
1048 | # kernel.bbclass | ||
1049 | 'UBOOT_ENTRYPOINT': "0x20008000", | ||
1050 | 'UBOOT_LOADADDRESS': "0x20008000", | ||
1051 | 'INITRAMFS_IMAGE': "", | ||
1052 | 'INITRAMFS_IMAGE_BUNDLE': "", | ||
1053 | # kernel-uboot.bbclass | ||
1054 | 'FIT_KERNEL_COMP_ALG': "gzip", | ||
1055 | 'FIT_KERNEL_COMP_ALG_EXTENSION': ".gz", | ||
1056 | 'UBOOT_MKIMAGE_KERNEL_TYPE': "kernel", | ||
1057 | # uboot-config.bbclass | ||
1058 | 'UBOOT_MKIMAGE_DTCOPTS': "", | ||
1059 | 'UBOOT_MKIMAGE': "uboot-mkimage", | ||
1060 | 'UBOOT_MKIMAGE_SIGN': "uboot-mkimage", | ||
1061 | 'UBOOT_MKIMAGE_SIGN_ARGS': "", | ||
1062 | 'UBOOT_SIGN_ENABLE': "0", | ||
1063 | 'UBOOT_SIGN_KEYDIR': None, | ||
1064 | 'UBOOT_SIGN_KEYNAME': None, | ||
1065 | 'UBOOT_SIGN_IMG_KEYNAME': None, | ||
1066 | # others | ||
1067 | 'MACHINE': "qemux86-64", | ||
1068 | 'UBOOT_ARCH': "x86", | ||
1069 | 'HOST_PREFIX': "x86_64-poky-linux-" | ||
1070 | } | ||
1071 | if bb_vars_overrides: | ||
1072 | bb_vars.update(bb_vars_overrides) | ||
1073 | |||
1074 | root_node = oe.fitimage.ItsNodeRootKernel( | ||
1075 | bb_vars["FIT_DESC"], bb_vars["FIT_ADDRESS_CELLS"], | ||
1076 | bb_vars['HOST_PREFIX'], bb_vars['UBOOT_ARCH'], bb_vars["FIT_CONF_PREFIX"], | ||
1077 | oe.types.boolean(bb_vars['UBOOT_SIGN_ENABLE']), bb_vars["UBOOT_SIGN_KEYDIR"], | ||
1078 | bb_vars["UBOOT_MKIMAGE"], bb_vars["UBOOT_MKIMAGE_DTCOPTS"], | ||
1079 | bb_vars["UBOOT_MKIMAGE_SIGN"], bb_vars["UBOOT_MKIMAGE_SIGN_ARGS"], | ||
1080 | bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG'], bb_vars['FIT_PAD_ALG'], | ||
1081 | bb_vars['UBOOT_SIGN_KEYNAME'], | ||
1082 | oe.types.boolean(bb_vars['FIT_SIGN_INDIVIDUAL']), bb_vars['UBOOT_SIGN_IMG_KEYNAME'] | ||
1083 | ) | ||
1084 | |||
1085 | root_node.fitimage_emit_section_kernel("kernel-1", "linux.bin", "none", | ||
1086 | bb_vars.get('UBOOT_LOADADDRESS'), bb_vars.get('UBOOT_ENTRYPOINT'), | ||
1087 | bb_vars.get('UBOOT_MKIMAGE_KERNEL_TYPE'), bb_vars.get("UBOOT_ENTRYSYMBOL") | ||
1088 | ) | ||
1089 | |||
1090 | dtb_files, _ = FitImageTestCase._get_dtb_files(bb_vars) | ||
1091 | for dtb in dtb_files: | ||
1092 | root_node.fitimage_emit_section_dtb(dtb, os.path.join("a-dir", dtb), | ||
1093 | bb_vars.get("UBOOT_DTB_LOADADDRESS"), bb_vars.get("UBOOT_DTBO_LOADADDRESS")) | ||
1094 | |||
1095 | if bb_vars.get('FIT_UBOOT_ENV'): | ||
1096 | root_node.fitimage_emit_section_boot_script( | ||
1097 | "bootscr-" + bb_vars['FIT_UBOOT_ENV'], bb_vars['FIT_UBOOT_ENV']) | ||
1098 | |||
1099 | if bb_vars['MACHINE'] == "qemux86-64": # Not really the right if | ||
1100 | root_node.fitimage_emit_section_setup("setup-1", "setup1.bin") | ||
1101 | |||
1102 | if bb_vars.get('INITRAMFS_IMAGE') and bb_vars.get("INITRAMFS_IMAGE_BUNDLE") != "1": | ||
1103 | root_node.fitimage_emit_section_ramdisk("ramdisk-1", "a-dir/a-initramfs-1", | ||
1104 | "core-image-minimal-initramfs", | ||
1105 | bb_vars.get("UBOOT_RD_LOADADDRESS"), bb_vars.get("UBOOT_RD_ENTRYPOINT")) | ||
1106 | |||
1107 | root_node.fitimage_emit_section_config(bb_vars['FIT_CONF_DEFAULT_DTB']) | ||
1108 | root_node.write_its_file(fitimage_its_path) | ||
1109 | |||
1110 | self.assertExists(fitimage_its_path, "%s image tree source doesn't exist" % (fitimage_its_path)) | ||
1111 | self.logger.debug("Checking its: %s" % fitimage_its_path) | ||
1112 | self._check_its_file(bb_vars, fitimage_its_path) | ||
1113 | |||
1114 | def test_fitimage_py_default(self): | ||
1115 | self._test_fitimage_py() | ||
1116 | |||
1117 | def test_fitimage_py_default_dtb(self): | ||
1118 | bb_vars_overrides = { | ||
1119 | 'KERNEL_DEVICETREE': "one.dtb two.dtb three.dtb", | ||
1120 | 'FIT_CONF_DEFAULT_DTB': "two.dtb" | ||
1121 | } | ||
1122 | self._test_fitimage_py(bb_vars_overrides) | ||
1123 | |||
1124 | |||
1125 | class UBootFitImageTests(FitImageTestCase): | ||
1126 | """Test cases for the uboot-sign bbclass""" | ||
279 | 1127 | ||
280 | # The U-Boot fitImage is created as part of the U-Boot recipe | 1128 | BOOTLOADER_RECIPE = "virtual/bootloader" |
281 | bitbake("virtual/bootloader") | 1129 | |
282 | 1130 | def _fit_get_bb_vars(self, additional_vars=[]): | |
283 | deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') | 1131 | """Get bb_vars as needed by _test_sign_fit_image |
284 | machine = get_bb_var('MACHINE') | 1132 | |
285 | fitimage_its_path = os.path.join(deploy_dir_image, | 1133 | Call the get_bb_vars function once and get all variables needed by the test case. |
286 | "u-boot-its-%s" % (machine,)) | 1134 | """ |
287 | fitimage_path = os.path.join(deploy_dir_image, | 1135 | internal_used = { |
288 | "u-boot-fitImage-%s" % (machine,)) | 1136 | 'DEPLOY_DIR_IMAGE', |
289 | 1137 | 'FIT_HASH_ALG', | |
290 | self.assertTrue(os.path.exists(fitimage_its_path), | 1138 | 'FIT_KEY_GENRSA_ARGS', |
291 | "%s image tree source doesn't exist" % (fitimage_its_path)) | 1139 | 'FIT_KEY_REQ_ARGS', |
292 | self.assertTrue(os.path.exists(fitimage_path), | 1140 | 'FIT_KEY_SIGN_PKCS', |
293 | "%s FIT image doesn't exist" % (fitimage_path)) | 1141 | 'FIT_SIGN_ALG', |
294 | 1142 | 'FIT_SIGN_INDIVIDUAL', | |
295 | # Check that the type, load address, entrypoint address and default | 1143 | 'FIT_SIGN_NUMBITS', |
296 | # values for kernel and ramdisk in Image Tree Source are as expected. | 1144 | 'MACHINE', |
297 | # The order of fields in the below array is important. Not all the | 1145 | 'SPL_MKIMAGE_SIGN_ARGS', |
298 | # fields are tested, only the key fields that wont vary between | 1146 | 'SPL_SIGN_ENABLE', |
299 | # different architectures. | 1147 | 'SPL_SIGN_KEYNAME', |
1148 | 'UBOOT_ARCH', | ||
1149 | 'UBOOT_DTB_BINARY', | ||
1150 | 'UBOOT_DTB_IMAGE', | ||
1151 | 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT', | ||
1152 | 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS', | ||
1153 | 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE', | ||
1154 | 'UBOOT_FIT_CONF_USER_LOADABLES', | ||
1155 | 'UBOOT_FIT_DESC', | ||
1156 | 'UBOOT_FIT_HASH_ALG', | ||
1157 | 'UBOOT_FIT_SIGN_ALG', | ||
1158 | 'UBOOT_FIT_TEE_ENTRYPOINT', | ||
1159 | 'UBOOT_FIT_TEE_LOADADDRESS', | ||
1160 | 'UBOOT_FIT_TEE', | ||
1161 | 'UBOOT_FIT_UBOOT_ENTRYPOINT', | ||
1162 | 'UBOOT_FIT_UBOOT_LOADADDRESS', | ||
1163 | 'UBOOT_FIT_USER_SETTINGS', | ||
1164 | 'UBOOT_FITIMAGE_ENABLE', | ||
1165 | 'UBOOT_NODTB_BINARY', | ||
1166 | 'UBOOT_SIGN_ENABLE', | ||
1167 | 'UBOOT_SIGN_IMG_KEYNAME', | ||
1168 | 'UBOOT_SIGN_KEYDIR', | ||
1169 | 'UBOOT_SIGN_KEYNAME', | ||
1170 | } | ||
1171 | bb_vars = get_bb_vars(list(internal_used | set(additional_vars)), UBootFitImageTests.BOOTLOADER_RECIPE) | ||
1172 | self.logger.debug("bb_vars: %s" % pprint.pformat(bb_vars, indent=4)) | ||
1173 | return bb_vars | ||
1174 | |||
1175 | def _bitbake_fit_image(self, bb_vars): | ||
1176 | """Bitbake the bootloader and return the paths to the its file and the FIT image""" | ||
1177 | bitbake(UBootFitImageTests.BOOTLOADER_RECIPE) | ||
1178 | |||
1179 | deploy_dir_image = bb_vars['DEPLOY_DIR_IMAGE'] | ||
1180 | machine = bb_vars['MACHINE'] | ||
1181 | fitimage_its_path = os.path.join(deploy_dir_image, "u-boot-its-%s" % machine) | ||
1182 | fitimage_path = os.path.join(deploy_dir_image, "u-boot-fitImage-%s" % machine) | ||
1183 | return (fitimage_its_path, fitimage_path) | ||
1184 | |||
1185 | def _get_req_its_paths(self, bb_vars): | ||
1186 | # image nodes | ||
1187 | images = [ 'uboot', 'fdt', ] | ||
1188 | if bb_vars['UBOOT_FIT_TEE'] == "1": | ||
1189 | images.append('tee') | ||
1190 | if bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE'] == "1": | ||
1191 | images.append('atf') | ||
1192 | # if bb_vars['UBOOT_FIT_USER_SETTINGS']: | ||
1193 | |||
1194 | # configuration nodes | ||
1195 | configurations = [ 'conf'] | ||
1196 | |||
1197 | # Create a list of paths for all image and configuration nodes | ||
1198 | req_its_paths = [] | ||
1199 | for image in images: | ||
1200 | req_its_paths.append(['/', 'images', image]) | ||
1201 | if bb_vars['SPL_SIGN_ENABLE'] == "1": | ||
1202 | req_its_paths.append(['/', 'images', image, 'signature']) | ||
1203 | for configuration in configurations: | ||
1204 | req_its_paths.append(['/', 'configurations', configuration]) | ||
1205 | return req_its_paths | ||
1206 | |||
1207 | def _get_req_its_fields(self, bb_vars): | ||
1208 | loadables = ["uboot"] | ||
300 | its_field_check = [ | 1209 | its_field_check = [ |
301 | 'description = "A model description";', | 1210 | 'description = "%s";' % bb_vars['UBOOT_FIT_DESC'], |
1211 | 'description = "U-Boot image";', | ||
1212 | 'data = /incbin/("%s");' % bb_vars['UBOOT_NODTB_BINARY'], | ||
302 | 'type = "standalone";', | 1213 | 'type = "standalone";', |
303 | 'load = <0x80080000>;', | 1214 | 'os = "u-boot";', |
304 | 'entry = <0x80080000>;', | 1215 | 'arch = "%s";' % bb_vars['UBOOT_ARCH'], |
305 | 'default = "conf";', | 1216 | 'compression = "none";', |
306 | 'loadables = "uboot";', | 1217 | 'load = <%s>;' % bb_vars['UBOOT_FIT_UBOOT_LOADADDRESS'], |
307 | 'fdt = "fdt";' | 1218 | 'entry = <%s>;' % bb_vars['UBOOT_FIT_UBOOT_ENTRYPOINT'], |
1219 | 'description = "U-Boot FDT";', | ||
1220 | 'data = /incbin/("%s");' % bb_vars['UBOOT_DTB_BINARY'], | ||
1221 | 'type = "flat_dt";', | ||
1222 | 'arch = "%s";' % bb_vars['UBOOT_ARCH'], | ||
1223 | 'compression = "none";', | ||
1224 | ] | ||
1225 | if bb_vars['UBOOT_FIT_TEE'] == "1": | ||
1226 | its_field_check += [ | ||
1227 | 'description = "Trusted Execution Environment";', | ||
1228 | 'data = /incbin/("%s");' % bb_vars['UBOOT_FIT_TEE_IMAGE'], | ||
1229 | 'type = "tee";', | ||
1230 | 'arch = "%s";' % bb_vars['UBOOT_ARCH'], | ||
1231 | 'os = "tee";', | ||
1232 | 'load = <%s>;' % bb_vars['UBOOT_FIT_TEE_LOADADDRESS'], | ||
1233 | 'entry = <%s>;' % bb_vars['UBOOT_FIT_TEE_ENTRYPOINT'], | ||
1234 | 'compression = "none";', | ||
308 | ] | 1235 | ] |
1236 | loadables.insert(0, "tee") | ||
1237 | if bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE'] == "1": | ||
1238 | its_field_check += [ | ||
1239 | 'description = "ARM Trusted Firmware";', | ||
1240 | 'data = /incbin/("%s");' % bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE'], | ||
1241 | 'type = "firmware";', | ||
1242 | 'arch = "%s";' % bb_vars['UBOOT_ARCH'], | ||
1243 | 'os = "arm-trusted-firmware";', | ||
1244 | 'load = <%s>;' % bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS'], | ||
1245 | 'entry = <%s>;' % bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT'], | ||
1246 | 'compression = "none";', | ||
1247 | ] | ||
1248 | loadables.insert(0, "atf") | ||
1249 | its_field_check += [ | ||
1250 | 'default = "conf";', | ||
1251 | 'description = "Boot with signed U-Boot FIT";', | ||
1252 | 'loadables = "%s";' % '", "'.join(loadables), | ||
1253 | 'fdt = "fdt";', | ||
1254 | ] | ||
1255 | return its_field_check | ||
1256 | |||
1257 | def _get_req_sigvalues_config(self, bb_vars): | ||
1258 | # COnfigurations are not signed by uboot-sign | ||
1259 | return {} | ||
1260 | |||
1261 | def _get_req_sigvalues_image(self, bb_vars): | ||
1262 | if bb_vars['SPL_SIGN_ENABLE'] != "1": | ||
1263 | return {} | ||
1264 | req_sigvalues_image = { | ||
1265 | 'algo': '"%s,%s"' % (bb_vars['UBOOT_FIT_HASH_ALG'], bb_vars['UBOOT_FIT_SIGN_ALG']), | ||
1266 | 'key-name-hint': '"%s"' % bb_vars['SPL_SIGN_KEYNAME'], | ||
1267 | } | ||
1268 | return req_sigvalues_image | ||
309 | 1269 | ||
310 | with open(fitimage_its_path) as its_file: | 1270 | def _get_req_sections(self, bb_vars): |
311 | field_index = 0 | 1271 | """Generate the expected output of dumpimage for beaglebone targets |
312 | for line in its_file: | 1272 | |
313 | if field_index == len(its_field_check): | 1273 | The dict generated by this function is supposed to be compared against |
314 | break | 1274 | the dict which is generated by the _dump_fitimage function. |
315 | if its_field_check[field_index] in line: | 1275 | """ |
316 | field_index +=1 | 1276 | loadables = ['uboot'] |
1277 | req_sections = { | ||
1278 | "uboot": { | ||
1279 | "Type": "Standalone Program", | ||
1280 | "Load Address": bb_vars['UBOOT_FIT_UBOOT_LOADADDRESS'], | ||
1281 | "Entry Point": bb_vars['UBOOT_FIT_UBOOT_ENTRYPOINT'], | ||
1282 | }, | ||
1283 | "fdt": { | ||
1284 | "Type": "Flat Device Tree", | ||
1285 | } | ||
1286 | } | ||
1287 | if bb_vars['UBOOT_FIT_TEE'] == "1": | ||
1288 | loadables.insert(0, "tee") | ||
1289 | req_sections['tee'] = { | ||
1290 | "Type": "Trusted Execution Environment Image", | ||
1291 | # "Load Address": bb_vars['UBOOT_FIT_TEE_LOADADDRESS'], not printed by mkimage? | ||
1292 | # "Entry Point": bb_vars['UBOOT_FIT_TEE_ENTRYPOINT'], not printed by mkimage? | ||
1293 | } | ||
1294 | if bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE'] == "1": | ||
1295 | loadables.insert(0, "atf") | ||
1296 | req_sections['atf'] = { | ||
1297 | "Type": "Firmware", | ||
1298 | "Load Address": bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS'], | ||
1299 | # "Entry Point": bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT'], not printed by mkimage? | ||
1300 | } | ||
1301 | req_sections["conf"] = { | ||
1302 | "Kernel": "unavailable", | ||
1303 | "FDT": "fdt", | ||
1304 | "Loadables": ','.join(loadables), | ||
1305 | } | ||
1306 | |||
1307 | # Add signing related properties if needed | ||
1308 | uboot_fit_hash_alg = bb_vars['UBOOT_FIT_HASH_ALG'] | ||
1309 | uboot_fit_sign_alg = bb_vars['UBOOT_FIT_SIGN_ALG'] | ||
1310 | spl_sign_enable = bb_vars['SPL_SIGN_ENABLE'] | ||
1311 | spl_sign_keyname = bb_vars['SPL_SIGN_KEYNAME'] | ||
1312 | num_signatures = 0 | ||
1313 | if spl_sign_enable == "1": | ||
1314 | for section in req_sections: | ||
1315 | if not section.startswith('conf'): | ||
1316 | req_sections[section]['Sign algo'] = "%s,%s:%s" % \ | ||
1317 | (uboot_fit_hash_alg, uboot_fit_sign_alg, spl_sign_keyname) | ||
1318 | num_signatures += 1 | ||
1319 | return (req_sections, num_signatures) | ||
1320 | |||
1321 | def _check_signing(self, bb_vars, sections, num_signatures, uboot_tools_bindir, fitimage_path): | ||
1322 | if bb_vars['UBOOT_FITIMAGE_ENABLE'] == '1' and bb_vars['SPL_SIGN_ENABLE'] == "1": | ||
1323 | self.logger.debug("Verifying signatures in the FIT image") | ||
1324 | else: | ||
1325 | self.logger.debug("FIT image is not signed. Signature verification is not needed.") | ||
1326 | return | ||
1327 | |||
1328 | uboot_fit_hash_alg = bb_vars['UBOOT_FIT_HASH_ALG'] | ||
1329 | uboot_fit_sign_alg = bb_vars['UBOOT_FIT_SIGN_ALG'] | ||
1330 | spl_sign_keyname = bb_vars['SPL_SIGN_KEYNAME'] | ||
1331 | fit_sign_alg_len = FitImageTestCase.MKIMAGE_SIGNATURE_LENGTHS[uboot_fit_sign_alg] | ||
1332 | for section, values in sections.items(): | ||
1333 | # Configuration nodes are always signed with UBOOT_SIGN_KEYNAME (if UBOOT_SIGN_ENABLE = "1") | ||
1334 | if section.startswith("conf"): | ||
1335 | # uboot-sign does not sign configuration nodes | ||
1336 | pass | ||
1337 | else: | ||
1338 | # uboot-sign does not add hash nodes, only image signatures | ||
1339 | sign_algo = values.get('Sign algo', None) | ||
1340 | req_sign_algo = "%s,%s:%s" % (uboot_fit_hash_alg, uboot_fit_sign_alg, spl_sign_keyname) | ||
1341 | self.assertEqual(sign_algo, req_sign_algo, 'Signature algorithm for %s not expected value' % section) | ||
1342 | sign_value = values.get('Sign value', None) | ||
1343 | self.assertEqual(len(sign_value), fit_sign_alg_len, 'Signature value for section %s not expected length' % section) | ||
1344 | |||
1345 | # Search for the string passed to mkimage in each signed section of the FIT image. | ||
1346 | # Looks like mkimage supports to add a comment but does not support to read it back. | ||
1347 | a_comment = FitImageTestCase._get_uboot_mkimage_sign_args(bb_vars['SPL_MKIMAGE_SIGN_ARGS']) | ||
1348 | self.logger.debug("a_comment: %s" % a_comment) | ||
1349 | if a_comment: | ||
1350 | found_comments = FitImageTestCase._find_string_in_bin_file(fitimage_path, a_comment) | ||
1351 | self.assertEqual(found_comments, num_signatures, "Expected %d signed and commented (%s) sections in the fitImage." % | ||
1352 | (num_signatures, a_comment)) | ||
1353 | |||
1354 | def _check_kernel_dtb(self, bb_vars): | ||
1355 | """ | ||
1356 | Check if the device-tree from U-Boot has the kernel public key(s). | ||
1357 | |||
1358 | The concat_dtb function of the uboot-sign.bbclass injects the public keys | ||
1359 | which are required for verifying the kernel at run-time into the DTB from | ||
1360 | U-Boot. The following example is from a build with FIT_SIGN_INDIVIDUAL | ||
1361 | set to "1". If it is set to "0" the key-the-kernel-image-key node is not | ||
1362 | present. | ||
1363 | / { | ||
1364 | ... | ||
1365 | signature { | ||
1366 | key-the-kernel-image-key { | ||
1367 | required = "image"; | ||
1368 | algo = "sha256,rsa2048"; | ||
1369 | ... | ||
1370 | }; | ||
1371 | key-the-kernel-config-key { | ||
1372 | required = "conf"; | ||
1373 | algo = "sha256,rsa2048"; | ||
1374 | ... | ||
1375 | }; | ||
1376 | }; | ||
1377 | """ | ||
1378 | # Setup u-boot-tools-native | ||
1379 | dtc_bindir = FitImageTestCase._setup_native('dtc-native') | ||
1380 | |||
1381 | # Check if 1 or 2 signature sections are in the DTB. | ||
1382 | uboot_dtb_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], bb_vars['UBOOT_DTB_IMAGE']) | ||
1383 | algo = "%s,%s" % (bb_vars['FIT_HASH_ALG'], bb_vars['FIT_SIGN_ALG']) | ||
1384 | if bb_vars['FIT_SIGN_INDIVIDUAL'] == "1": | ||
1385 | uboot_sign_img_keyname = bb_vars['UBOOT_SIGN_IMG_KEYNAME'] | ||
1386 | key_dtb_path = "/signature/key-" + uboot_sign_img_keyname | ||
1387 | self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "required", "image") | ||
1388 | self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "algo", algo) | ||
1389 | self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "key-name-hint", uboot_sign_img_keyname) | ||
1390 | |||
1391 | uboot_sign_keyname = bb_vars['UBOOT_SIGN_KEYNAME'] | ||
1392 | key_dtb_path = "/signature/key-" + uboot_sign_keyname | ||
1393 | self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "required", "conf") | ||
1394 | self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "algo", algo) | ||
1395 | self._verify_dtb_property(dtc_bindir, uboot_dtb_path, key_dtb_path, "key-name-hint", uboot_sign_keyname) | ||
317 | 1396 | ||
318 | if field_index != len(its_field_check): # if its equal, the test passed | ||
319 | self.assertTrue(field_index == len(its_field_check), | ||
320 | "Fields in Image Tree Source File %s did not match, error in finding %s" | ||
321 | % (fitimage_its_path, its_field_check[field_index])) | ||
322 | 1397 | ||
323 | def test_uboot_sign_fit_image(self): | 1398 | def test_uboot_fit_image(self): |
324 | """ | 1399 | """ |
325 | Summary: Check if Uboot FIT image and Image Tree Source | 1400 | Summary: Check if Uboot FIT image and Image Tree Source |
326 | (its) are built and the Image Tree Source has the | 1401 | (its) are built and the Image Tree Source has the |
327 | correct fields, in the scenario where the Kernel | 1402 | correct fields. |
328 | is also creating/signing it's fitImage. | ||
329 | Expected: 1. u-boot-fitImage and u-boot-its can be built | 1403 | Expected: 1. u-boot-fitImage and u-boot-its can be built |
330 | 2. The type, load address, entrypoint address and | 1404 | 2. The type, load address, entrypoint address and |
331 | default values of U-boot image are correct in the | 1405 | default values of U-boot image are correct in the |
@@ -349,61 +1423,10 @@ UBOOT_FITIMAGE_ENABLE = "1" | |||
349 | UBOOT_LOADADDRESS = "0x80080000" | 1423 | UBOOT_LOADADDRESS = "0x80080000" |
350 | UBOOT_ENTRYPOINT = "0x80080000" | 1424 | UBOOT_ENTRYPOINT = "0x80080000" |
351 | UBOOT_FIT_DESC = "A model description" | 1425 | UBOOT_FIT_DESC = "A model description" |
352 | KERNEL_IMAGETYPES += " fitImage " | ||
353 | KERNEL_CLASSES = " kernel-fitimage " | ||
354 | INHERIT += "test-mkimage-wrapper" | ||
355 | UBOOT_SIGN_ENABLE = "1" | ||
356 | FIT_GENERATE_KEYS = "1" | ||
357 | UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" | ||
358 | UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" | ||
359 | UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" | ||
360 | FIT_SIGN_INDIVIDUAL = "1" | ||
361 | UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'" | ||
362 | """ | 1426 | """ |
363 | self.write_config(config) | 1427 | self.write_config(config) |
364 | 1428 | bb_vars = self._fit_get_bb_vars() | |
365 | # The U-Boot fitImage is created as part of the U-Boot recipe | 1429 | self._test_fitimage(bb_vars) |
366 | bitbake("virtual/bootloader") | ||
367 | |||
368 | deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') | ||
369 | machine = get_bb_var('MACHINE') | ||
370 | fitimage_its_path = os.path.join(deploy_dir_image, | ||
371 | "u-boot-its-%s" % (machine,)) | ||
372 | fitimage_path = os.path.join(deploy_dir_image, | ||
373 | "u-boot-fitImage-%s" % (machine,)) | ||
374 | |||
375 | self.assertTrue(os.path.exists(fitimage_its_path), | ||
376 | "%s image tree source doesn't exist" % (fitimage_its_path)) | ||
377 | self.assertTrue(os.path.exists(fitimage_path), | ||
378 | "%s FIT image doesn't exist" % (fitimage_path)) | ||
379 | |||
380 | # Check that the type, load address, entrypoint address and default | ||
381 | # values for kernel and ramdisk in Image Tree Source are as expected. | ||
382 | # The order of fields in the below array is important. Not all the | ||
383 | # fields are tested, only the key fields that wont vary between | ||
384 | # different architectures. | ||
385 | its_field_check = [ | ||
386 | 'description = "A model description";', | ||
387 | 'type = "standalone";', | ||
388 | 'load = <0x80080000>;', | ||
389 | 'entry = <0x80080000>;', | ||
390 | 'default = "conf";', | ||
391 | 'loadables = "uboot";', | ||
392 | 'fdt = "fdt";' | ||
393 | ] | ||
394 | |||
395 | with open(fitimage_its_path) as its_file: | ||
396 | field_index = 0 | ||
397 | for line in its_file: | ||
398 | if field_index == len(its_field_check): | ||
399 | break | ||
400 | if its_field_check[field_index] in line: | ||
401 | field_index +=1 | ||
402 | |||
403 | if field_index != len(its_field_check): # if its equal, the test passed | ||
404 | self.assertTrue(field_index == len(its_field_check), | ||
405 | "Fields in Image Tree Source File %s did not match, error in finding %s" | ||
406 | % (fitimage_its_path, its_field_check[field_index])) | ||
407 | 1430 | ||
408 | 1431 | ||
409 | def test_sign_standalone_uboot_fit_image(self): | 1432 | def test_sign_standalone_uboot_fit_image(self): |
@@ -426,15 +1449,11 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'" | |||
426 | Usama Arif <usama.arif@arm.com> | 1449 | Usama Arif <usama.arif@arm.com> |
427 | """ | 1450 | """ |
428 | config = """ | 1451 | config = """ |
429 | # There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at | 1452 | # There's no U-boot defconfig with CONFIG_FIT_SIGNATURE yet, so we need at |
430 | # least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set | 1453 | # least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set |
431 | MACHINE = "qemuarm" | 1454 | MACHINE = "qemuarm" |
432 | UBOOT_MACHINE = "am57xx_evm_defconfig" | 1455 | UBOOT_MACHINE = "am57xx_evm_defconfig" |
433 | SPL_BINARY = "MLO" | 1456 | SPL_BINARY = "MLO" |
434 | # The kernel-fitimage class is a dependency even if we're only | ||
435 | # creating/signing the U-Boot fitImage | ||
436 | KERNEL_CLASSES = " kernel-fitimage" | ||
437 | INHERIT += "test-mkimage-wrapper" | ||
438 | # Enable creation and signing of the U-Boot fitImage | 1457 | # Enable creation and signing of the U-Boot fitImage |
439 | UBOOT_FITIMAGE_ENABLE = "1" | 1458 | UBOOT_FITIMAGE_ENABLE = "1" |
440 | SPL_SIGN_ENABLE = "1" | 1459 | SPL_SIGN_ENABLE = "1" |
@@ -452,106 +1471,9 @@ UBOOT_FIT_GENERATE_KEYS = "1" | |||
452 | UBOOT_FIT_HASH_ALG = "sha256" | 1471 | UBOOT_FIT_HASH_ALG = "sha256" |
453 | """ | 1472 | """ |
454 | self.write_config(config) | 1473 | self.write_config(config) |
1474 | bb_vars = self._fit_get_bb_vars() | ||
1475 | self._test_fitimage(bb_vars) | ||
455 | 1476 | ||
456 | # The U-Boot fitImage is created as part of the U-Boot recipe | ||
457 | bitbake("virtual/bootloader") | ||
458 | |||
459 | image_type = "core-image-minimal" | ||
460 | deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') | ||
461 | machine = get_bb_var('MACHINE') | ||
462 | fitimage_its_path = os.path.join(deploy_dir_image, | ||
463 | "u-boot-its-%s" % (machine,)) | ||
464 | fitimage_path = os.path.join(deploy_dir_image, | ||
465 | "u-boot-fitImage-%s" % (machine,)) | ||
466 | |||
467 | self.assertTrue(os.path.exists(fitimage_its_path), | ||
468 | "%s image tree source doesn't exist" % (fitimage_its_path)) | ||
469 | self.assertTrue(os.path.exists(fitimage_path), | ||
470 | "%s FIT image doesn't exist" % (fitimage_path)) | ||
471 | |||
472 | req_itspaths = [ | ||
473 | ['/', 'images', 'uboot'], | ||
474 | ['/', 'images', 'uboot', 'signature'], | ||
475 | ['/', 'images', 'fdt'], | ||
476 | ['/', 'images', 'fdt', 'signature'], | ||
477 | ] | ||
478 | |||
479 | itspath = [] | ||
480 | itspaths = [] | ||
481 | linect = 0 | ||
482 | sigs = {} | ||
483 | with open(fitimage_its_path) as its_file: | ||
484 | linect += 1 | ||
485 | for line in its_file: | ||
486 | line = line.strip() | ||
487 | if line.endswith('};'): | ||
488 | itspath.pop() | ||
489 | elif line.endswith('{'): | ||
490 | itspath.append(line[:-1].strip()) | ||
491 | itspaths.append(itspath[:]) | ||
492 | elif itspath and itspath[-1] == 'signature': | ||
493 | itsdotpath = '.'.join(itspath) | ||
494 | if not itsdotpath in sigs: | ||
495 | sigs[itsdotpath] = {} | ||
496 | if not '=' in line or not line.endswith(';'): | ||
497 | self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) | ||
498 | key, value = line.split('=', 1) | ||
499 | sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') | ||
500 | |||
501 | for reqpath in req_itspaths: | ||
502 | if not reqpath in itspaths: | ||
503 | self.fail('Missing section in its file: %s' % reqpath) | ||
504 | |||
505 | reqsigvalues_image = { | ||
506 | 'algo': '"sha256,rsa2048"', | ||
507 | 'key-name-hint': '"spl-oe-selftest"', | ||
508 | } | ||
509 | |||
510 | for itspath, values in sigs.items(): | ||
511 | reqsigvalues = reqsigvalues_image | ||
512 | for reqkey, reqvalue in reqsigvalues.items(): | ||
513 | value = values.get(reqkey, None) | ||
514 | if value is None: | ||
515 | self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) | ||
516 | self.assertEqual(value, reqvalue) | ||
517 | |||
518 | # Dump the image to see if it really got signed | ||
519 | bitbake("u-boot-tools-native -c addto_recipe_sysroot") | ||
520 | result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=') | ||
521 | recipe_sysroot_native = result.output.split('=')[1].strip('"') | ||
522 | dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage') | ||
523 | result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path)) | ||
524 | in_signed = None | ||
525 | signed_sections = {} | ||
526 | for line in result.output.splitlines(): | ||
527 | if line.startswith((' Image')): | ||
528 | in_signed = re.search(r'\((.*)\)', line).groups()[0] | ||
529 | elif re.match(' \w', line): | ||
530 | in_signed = None | ||
531 | elif in_signed: | ||
532 | if not in_signed in signed_sections: | ||
533 | signed_sections[in_signed] = {} | ||
534 | key, value = line.split(':', 1) | ||
535 | signed_sections[in_signed][key.strip()] = value.strip() | ||
536 | self.assertIn('uboot', signed_sections) | ||
537 | self.assertIn('fdt', signed_sections) | ||
538 | for signed_section, values in signed_sections.items(): | ||
539 | value = values.get('Sign algo', None) | ||
540 | self.assertEqual(value, 'sha256,rsa2048:spl-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) | ||
541 | value = values.get('Sign value', None) | ||
542 | self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) | ||
543 | |||
544 | # Check for SPL_MKIMAGE_SIGN_ARGS | ||
545 | result = runCmd('bitbake -e virtual/bootloader | grep ^T=') | ||
546 | tempdir = result.output.split('=', 1)[1].strip().strip('') | ||
547 | result = runCmd('grep "a smart U-Boot comment" %s/run.do_uboot_assemble_fitimage' % tempdir, ignore_status=True) | ||
548 | self.assertEqual(result.status, 0, 'SPL_MKIMAGE_SIGN_ARGS value did not get used') | ||
549 | |||
550 | # Check for evidence of test-mkimage-wrapper class | ||
551 | result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True) | ||
552 | self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work') | ||
553 | result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True) | ||
554 | self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work') | ||
555 | 1477 | ||
556 | def test_sign_cascaded_uboot_fit_image(self): | 1478 | def test_sign_cascaded_uboot_fit_image(self): |
557 | """ | 1479 | """ |
@@ -565,9 +1487,9 @@ UBOOT_FIT_HASH_ALG = "sha256" | |||
565 | via UBOOT_FIT_GENERATE_KEYS) | 1487 | via UBOOT_FIT_GENERATE_KEYS) |
566 | 3) Dumping the FIT image indicates signature values | 1488 | 3) Dumping the FIT image indicates signature values |
567 | are present | 1489 | are present |
568 | 4) Examination of the do_uboot_assemble_fitimage | 1490 | 4) Examination of the do_uboot_assemble_fitimage that |
569 | runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN | 1491 | UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN and SPL_MKIMAGE_SIGN_ARGS |
570 | and SPL_MKIMAGE_SIGN_ARGS are working as expected. | 1492 | are working as expected. |
571 | Product: oe-core | 1493 | Product: oe-core |
572 | Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon | 1494 | Author: Klaus Heinrich Kiwi <klaus@linux.vnet.ibm.com> based upon |
573 | work by Paul Eggleton <paul.eggleton@microsoft.com> and | 1495 | work by Paul Eggleton <paul.eggleton@microsoft.com> and |
@@ -588,7 +1510,7 @@ UBOOT_DTB_BINARY = "u-boot.dtb" | |||
588 | UBOOT_ENTRYPOINT = "0x80000000" | 1510 | UBOOT_ENTRYPOINT = "0x80000000" |
589 | UBOOT_LOADADDRESS = "0x80000000" | 1511 | UBOOT_LOADADDRESS = "0x80000000" |
590 | UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" | 1512 | UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" |
591 | UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded Kernel comment'" | 1513 | UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'" |
592 | UBOOT_DTB_LOADADDRESS = "0x82000000" | 1514 | UBOOT_DTB_LOADADDRESS = "0x82000000" |
593 | UBOOT_ARCH = "arm" | 1515 | UBOOT_ARCH = "arm" |
594 | SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" | 1516 | SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" |
@@ -596,251 +1518,214 @@ SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'" | |||
596 | UBOOT_EXTLINUX = "0" | 1518 | UBOOT_EXTLINUX = "0" |
597 | UBOOT_FIT_GENERATE_KEYS = "1" | 1519 | UBOOT_FIT_GENERATE_KEYS = "1" |
598 | UBOOT_FIT_HASH_ALG = "sha256" | 1520 | UBOOT_FIT_HASH_ALG = "sha256" |
599 | KERNEL_IMAGETYPES += " fitImage " | ||
600 | KERNEL_CLASSES = " kernel-fitimage " | ||
601 | INHERIT += "test-mkimage-wrapper" | ||
602 | UBOOT_SIGN_ENABLE = "1" | 1521 | UBOOT_SIGN_ENABLE = "1" |
603 | FIT_GENERATE_KEYS = "1" | ||
604 | UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" | 1522 | UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" |
605 | UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" | ||
606 | UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" | 1523 | UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" |
607 | FIT_SIGN_INDIVIDUAL = "1" | ||
608 | """ | 1524 | """ |
609 | self.write_config(config) | 1525 | self.write_config(config) |
1526 | bb_vars = self._fit_get_bb_vars() | ||
610 | 1527 | ||
611 | # The U-Boot fitImage is created as part of the U-Boot recipe | 1528 | self._gen_signing_key(bb_vars) |
612 | bitbake("virtual/bootloader") | 1529 | self._test_fitimage(bb_vars) |
613 | 1530 | self._check_kernel_dtb(bb_vars) | |
614 | image_type = "core-image-minimal" | ||
615 | deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') | ||
616 | machine = get_bb_var('MACHINE') | ||
617 | fitimage_its_path = os.path.join(deploy_dir_image, | ||
618 | "u-boot-its-%s" % (machine,)) | ||
619 | fitimage_path = os.path.join(deploy_dir_image, | ||
620 | "u-boot-fitImage-%s" % (machine,)) | ||
621 | |||
622 | self.assertTrue(os.path.exists(fitimage_its_path), | ||
623 | "%s image tree source doesn't exist" % (fitimage_its_path)) | ||
624 | self.assertTrue(os.path.exists(fitimage_path), | ||
625 | "%s FIT image doesn't exist" % (fitimage_path)) | ||
626 | |||
627 | req_itspaths = [ | ||
628 | ['/', 'images', 'uboot'], | ||
629 | ['/', 'images', 'uboot', 'signature'], | ||
630 | ['/', 'images', 'fdt'], | ||
631 | ['/', 'images', 'fdt', 'signature'], | ||
632 | ] | ||
633 | 1531 | ||
634 | itspath = [] | 1532 | def test_uboot_atf_tee_fit_image(self): |
635 | itspaths = [] | 1533 | """ |
636 | linect = 0 | 1534 | Summary: Check if U-boot FIT image and Image Tree Source |
637 | sigs = {} | 1535 | (its) are built and the Image Tree Source has the |
638 | with open(fitimage_its_path) as its_file: | 1536 | correct fields. |
639 | linect += 1 | 1537 | Expected: 1. Create atf and tee dummy images |
640 | for line in its_file: | 1538 | 2. Both u-boot-fitImage and u-boot-its can be built |
641 | line = line.strip() | 1539 | 3. The os, load address, entrypoint address and |
642 | if line.endswith('};'): | 1540 | default values of U-boot, ATF and TEE images are |
643 | itspath.pop() | 1541 | correct in the Image Tree Source. Not all the |
644 | elif line.endswith('{'): | 1542 | fields are tested, only the key fields that wont |
645 | itspath.append(line[:-1].strip()) | 1543 | vary between different architectures. |
646 | itspaths.append(itspath[:]) | 1544 | Product: oe-core |
647 | elif itspath and itspath[-1] == 'signature': | 1545 | Author: Jamin Lin <jamin_lin@aspeedtech.com> |
648 | itsdotpath = '.'.join(itspath) | 1546 | """ |
649 | if not itsdotpath in sigs: | 1547 | config = """ |
650 | sigs[itsdotpath] = {} | 1548 | # We need at least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set |
651 | if not '=' in line or not line.endswith(';'): | 1549 | MACHINE = "qemuarm" |
652 | self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) | 1550 | UBOOT_MACHINE = "am57xx_evm_defconfig" |
653 | key, value = line.split('=', 1) | 1551 | SPL_BINARY = "MLO" |
654 | sigs[itsdotpath][key.rstrip()] = value.lstrip().rstrip(';') | ||
655 | 1552 | ||
656 | for reqpath in req_itspaths: | 1553 | # Enable creation of the U-Boot fitImage |
657 | if not reqpath in itspaths: | 1554 | UBOOT_FITIMAGE_ENABLE = "1" |
658 | self.fail('Missing section in its file: %s' % reqpath) | ||
659 | 1555 | ||
660 | reqsigvalues_image = { | 1556 | # (U-boot) fitImage properties |
661 | 'algo': '"sha256,rsa2048"', | 1557 | UBOOT_LOADADDRESS = "0x80080000" |
662 | 'key-name-hint': '"spl-cascaded-oe-selftest"', | 1558 | UBOOT_ENTRYPOINT = "0x80080000" |
663 | } | 1559 | UBOOT_FIT_DESC = "A model description" |
664 | 1560 | ||
665 | for itspath, values in sigs.items(): | 1561 | # Enable creation of the TEE fitImage |
666 | reqsigvalues = reqsigvalues_image | 1562 | UBOOT_FIT_TEE = "1" |
667 | for reqkey, reqvalue in reqsigvalues.items(): | ||
668 | value = values.get(reqkey, None) | ||
669 | if value is None: | ||
670 | self.fail('Missing key "%s" in its file signature section %s' % (reqkey, itspath)) | ||
671 | self.assertEqual(value, reqvalue) | ||
672 | |||
673 | # Dump the image to see if it really got signed | ||
674 | bitbake("u-boot-tools-native -c addto_recipe_sysroot") | ||
675 | result = runCmd('bitbake -e u-boot-tools-native | grep ^RECIPE_SYSROOT_NATIVE=') | ||
676 | recipe_sysroot_native = result.output.split('=')[1].strip('"') | ||
677 | dumpimage_path = os.path.join(recipe_sysroot_native, 'usr', 'bin', 'dumpimage') | ||
678 | result = runCmd('%s -l %s' % (dumpimage_path, fitimage_path)) | ||
679 | in_signed = None | ||
680 | signed_sections = {} | ||
681 | for line in result.output.splitlines(): | ||
682 | if line.startswith((' Image')): | ||
683 | in_signed = re.search(r'\((.*)\)', line).groups()[0] | ||
684 | elif re.match(' \w', line): | ||
685 | in_signed = None | ||
686 | elif in_signed: | ||
687 | if not in_signed in signed_sections: | ||
688 | signed_sections[in_signed] = {} | ||
689 | key, value = line.split(':', 1) | ||
690 | signed_sections[in_signed][key.strip()] = value.strip() | ||
691 | self.assertIn('uboot', signed_sections) | ||
692 | self.assertIn('fdt', signed_sections) | ||
693 | for signed_section, values in signed_sections.items(): | ||
694 | value = values.get('Sign algo', None) | ||
695 | self.assertEqual(value, 'sha256,rsa2048:spl-cascaded-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section) | ||
696 | value = values.get('Sign value', None) | ||
697 | self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section) | ||
698 | |||
699 | # Check for SPL_MKIMAGE_SIGN_ARGS | ||
700 | result = runCmd('bitbake -e virtual/bootloader | grep ^T=') | ||
701 | tempdir = result.output.split('=', 1)[1].strip().strip('') | ||
702 | result = runCmd('grep "a smart cascaded U-Boot comment" %s/run.do_uboot_assemble_fitimage' % tempdir, ignore_status=True) | ||
703 | self.assertEqual(result.status, 0, 'SPL_MKIMAGE_SIGN_ARGS value did not get used') | ||
704 | |||
705 | # Check for evidence of test-mkimage-wrapper class | ||
706 | result = runCmd('grep "### uboot-mkimage wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True) | ||
707 | self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE did not work') | ||
708 | result = runCmd('grep "### uboot-mkimage signing wrapper message" %s/log.do_uboot_assemble_fitimage' % tempdir, ignore_status=True) | ||
709 | self.assertEqual(result.status, 0, 'UBOOT_MKIMAGE_SIGN did not work') | ||
710 | |||
711 | |||
712 | |||
713 | def test_initramfs_bundle(self): | ||
714 | """ | ||
715 | Summary: Verifies the content of the initramfs bundle node in the FIT Image Tree Source (its) | ||
716 | The FIT settings are set by the test case. | ||
717 | The machine used is beaglebone-yocto. | ||
718 | Expected: 1. The ITS is generated with initramfs bundle support | ||
719 | 2. All the fields in the kernel node are as expected (matching the | ||
720 | conf settings) | ||
721 | 3. The kernel is included in all the available configurations and | ||
722 | its hash is included in the configuration signature | ||
723 | 1563 | ||
724 | Product: oe-core | 1564 | # TEE fitImage properties |
725 | Author: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> | 1565 | UBOOT_FIT_TEE_IMAGE = "${TOPDIR}/tee-dummy.bin" |
726 | """ | 1566 | UBOOT_FIT_TEE_LOADADDRESS = "0x80180000" |
1567 | UBOOT_FIT_TEE_ENTRYPOINT = "0x80180000" | ||
1568 | |||
1569 | # Enable creation of the ATF fitImage | ||
1570 | UBOOT_FIT_ARM_TRUSTED_FIRMWARE = "1" | ||
1571 | |||
1572 | # ATF fitImage properties | ||
1573 | UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE = "${TOPDIR}/atf-dummy.bin" | ||
1574 | UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS = "0x80280000" | ||
1575 | UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT = "0x80280000" | ||
1576 | """ | ||
1577 | self.write_config(config) | ||
1578 | |||
1579 | bb_vars = self._fit_get_bb_vars([ | ||
1580 | 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE', | ||
1581 | 'UBOOT_FIT_TEE_IMAGE', | ||
1582 | ]) | ||
1583 | |||
1584 | # Create an ATF dummy image | ||
1585 | dummy_atf = os.path.join(self.builddir, bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE']) | ||
1586 | FitImageTestCase._gen_random_file(dummy_atf) | ||
727 | 1587 | ||
1588 | # Create a TEE dummy image | ||
1589 | dummy_tee = os.path.join(self.builddir, bb_vars['UBOOT_FIT_TEE_IMAGE']) | ||
1590 | FitImageTestCase._gen_random_file(dummy_tee) | ||
1591 | |||
1592 | self._test_fitimage(bb_vars) | ||
1593 | |||
1594 | def test_sign_standalone_uboot_atf_tee_fit_image(self): | ||
1595 | """ | ||
1596 | Summary: Check if U-Boot FIT image and Image Tree Source (its) are | ||
1597 | created and signed correctly for the scenario where only | ||
1598 | the U-Boot proper fitImage is being created and signed. | ||
1599 | Expected: 1. Create atf and tee dummy images | ||
1600 | 2. U-Boot its and FIT image are built successfully | ||
1601 | 3. Scanning the its file indicates signing is enabled | ||
1602 | as requested by SPL_SIGN_ENABLE (using keys generated | ||
1603 | via UBOOT_FIT_GENERATE_KEYS) | ||
1604 | 4. Dumping the FIT image indicates signature values | ||
1605 | are present | ||
1606 | 5. Examination of the do_uboot_assemble_fitimage | ||
1607 | runfile/logfile indicate that UBOOT_MKIMAGE, UBOOT_MKIMAGE_SIGN | ||
1608 | and SPL_MKIMAGE_SIGN_ARGS are working as expected. | ||
1609 | Product: oe-core | ||
1610 | Author: Jamin Lin <jamin_lin@aspeedtech.com> | ||
1611 | """ | ||
728 | config = """ | 1612 | config = """ |
729 | DISTRO="poky" | 1613 | # There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at |
730 | MACHINE = "beaglebone-yocto" | 1614 | # least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set |
731 | INITRAMFS_IMAGE_BUNDLE = "1" | 1615 | MACHINE = "qemuarm" |
732 | INITRAMFS_IMAGE = "core-image-minimal-initramfs" | 1616 | UBOOT_MACHINE = "am57xx_evm_defconfig" |
733 | INITRAMFS_SCRIPTS = "" | 1617 | SPL_BINARY = "MLO" |
734 | UBOOT_MACHINE = "am335x_evm_defconfig" | 1618 | # Enable creation and signing of the U-Boot fitImage |
735 | KERNEL_CLASSES = " kernel-fitimage " | 1619 | UBOOT_FITIMAGE_ENABLE = "1" |
736 | KERNEL_IMAGETYPES = "fitImage" | 1620 | SPL_SIGN_ENABLE = "1" |
737 | UBOOT_SIGN_ENABLE = "1" | 1621 | SPL_SIGN_KEYNAME = "spl-oe-selftest" |
738 | UBOOT_SIGN_KEYNAME = "beaglebonekey" | 1622 | SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys" |
739 | UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}" | ||
740 | UBOOT_DTB_BINARY = "u-boot.dtb" | 1623 | UBOOT_DTB_BINARY = "u-boot.dtb" |
741 | UBOOT_ENTRYPOINT = "0x80000000" | 1624 | UBOOT_ENTRYPOINT = "0x80000000" |
742 | UBOOT_LOADADDRESS = "0x80000000" | 1625 | UBOOT_LOADADDRESS = "0x80000000" |
743 | UBOOT_DTB_LOADADDRESS = "0x82000000" | ||
744 | UBOOT_ARCH = "arm" | 1626 | UBOOT_ARCH = "arm" |
745 | UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" | 1627 | SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" |
746 | UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" | 1628 | SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot ATF TEE comment'" |
747 | UBOOT_EXTLINUX = "0" | 1629 | UBOOT_EXTLINUX = "0" |
748 | FIT_GENERATE_KEYS = "1" | 1630 | UBOOT_FIT_GENERATE_KEYS = "1" |
749 | KERNEL_IMAGETYPE_REPLACEMENT = "zImage" | 1631 | UBOOT_FIT_HASH_ALG = "sha256" |
750 | FIT_KERNEL_COMP_ALG = "none" | ||
751 | FIT_HASH_ALG = "sha256" | ||
752 | """ | ||
753 | self.write_config(config) | ||
754 | 1632 | ||
755 | # fitImage is created as part of linux recipe | 1633 | # Enable creation of the TEE fitImage |
756 | bitbake("virtual/kernel") | 1634 | UBOOT_FIT_TEE = "1" |
757 | 1635 | ||
758 | image_type = get_bb_var('INITRAMFS_IMAGE') | 1636 | # TEE fitImage properties |
759 | deploy_dir_image = get_bb_var('DEPLOY_DIR_IMAGE') | 1637 | UBOOT_FIT_TEE_IMAGE = "${TOPDIR}/tee-dummy.bin" |
760 | machine = get_bb_var('MACHINE') | 1638 | UBOOT_FIT_TEE_LOADADDRESS = "0x80180000" |
761 | fitimage_its_path = os.path.join(deploy_dir_image, | 1639 | UBOOT_FIT_TEE_ENTRYPOINT = "0x80180000" |
762 | "fitImage-its-%s-%s-%s" % (image_type, machine, machine)) | ||
763 | fitimage_path = os.path.join(deploy_dir_image,"fitImage") | ||
764 | 1640 | ||
765 | self.assertTrue(os.path.exists(fitimage_its_path), | 1641 | # Enable creation of the ATF fitImage |
766 | "%s image tree source doesn't exist" % (fitimage_its_path)) | 1642 | UBOOT_FIT_ARM_TRUSTED_FIRMWARE = "1" |
767 | self.assertTrue(os.path.exists(fitimage_path), | ||
768 | "%s FIT image doesn't exist" % (fitimage_path)) | ||
769 | 1643 | ||
770 | kernel_load = str(get_bb_var('UBOOT_LOADADDRESS')) | 1644 | # ATF fitImage properties |
771 | kernel_entry = str(get_bb_var('UBOOT_ENTRYPOINT')) | 1645 | UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE = "${TOPDIR}/atf-dummy.bin" |
772 | kernel_type = str(get_bb_var('UBOOT_MKIMAGE_KERNEL_TYPE')) | 1646 | UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS = "0x80280000" |
773 | kernel_compression = str(get_bb_var('FIT_KERNEL_COMP_ALG')) | 1647 | UBOOT_FIT_ARM_TRUSTED_FIRMWARE_ENTRYPOINT = "0x80280000" |
774 | uboot_arch = str(get_bb_var('UBOOT_ARCH')) | 1648 | """ |
775 | fit_hash_alg = str(get_bb_var('FIT_HASH_ALG')) | 1649 | self.write_config(config) |
776 | 1650 | ||
777 | its_file = open(fitimage_its_path) | 1651 | bb_vars = self._fit_get_bb_vars([ |
1652 | 'UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE', | ||
1653 | 'UBOOT_FIT_TEE_IMAGE', | ||
1654 | ]) | ||
778 | 1655 | ||
779 | its_lines = [line.strip() for line in its_file.readlines()] | 1656 | # Create an ATF dummy image |
1657 | dummy_atf = os.path.join(self.builddir, bb_vars['UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE']) | ||
1658 | FitImageTestCase._gen_random_file(dummy_atf) | ||
780 | 1659 | ||
781 | exp_node_lines = [ | 1660 | # Create a TEE dummy image |
782 | 'kernel-1 {', | 1661 | dummy_tee = os.path.join(self.builddir, bb_vars['UBOOT_FIT_TEE_IMAGE']) |
783 | 'description = "Linux kernel";', | 1662 | FitImageTestCase._gen_random_file(dummy_tee) |
784 | 'data = /incbin/("linux.bin");', | ||
785 | 'type = "' + kernel_type + '";', | ||
786 | 'arch = "' + uboot_arch + '";', | ||
787 | 'os = "linux";', | ||
788 | 'compression = "' + kernel_compression + '";', | ||
789 | 'load = <' + kernel_load + '>;', | ||
790 | 'entry = <' + kernel_entry + '>;', | ||
791 | 'hash-1 {', | ||
792 | 'algo = "' + fit_hash_alg +'";', | ||
793 | '};', | ||
794 | '};' | ||
795 | ] | ||
796 | 1663 | ||
797 | node_str = exp_node_lines[0] | 1664 | self._test_fitimage(bb_vars) |
798 | 1665 | ||
799 | test_passed = False | ||
800 | 1666 | ||
801 | print ("checking kernel node\n") | 1667 | def test_sign_uboot_kernel_individual(self): |
1668 | """ | ||
1669 | Summary: Check if the device-tree from U-Boot has two public keys | ||
1670 | for verifying the kernel FIT image created by the | ||
1671 | kernel-fitimage.bbclass included. | ||
1672 | This test sets: FIT_SIGN_INDIVIDUAL = "1" | ||
1673 | Expected: There must be two signature nodes. One is required for | ||
1674 | the individual image nodes, the other is required for the | ||
1675 | verification of the configuration section. | ||
1676 | """ | ||
1677 | config = """ | ||
1678 | # Enable creation of fitImage | ||
1679 | MACHINE = "beaglebone-yocto" | ||
1680 | UBOOT_SIGN_ENABLE = "1" | ||
1681 | UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" | ||
1682 | UBOOT_SIGN_KEYNAME = "the-kernel-config-key" | ||
1683 | UBOOT_SIGN_IMG_KEYNAME = "the-kernel-image-key" | ||
1684 | UBOOT_MKIMAGE_DTCOPTS="-I dts -O dtb -p 2000" | ||
1685 | FIT_SIGN_INDIVIDUAL = "1" | ||
1686 | """ | ||
1687 | self.write_config(config) | ||
1688 | bb_vars = self._fit_get_bb_vars() | ||
1689 | self._gen_signing_key(bb_vars) | ||
802 | 1690 | ||
803 | if node_str in its_lines: | 1691 | bitbake(UBootFitImageTests.BOOTLOADER_RECIPE) |
804 | node_start_idx = its_lines.index(node_str) | ||
805 | node = its_lines[node_start_idx:(node_start_idx + len(exp_node_lines))] | ||
806 | if node == exp_node_lines: | ||
807 | print("kernel node verified") | ||
808 | else: | ||
809 | self.assertTrue(test_passed == True,"kernel node does not match expectation") | ||
810 | |||
811 | rx_configs = re.compile("^conf-.*") | ||
812 | its_configs = list(filter(rx_configs.match, its_lines)) | ||
813 | |||
814 | for cfg_str in its_configs: | ||
815 | cfg_start_idx = its_lines.index(cfg_str) | ||
816 | line_idx = cfg_start_idx + 2 | ||
817 | node_end = False | ||
818 | while node_end == False: | ||
819 | if its_lines[line_idx] == "};" and its_lines[line_idx-1] == "};" : | ||
820 | node_end = True | ||
821 | line_idx = line_idx + 1 | ||
822 | |||
823 | node = its_lines[cfg_start_idx:line_idx] | ||
824 | print("checking configuration " + cfg_str.rstrip(" {")) | ||
825 | rx_desc_line = re.compile("^description.*1 Linux kernel.*") | ||
826 | if len(list(filter(rx_desc_line.match, node))) != 1: | ||
827 | self.assertTrue(test_passed == True,"kernel keyword not found in the description line") | ||
828 | break | ||
829 | else: | ||
830 | print("kernel keyword found in the description line") | ||
831 | 1692 | ||
832 | if 'kernel = "kernel-1";' not in node: | 1693 | # Just check the DTB of u-boot since there is no u-boot FIT image |
833 | self.assertTrue(test_passed == True,"kernel line not found") | 1694 | self._check_kernel_dtb(bb_vars) |
834 | break | ||
835 | else: | ||
836 | print("kernel line found") | ||
837 | 1695 | ||
838 | rx_sign_line = re.compile("^sign-images.*kernel.*") | ||
839 | if len(list(filter(rx_sign_line.match, node))) != 1: | ||
840 | self.assertTrue(test_passed == True,"kernel hash not signed") | ||
841 | break | ||
842 | else: | ||
843 | print("kernel hash signed") | ||
844 | 1696 | ||
845 | test_passed = True | 1697 | def test_sign_uboot_fit_image_without_spl(self): |
846 | self.assertTrue(test_passed == True,"Initramfs bundle test success") | 1698 | """ |
1699 | Summary: Check if U-Boot FIT image and Image Tree Source (its) are | ||
1700 | created and signed correctly for the scenario where only | ||
1701 | the U-Boot proper fitImage is being created and signed | ||
1702 | (no SPL included). | ||
1703 | Expected: 1) U-Boot its and FIT image are built successfully | ||
1704 | 2) Scanning the its file indicates signing is enabled | ||
1705 | as requested by SPL_SIGN_ENABLE (using keys generated | ||
1706 | via UBOOT_FIT_GENERATE_KEYS) | ||
1707 | 3) Dumping the FIT image indicates signature values | ||
1708 | are present | ||
1709 | 4) Examination of the do_uboot_assemble_fitimage | ||
1710 | runfile/logfile indicate that UBOOT_MKIMAGE and | ||
1711 | UBOOT_MKIMAGE_SIGN are working as expected. | ||
1712 | Product: oe-core | ||
1713 | Author: Jamin Lin <jamin_lin@aspeedtech.com> | ||
1714 | """ | ||
1715 | config = """ | ||
1716 | # There's no U-boot defconfig with CONFIG_FIT_SIGNATURE yet, so we need at | ||
1717 | # least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set | ||
1718 | MACHINE = "qemuarm" | ||
1719 | UBOOT_MACHINE = "am57xx_evm_defconfig" | ||
1720 | # Enable creation and signing of the U-Boot fitImage (no SPL) | ||
1721 | UBOOT_FITIMAGE_ENABLE = "1" | ||
1722 | SPL_DTB_BINARY = "" | ||
1723 | SPL_SIGN_ENABLE = "1" | ||
1724 | SPL_SIGN_KEYNAME = "spl-oe-selftest" | ||
1725 | SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys" | ||
1726 | UBOOT_FIT_GENERATE_KEYS = "1" | ||
1727 | """ | ||
1728 | self.write_config(config) | ||
1729 | bb_vars = self._fit_get_bb_vars() | ||
1730 | self._test_fitimage(bb_vars) | ||
1731 | |||
diff --git a/meta/lib/oeqa/selftest/cases/gcc.py b/meta/lib/oeqa/selftest/cases/gcc.py index 89360178fe..1bda29a72b 100644 --- a/meta/lib/oeqa/selftest/cases/gcc.py +++ b/meta/lib/oeqa/selftest/cases/gcc.py | |||
@@ -37,7 +37,7 @@ class GccSelfTestBase(OESelftestTestCase, OEPTestResultTestCase): | |||
37 | features = [] | 37 | features = [] |
38 | features.append('MAKE_CHECK_TARGETS = "{0}"'.format(" ".join(targets))) | 38 | features.append('MAKE_CHECK_TARGETS = "{0}"'.format(" ".join(targets))) |
39 | if ssh is not None: | 39 | if ssh is not None: |
40 | features.append('TOOLCHAIN_TEST_TARGET = "ssh"') | 40 | features.append('TOOLCHAIN_TEST_TARGET = "linux-ssh"') |
41 | features.append('TOOLCHAIN_TEST_HOST = "{0}"'.format(ssh)) | 41 | features.append('TOOLCHAIN_TEST_HOST = "{0}"'.format(ssh)) |
42 | features.append('TOOLCHAIN_TEST_HOST_USER = "root"') | 42 | features.append('TOOLCHAIN_TEST_HOST_USER = "root"') |
43 | features.append('TOOLCHAIN_TEST_HOST_PORT = "22"') | 43 | features.append('TOOLCHAIN_TEST_HOST_PORT = "22"') |
@@ -83,6 +83,8 @@ class GccSelfTestBase(OESelftestTestCase, OEPTestResultTestCase): | |||
83 | # validate that SSH is working | 83 | # validate that SSH is working |
84 | status, _ = qemu.run("uname") | 84 | status, _ = qemu.run("uname") |
85 | self.assertEqual(status, 0) | 85 | self.assertEqual(status, 0) |
86 | qemu.run('echo "MaxStartups 75:30:100" >> /etc/ssh/sshd_config') | ||
87 | qemu.run('service sshd restart') | ||
86 | 88 | ||
87 | return self.run_check(*args, ssh=qemu.ip, **kwargs) | 89 | return self.run_check(*args, ssh=qemu.ip, **kwargs) |
88 | 90 | ||
diff --git a/meta/lib/oeqa/selftest/cases/gdbserver.py b/meta/lib/oeqa/selftest/cases/gdbserver.py index 9da97ae780..b6b7c5c473 100644 --- a/meta/lib/oeqa/selftest/cases/gdbserver.py +++ b/meta/lib/oeqa/selftest/cases/gdbserver.py | |||
@@ -54,7 +54,7 @@ CORE_IMAGE_EXTRA_INSTALL = "gdbserver" | |||
54 | self.logger.warning("starting gdb %s" % cmd) | 54 | self.logger.warning("starting gdb %s" % cmd) |
55 | r = runCmd(cmd, native_sysroot=native_sysroot, target_sys=target_sys) | 55 | r = runCmd(cmd, native_sysroot=native_sysroot, target_sys=target_sys) |
56 | self.assertEqual(0, r.status) | 56 | self.assertEqual(0, r.status) |
57 | line_re = r"Line \d+ of \"/usr/src/debug/kmod/.*/tools/kmod.c\" starts at address 0x[0-9A-Fa-f]+ <kmod_help>" | 57 | line_re = r"Line \d+ of \".*\" starts at address 0x[0-9A-Fa-f]+ <kmod_help>" |
58 | self.assertRegex(r.output, line_re) | 58 | self.assertRegex(r.output, line_re) |
59 | break | 59 | break |
60 | else: | 60 | else: |
diff --git a/meta/lib/oeqa/selftest/cases/imagefeatures.py b/meta/lib/oeqa/selftest/cases/imagefeatures.py index dc88c222bd..94d01ba116 100644 --- a/meta/lib/oeqa/selftest/cases/imagefeatures.py +++ b/meta/lib/oeqa/selftest/cases/imagefeatures.py | |||
@@ -250,12 +250,7 @@ USERADD_GID_TABLES += "files/static-group" | |||
250 | DISTRO_FEATURES:append = " pam opengl wayland" | 250 | DISTRO_FEATURES:append = " pam opengl wayland" |
251 | 251 | ||
252 | # Switch to systemd | 252 | # Switch to systemd |
253 | DISTRO_FEATURES:append = " systemd usrmerge" | 253 | INIT_MANAGER = "systemd" |
254 | VIRTUAL-RUNTIME_init_manager = "systemd" | ||
255 | VIRTUAL-RUNTIME_initscripts = "" | ||
256 | VIRTUAL-RUNTIME_syslog = "" | ||
257 | VIRTUAL-RUNTIME_login_manager = "shadow-base" | ||
258 | DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit" | ||
259 | 254 | ||
260 | # Replace busybox | 255 | # Replace busybox |
261 | PREFERRED_PROVIDER_virtual/base-utils = "packagegroup-core-base-utils" | 256 | PREFERRED_PROVIDER_virtual/base-utils = "packagegroup-core-base-utils" |
@@ -319,7 +314,7 @@ SKIP_RECIPE[busybox] = "Don't build this" | |||
319 | """ | 314 | """ |
320 | config = """ | 315 | config = """ |
321 | DISTRO_FEATURES:append = " api-documentation" | 316 | DISTRO_FEATURES:append = " api-documentation" |
322 | CORE_IMAGE_EXTRA_INSTALL = "man-pages kmod-doc" | 317 | CORE_IMAGE_EXTRA_INSTALL = "man-pages" |
323 | """ | 318 | """ |
324 | self.write_config(config) | 319 | self.write_config(config) |
325 | bitbake("core-image-minimal") | 320 | bitbake("core-image-minimal") |
@@ -330,7 +325,7 @@ CORE_IMAGE_EXTRA_INSTALL = "man-pages kmod-doc" | |||
330 | self.assertEqual(status, 1, 'Failed to run apropos: %s' % (output)) | 325 | self.assertEqual(status, 1, 'Failed to run apropos: %s' % (output)) |
331 | self.assertIn("iso_8859_15", output) | 326 | self.assertIn("iso_8859_15", output) |
332 | 327 | ||
333 | # This manpage is provided by kmod | 328 | # This manpage is provided by man-pages |
334 | status, output = qemu.run_serial("man --pager=cat modprobe") | 329 | status, output = qemu.run_serial("man --pager=cat intro") |
335 | self.assertEqual(status, 1, 'Failed to run man: %s' % (output)) | 330 | self.assertEqual(status, 1, 'Failed to run man: %s' % (output)) |
336 | self.assertIn("force-modversion", output) | 331 | self.assertIn("introduction to user commands", output) |
diff --git a/meta/lib/oeqa/selftest/cases/incompatible_lic.py b/meta/lib/oeqa/selftest/cases/incompatible_lic.py index f4af67a239..93884f5731 100644 --- a/meta/lib/oeqa/selftest/cases/incompatible_lic.py +++ b/meta/lib/oeqa/selftest/cases/incompatible_lic.py | |||
@@ -102,6 +102,7 @@ class IncompatibleLicensePerImageTests(OESelftestTestCase): | |||
102 | return """ | 102 | return """ |
103 | IMAGE_INSTALL:append = " bash" | 103 | IMAGE_INSTALL:append = " bash" |
104 | INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*" | 104 | INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*" |
105 | MACHINE_ESSENTIAL_EXTRA_RDEPENDS:remove = "tar" | ||
105 | """ | 106 | """ |
106 | 107 | ||
107 | def test_bash_default(self): | 108 | def test_bash_default(self): |
@@ -114,7 +115,7 @@ INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*" | |||
114 | 115 | ||
115 | def test_bash_and_license(self): | 116 | def test_bash_and_license(self): |
116 | self.disable_class("create-spdx") | 117 | self.disable_class("create-spdx") |
117 | self.write_config(self.default_config() + '\nLICENSE:append:pn-bash = " & SomeLicense"') | 118 | self.write_config(self.default_config() + '\nLICENSE:append:pn-bash = " & SomeLicense"\nERROR_QA:remove:pn-bash = "license-exists"') |
118 | error_msg = "ERROR: core-image-minimal-1.0-r0 do_rootfs: Package bash cannot be installed into the image because it has incompatible license(s): GPL-3.0-or-later" | 119 | error_msg = "ERROR: core-image-minimal-1.0-r0 do_rootfs: Package bash cannot be installed into the image because it has incompatible license(s): GPL-3.0-or-later" |
119 | 120 | ||
120 | result = bitbake('core-image-minimal', ignore_status=True) | 121 | result = bitbake('core-image-minimal', ignore_status=True) |
@@ -123,12 +124,12 @@ INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*" | |||
123 | 124 | ||
124 | def test_bash_or_license(self): | 125 | def test_bash_or_license(self): |
125 | self.disable_class("create-spdx") | 126 | self.disable_class("create-spdx") |
126 | self.write_config(self.default_config() + '\nLICENSE:append:pn-bash = " | SomeLicense"') | 127 | self.write_config(self.default_config() + '\nLICENSE:append:pn-bash = " | SomeLicense"\nERROR_QA:remove:pn-bash = "license-exists"\nERROR_QA:remove:pn-core-image-minimal = "license-file-missing"') |
127 | 128 | ||
128 | bitbake('core-image-minimal') | 129 | bitbake('core-image-minimal') |
129 | 130 | ||
130 | def test_bash_license_exceptions(self): | 131 | def test_bash_license_exceptions(self): |
131 | self.write_config(self.default_config() + '\nINCOMPATIBLE_LICENSE_EXCEPTIONS:pn-core-image-minimal = "bash:GPL-3.0-or-later"') | 132 | self.write_config(self.default_config() + '\nINCOMPATIBLE_LICENSE_EXCEPTIONS:pn-core-image-minimal = "bash:GPL-3.0-or-later"\nERROR_QA:remove:pn-core-image-minimal = "license-exception"') |
132 | 133 | ||
133 | bitbake('core-image-minimal') | 134 | bitbake('core-image-minimal') |
134 | 135 | ||
@@ -136,6 +137,8 @@ class NoGPL3InImagesTests(OESelftestTestCase): | |||
136 | def test_core_image_minimal(self): | 137 | def test_core_image_minimal(self): |
137 | self.write_config(""" | 138 | self.write_config(""" |
138 | INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*" | 139 | INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*" |
140 | |||
141 | require conf/distro/include/no-gplv3.inc | ||
139 | """) | 142 | """) |
140 | bitbake('core-image-minimal') | 143 | bitbake('core-image-minimal') |
141 | 144 | ||
diff --git a/meta/lib/oeqa/selftest/cases/layerappend.py b/meta/lib/oeqa/selftest/cases/layerappend.py index 379ed589ad..64b17117cc 100644 --- a/meta/lib/oeqa/selftest/cases/layerappend.py +++ b/meta/lib/oeqa/selftest/cases/layerappend.py | |||
@@ -37,7 +37,7 @@ FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" | |||
37 | SRC_URI:append = " file://appendtest.txt" | 37 | SRC_URI:append = " file://appendtest.txt" |
38 | 38 | ||
39 | sysroot_stage_all:append() { | 39 | sysroot_stage_all:append() { |
40 | install -m 644 ${WORKDIR}/appendtest.txt ${SYSROOT_DESTDIR}/ | 40 | install -m 644 ${UNPACKDIR}/appendtest.txt ${SYSROOT_DESTDIR}/ |
41 | } | 41 | } |
42 | 42 | ||
43 | """ | 43 | """ |
diff --git a/meta/lib/oeqa/selftest/cases/liboe.py b/meta/lib/oeqa/selftest/cases/liboe.py index d5ffffdcb4..930354c931 100644 --- a/meta/lib/oeqa/selftest/cases/liboe.py +++ b/meta/lib/oeqa/selftest/cases/liboe.py | |||
@@ -9,11 +9,11 @@ from oeqa.utils.commands import get_bb_var, get_bb_vars, bitbake, runCmd | |||
9 | import oe.path | 9 | import oe.path |
10 | import os | 10 | import os |
11 | 11 | ||
12 | class LibOE(OESelftestTestCase): | 12 | class CopyTreeTests(OESelftestTestCase): |
13 | 13 | ||
14 | @classmethod | 14 | @classmethod |
15 | def setUpClass(cls): | 15 | def setUpClass(cls): |
16 | super(LibOE, cls).setUpClass() | 16 | super().setUpClass() |
17 | cls.tmp_dir = get_bb_var('TMPDIR') | 17 | cls.tmp_dir = get_bb_var('TMPDIR') |
18 | 18 | ||
19 | def test_copy_tree_special(self): | 19 | def test_copy_tree_special(self): |
@@ -102,3 +102,36 @@ class LibOE(OESelftestTestCase): | |||
102 | self.assertEqual(dstcnt, len(testfiles), "Number of files in dst (%s) differs from number of files in src(%s)." % (dstcnt, srccnt)) | 102 | self.assertEqual(dstcnt, len(testfiles), "Number of files in dst (%s) differs from number of files in src(%s)." % (dstcnt, srccnt)) |
103 | 103 | ||
104 | oe.path.remove(testloc) | 104 | oe.path.remove(testloc) |
105 | |||
106 | class SubprocessTests(OESelftestTestCase): | ||
107 | |||
108 | def test_subprocess_tweak(self): | ||
109 | """ | ||
110 | Test that the string representation of | ||
111 | oeqa.utils.subprocesstweak.OETestCalledProcessError includes stdout and | ||
112 | stderr, as expected. | ||
113 | """ | ||
114 | script = """ | ||
115 | #! /bin/sh | ||
116 | echo Ivn fgqbhg | tr '[a-zA-Z]' '[n-za-mN-ZA-M]' | ||
117 | echo Ivn fgqree | tr '[a-zA-Z]' '[n-za-mN-ZA-M]' >&2 | ||
118 | exit 42 | ||
119 | """ | ||
120 | |||
121 | import subprocess | ||
122 | import unittest.mock | ||
123 | from oeqa.utils.subprocesstweak import OETestCalledProcessError | ||
124 | |||
125 | with self.assertRaises(OETestCalledProcessError) as cm: | ||
126 | with unittest.mock.patch("subprocess.CalledProcessError", OETestCalledProcessError): | ||
127 | subprocess.run(["bash", "-"], input=script, text=True, capture_output=True, check=True) | ||
128 | |||
129 | e = cm.exception | ||
130 | self.assertEqual(e.returncode, 42) | ||
131 | self.assertEqual("Via stdout\n", e.stdout) | ||
132 | self.assertEqual("Via stderr\n", e.stderr) | ||
133 | |||
134 | string = str(e) | ||
135 | self.assertIn("exit status 42", string) | ||
136 | self.assertIn("Standard Output: Via stdout", string) | ||
137 | self.assertIn("Standard Error: Via stderr", string) | ||
diff --git a/meta/lib/oeqa/selftest/cases/locales.py b/meta/lib/oeqa/selftest/cases/locales.py index 4ca8ffb7aa..ac4888ef66 100644 --- a/meta/lib/oeqa/selftest/cases/locales.py +++ b/meta/lib/oeqa/selftest/cases/locales.py | |||
@@ -14,7 +14,7 @@ class LocalesTest(OESelftestTestCase): | |||
14 | features = [] | 14 | features = [] |
15 | features.append('EXTRA_IMAGE_FEATURES = "empty-root-password allow-empty-password allow-root-login"') | 15 | features.append('EXTRA_IMAGE_FEATURES = "empty-root-password allow-empty-password allow-root-login"') |
16 | features.append('IMAGE_INSTALL:append = " glibc-utils localedef"') | 16 | features.append('IMAGE_INSTALL:append = " glibc-utils localedef"') |
17 | features.append('GLIBC_GENERATE_LOCALES = "en_US.UTF-8 fr_FR.UTF-8"') | 17 | features.append('GLIBC_GENERATE_LOCALES = "en_US.UTF-8 fr_FR.UTF-8 en_US.ISO-8859-1 de_DE.UTF-8 fr_FR.ISO-8859-1 zh_HK.BIG5-HKSCS tr_TR.UTF-8"') |
18 | features.append('IMAGE_LINGUAS:append = " en-us fr-fr"') | 18 | features.append('IMAGE_LINGUAS:append = " en-us fr-fr"') |
19 | if binary_enabled: | 19 | if binary_enabled: |
20 | features.append('ENABLE_BINARY_LOCALE_GENERATION = "1"') | 20 | features.append('ENABLE_BINARY_LOCALE_GENERATION = "1"') |
diff --git a/meta/lib/oeqa/selftest/cases/meta_ide.py b/meta/lib/oeqa/selftest/cases/meta_ide.py index ffe0d2604d..c3a7df4cdf 100644 --- a/meta/lib/oeqa/selftest/cases/meta_ide.py +++ b/meta/lib/oeqa/selftest/cases/meta_ide.py | |||
@@ -20,8 +20,8 @@ class MetaIDE(OESelftestTestCase): | |||
20 | bitbake('meta-ide-support') | 20 | bitbake('meta-ide-support') |
21 | bitbake('build-sysroots -c build_native_sysroot') | 21 | bitbake('build-sysroots -c build_native_sysroot') |
22 | bitbake('build-sysroots -c build_target_sysroot') | 22 | bitbake('build-sysroots -c build_target_sysroot') |
23 | bb_vars = get_bb_vars(['MULTIMACH_TARGET_SYS', 'DEPLOY_DIR_IMAGE', 'COREBASE']) | 23 | bb_vars = get_bb_vars(['MACHINE_ARCH', 'TARGET_VENDOR', 'TARGET_OS', 'DEPLOY_DIR_IMAGE', 'COREBASE']) |
24 | cls.environment_script = 'environment-setup-%s' % bb_vars['MULTIMACH_TARGET_SYS'] | 24 | cls.environment_script = 'environment-setup-%s%s-%s' % (bb_vars['MACHINE_ARCH'], bb_vars['TARGET_VENDOR'], bb_vars['TARGET_OS']) |
25 | cls.deploydir = bb_vars['DEPLOY_DIR_IMAGE'] | 25 | cls.deploydir = bb_vars['DEPLOY_DIR_IMAGE'] |
26 | cls.environment_script_path = '%s/%s' % (cls.deploydir, cls.environment_script) | 26 | cls.environment_script_path = '%s/%s' % (cls.deploydir, cls.environment_script) |
27 | cls.corebasedir = bb_vars['COREBASE'] | 27 | cls.corebasedir = bb_vars['COREBASE'] |
@@ -47,9 +47,9 @@ class MetaIDE(OESelftestTestCase): | |||
47 | "https://ftp.gnu.org/gnu/cpio/cpio-2.15.tar.gz", | 47 | "https://ftp.gnu.org/gnu/cpio/cpio-2.15.tar.gz", |
48 | self.tmpdir_metaideQA, self.td['DATETIME'], dl_dir=dl_dir) | 48 | self.tmpdir_metaideQA, self.td['DATETIME'], dl_dir=dl_dir) |
49 | self.project.download_archive() | 49 | self.project.download_archive() |
50 | self.assertEqual(self.project.run_configure('$CONFIGURE_FLAGS'), 0, | 50 | self.assertEqual(self.project.run_configure('CFLAGS="-std=gnu17 -Dbool=int -Dtrue=1 -Dfalse=0 -Wno-error=implicit-function-declaration" $CONFIGURE_FLAGS'), 0, |
51 | msg="Running configure failed") | 51 | msg="Running configure failed") |
52 | self.assertEqual(self.project.run_make(), 0, | 52 | self.assertEqual(self.project.run_make(make_args="CFLAGS='-std=gnu17 -Dbool=int -Dtrue=1 -Dfalse=0 -Wno-error=implicit-function-declaration'"), 0, |
53 | msg="Running make failed") | 53 | msg="Running make failed") |
54 | self.assertEqual(self.project.run_install(), 0, | 54 | self.assertEqual(self.project.run_install(), 0, |
55 | msg="Running make install failed") | 55 | msg="Running make install failed") |
diff --git a/meta/lib/oeqa/selftest/cases/minidebuginfo.py b/meta/lib/oeqa/selftest/cases/minidebuginfo.py index 2919f07939..a8923460f9 100644 --- a/meta/lib/oeqa/selftest/cases/minidebuginfo.py +++ b/meta/lib/oeqa/selftest/cases/minidebuginfo.py | |||
@@ -8,6 +8,7 @@ import subprocess | |||
8 | import tempfile | 8 | import tempfile |
9 | import shutil | 9 | import shutil |
10 | 10 | ||
11 | from oeqa.core.decorator import OETestTag | ||
11 | from oeqa.selftest.case import OESelftestTestCase | 12 | from oeqa.selftest.case import OESelftestTestCase |
12 | from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runCmd | 13 | from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runCmd |
13 | 14 | ||
@@ -42,3 +43,18 @@ IMAGE_FSTYPES = "tar.bz2" | |||
42 | native_sysroot = native_sysroot, target_sys = target_sys) | 43 | native_sysroot = native_sysroot, target_sys = target_sys) |
43 | self.assertIn(".gnu_debugdata", r.output) | 44 | self.assertIn(".gnu_debugdata", r.output) |
44 | 45 | ||
46 | @OETestTag("runqemu") | ||
47 | def test_minidebuginfo_qemu(self): | ||
48 | """ | ||
49 | Test minidebuginfo inside a qemu. | ||
50 | This runs test_systemd_coredump_minidebuginfo and other minidebuginfo runtime tests which may be added in the future. | ||
51 | """ | ||
52 | |||
53 | self.write_config(""" | ||
54 | DISTRO_FEATURES:append = " minidebuginfo" | ||
55 | INIT_MANAGER = "systemd" | ||
56 | IMAGE_CLASSES += "testimage" | ||
57 | TEST_SUITES = "ping ssh systemd" | ||
58 | """) | ||
59 | bitbake('core-image-minimal') | ||
60 | bitbake('-c testimage core-image-minimal') | ||
diff --git a/meta/lib/oeqa/selftest/cases/oescripts.py b/meta/lib/oeqa/selftest/cases/oescripts.py index f69efccfee..3f9899b289 100644 --- a/meta/lib/oeqa/selftest/cases/oescripts.py +++ b/meta/lib/oeqa/selftest/cases/oescripts.py | |||
@@ -9,33 +9,9 @@ import shutil | |||
9 | import importlib | 9 | import importlib |
10 | import unittest | 10 | import unittest |
11 | from oeqa.selftest.case import OESelftestTestCase | 11 | from oeqa.selftest.case import OESelftestTestCase |
12 | from oeqa.selftest.cases.buildhistory import BuildhistoryBase | ||
13 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var | 12 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var |
14 | from oeqa.utils import CommandError | 13 | from oeqa.utils import CommandError |
15 | 14 | ||
16 | class BuildhistoryDiffTests(BuildhistoryBase): | ||
17 | |||
18 | def test_buildhistory_diff(self): | ||
19 | target = 'xcursor-transparent-theme' | ||
20 | self.run_buildhistory_operation(target, target_config="PR = \"r1\"", change_bh_location=True) | ||
21 | self.run_buildhistory_operation(target, target_config="PR = \"r0\"", change_bh_location=False, expect_error=True) | ||
22 | result = runCmd("oe-pkgdata-util read-value PKGV %s" % target) | ||
23 | pkgv = result.output.rstrip() | ||
24 | result = runCmd("buildhistory-diff -p %s" % get_bb_var('BUILDHISTORY_DIR')) | ||
25 | expected_endlines = [ | ||
26 | "xcursor-transparent-theme-dev: RRECOMMENDS: removed \"xcursor-transparent-theme (['= %s-r1'])\", added \"xcursor-transparent-theme (['= %s-r0'])\"" % (pkgv, pkgv), | ||
27 | "xcursor-transparent-theme-staticdev: RDEPENDS: removed \"xcursor-transparent-theme-dev (['= %s-r1'])\", added \"xcursor-transparent-theme-dev (['= %s-r0'])\"" % (pkgv, pkgv) | ||
28 | ] | ||
29 | for line in result.output.splitlines(): | ||
30 | for el in expected_endlines: | ||
31 | if line.endswith(el): | ||
32 | expected_endlines.remove(el) | ||
33 | break | ||
34 | else: | ||
35 | self.fail('Unexpected line:\n%s\nExpected line endings:\n %s' % (line, '\n '.join(expected_endlines))) | ||
36 | if expected_endlines: | ||
37 | self.fail('Missing expected line endings:\n %s' % '\n '.join(expected_endlines)) | ||
38 | |||
39 | @unittest.skipUnless(importlib.util.find_spec("cairo"), "Python cairo module is not present") | 15 | @unittest.skipUnless(importlib.util.find_spec("cairo"), "Python cairo module is not present") |
40 | class OEPybootchartguyTests(OESelftestTestCase): | 16 | class OEPybootchartguyTests(OESelftestTestCase): |
41 | 17 | ||
@@ -175,7 +151,7 @@ class OEListPackageconfigTests(OESelftestTestCase): | |||
175 | def test_packageconfig_flags_option_all(self): | 151 | def test_packageconfig_flags_option_all(self): |
176 | results = runCmd('%s/contrib/list-packageconfig-flags.py -a' % self.scripts_dir) | 152 | results = runCmd('%s/contrib/list-packageconfig-flags.py -a' % self.scripts_dir) |
177 | expected_endlines = [] | 153 | expected_endlines = [] |
178 | expected_endlines.append("pinentry-1.2.1") | 154 | expected_endlines.append("pinentry-1.3.1") |
179 | expected_endlines.append("PACKAGECONFIG ncurses") | 155 | expected_endlines.append("PACKAGECONFIG ncurses") |
180 | expected_endlines.append("PACKAGECONFIG[qt] --enable-pinentry-qt, --disable-pinentry-qt, qtbase-native qtbase") | 156 | expected_endlines.append("PACKAGECONFIG[qt] --enable-pinentry-qt, --disable-pinentry-qt, qtbase-native qtbase") |
181 | expected_endlines.append("PACKAGECONFIG[gtk2] --enable-pinentry-gtk2, --disable-pinentry-gtk2, gtk+ glib-2.0") | 157 | expected_endlines.append("PACKAGECONFIG[gtk2] --enable-pinentry-gtk2, --disable-pinentry-gtk2, gtk+ glib-2.0") |
diff --git a/meta/lib/oeqa/selftest/cases/overlayfs.py b/meta/lib/oeqa/selftest/cases/overlayfs.py index e31063567b..580fbdcb9c 100644 --- a/meta/lib/oeqa/selftest/cases/overlayfs.py +++ b/meta/lib/oeqa/selftest/cases/overlayfs.py | |||
@@ -5,7 +5,7 @@ | |||
5 | # | 5 | # |
6 | 6 | ||
7 | from oeqa.selftest.case import OESelftestTestCase | 7 | from oeqa.selftest.case import OESelftestTestCase |
8 | from oeqa.utils.commands import bitbake, runqemu | 8 | from oeqa.utils.commands import bitbake, runqemu, get_bb_vars |
9 | from oeqa.core.decorator import OETestTag | 9 | from oeqa.core.decorator import OETestTag |
10 | from oeqa.core.decorator.data import skipIfNotMachine | 10 | from oeqa.core.decorator.data import skipIfNotMachine |
11 | 11 | ||
@@ -466,6 +466,45 @@ IMAGE_INSTALL:append = " overlayfs-user" | |||
466 | line = getline_qemu(output, "Read-only file system") | 466 | line = getline_qemu(output, "Read-only file system") |
467 | self.assertTrue(line, msg=output) | 467 | self.assertTrue(line, msg=output) |
468 | 468 | ||
469 | @skipIfNotMachine("qemux86-64", "tests are qemux86-64 specific currently") | ||
470 | def test_postinst_on_target_for_read_only_rootfs(self): | ||
471 | """ | ||
472 | Summary: The purpose of this test case is to verify that post-installation | ||
473 | on target scripts are executed even if using read-only rootfs when | ||
474 | read-only-rootfs-delayed-postinsts is set | ||
475 | Expected: The test files are created on first boot | ||
476 | """ | ||
477 | |||
478 | import oe.path | ||
479 | |||
480 | vars = get_bb_vars(("IMAGE_ROOTFS", "sysconfdir"), "core-image-minimal") | ||
481 | sysconfdir = vars["sysconfdir"] | ||
482 | self.assertIsNotNone(sysconfdir) | ||
483 | # Need to use oe.path here as sysconfdir starts with / | ||
484 | targettestdir = os.path.join(sysconfdir, "postinst-test") | ||
485 | |||
486 | config = self.get_working_config() | ||
487 | |||
488 | args = { | ||
489 | 'OVERLAYFS_INIT_OPTION': "", | ||
490 | 'OVERLAYFS_ETC_USE_ORIG_INIT_NAME': 1, | ||
491 | 'OVERLAYFS_ROOTFS_TYPE': "ext4", | ||
492 | 'OVERLAYFS_ETC_CREATE_MOUNT_DIRS': 1 | ||
493 | } | ||
494 | |||
495 | # read-only-rootfs is already set in get_working_config() | ||
496 | config += 'EXTRA_IMAGE_FEATURES += "read-only-rootfs-delayed-postinsts"\n' | ||
497 | config += 'CORE_IMAGE_EXTRA_INSTALL = "postinst-delayed-b"\n' | ||
498 | |||
499 | self.write_config(config.format(**args)) | ||
500 | |||
501 | res = bitbake('core-image-minimal') | ||
502 | |||
503 | with runqemu('core-image-minimal', image_fstype='wic') as qemu: | ||
504 | for filename in ("rootfs", "delayed-a", "delayed-b"): | ||
505 | status, output = qemu.run_serial("test -f %s && echo found" % os.path.join(targettestdir, filename)) | ||
506 | self.assertIn("found", output, "%s was not present on boot" % filename) | ||
507 | |||
469 | def get_working_config(self): | 508 | def get_working_config(self): |
470 | return """ | 509 | return """ |
471 | # Use systemd as init manager | 510 | # Use systemd as init manager |
diff --git a/meta/lib/oeqa/selftest/cases/package.py b/meta/lib/oeqa/selftest/cases/package.py index 1aa6c03f8a..38ed7173fe 100644 --- a/meta/lib/oeqa/selftest/cases/package.py +++ b/meta/lib/oeqa/selftest/cases/package.py | |||
@@ -103,11 +103,37 @@ class PackageTests(OESelftestTestCase): | |||
103 | 103 | ||
104 | dest = get_bb_var('PKGDEST', 'selftest-hardlink') | 104 | dest = get_bb_var('PKGDEST', 'selftest-hardlink') |
105 | bindir = get_bb_var('bindir', 'selftest-hardlink') | 105 | bindir = get_bb_var('bindir', 'selftest-hardlink') |
106 | libdir = get_bb_var('libdir', 'selftest-hardlink') | ||
107 | libexecdir = get_bb_var('libexecdir', 'selftest-hardlink') | ||
106 | 108 | ||
107 | def checkfiles(): | 109 | def checkfiles(): |
108 | # Recipe creates 4 hardlinked files, there is a copy in package/ and a copy in packages-split/ | 110 | # Recipe creates 4 hardlinked files, there is a copy in package/ and a copy in packages-split/ |
109 | # so expect 8 in total. | 111 | # so expect 8 in total. |
110 | self.assertEqual(os.stat(dest + "/selftest-hardlink" + bindir + "/hello1").st_nlink, 8) | 112 | self.assertEqual(os.stat(dest + "/selftest-hardlink" + bindir + "/hello1").st_nlink, 8) |
113 | self.assertEqual(os.stat(dest + "/selftest-hardlink" + libexecdir + "/hello3").st_nlink, 8) | ||
114 | |||
115 | # Check dbg version | ||
116 | # 2 items, a copy in both package/packages-split so 4 | ||
117 | self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + bindir + "/.debug/hello1").st_nlink, 4) | ||
118 | self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libexecdir + "/.debug/hello1").st_nlink, 4) | ||
119 | |||
120 | # Even though the libexecdir name is 'hello3' or 'hello4', that isn't the debug target name | ||
121 | self.assertEqual(os.path.exists(dest + "/selftest-hardlink-dbg" + libexecdir + "/.debug/hello3"), False) | ||
122 | self.assertEqual(os.path.exists(dest + "/selftest-hardlink-dbg" + libexecdir + "/.debug/hello4"), False) | ||
123 | |||
124 | # Check the staticdev libraries | ||
125 | # 101 items, a copy in both package/packages-split so 202 | ||
126 | self.assertEqual(os.stat(dest + "/selftest-hardlink-staticdev" + libdir + "/libhello.a").st_nlink, 202) | ||
127 | self.assertEqual(os.stat(dest + "/selftest-hardlink-staticdev" + libdir + "/libhello-25.a").st_nlink, 202) | ||
128 | self.assertEqual(os.stat(dest + "/selftest-hardlink-staticdev" + libdir + "/libhello-50.a").st_nlink, 202) | ||
129 | self.assertEqual(os.stat(dest + "/selftest-hardlink-staticdev" + libdir + "/libhello-75.a").st_nlink, 202) | ||
130 | |||
131 | # Check static dbg | ||
132 | # 101 items, a copy in both package/packages-split so 202 | ||
133 | self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libdir + "/.debug-static/libhello.a").st_nlink, 202) | ||
134 | self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libdir + "/.debug-static/libhello-25.a").st_nlink, 202) | ||
135 | self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libdir + "/.debug-static/libhello-50.a").st_nlink, 202) | ||
136 | self.assertEqual(os.stat(dest + "/selftest-hardlink-dbg" + libdir + "/.debug-static/libhello-75.a").st_nlink, 202) | ||
111 | 137 | ||
112 | # Test a sparse file remains sparse | 138 | # Test a sparse file remains sparse |
113 | sparsestat = os.stat(dest + "/selftest-hardlink" + bindir + "/sparsetest") | 139 | sparsestat = os.stat(dest + "/selftest-hardlink" + bindir + "/sparsetest") |
diff --git a/meta/lib/oeqa/selftest/cases/picolibc.py b/meta/lib/oeqa/selftest/cases/picolibc.py new file mode 100644 index 0000000000..e40b4fc3d3 --- /dev/null +++ b/meta/lib/oeqa/selftest/cases/picolibc.py | |||
@@ -0,0 +1,18 @@ | |||
1 | # | ||
2 | # Copyright OpenEmbedded Contributors | ||
3 | # | ||
4 | # SPDX-License-Identifier: MIT | ||
5 | # | ||
6 | |||
7 | from oeqa.selftest.case import OESelftestTestCase | ||
8 | from oeqa.utils.commands import bitbake, get_bb_var | ||
9 | |||
10 | class PicolibcTest(OESelftestTestCase): | ||
11 | |||
12 | def test_picolibc(self): | ||
13 | compatible_machines = ['qemuarm', 'qemuarm64', 'qemuriscv32', 'qemuriscv64'] | ||
14 | machine = get_bb_var('MACHINE') | ||
15 | if machine not in compatible_machines: | ||
16 | self.skipTest('This test only works with machines : %s' % ' '.join(compatible_machines)) | ||
17 | self.write_config('TCLIBC = "picolibc"') | ||
18 | bitbake("picolibc-helloworld") | ||
diff --git a/meta/lib/oeqa/selftest/cases/recipetool.py b/meta/lib/oeqa/selftest/cases/recipetool.py index aebea42502..0bd724c8ee 100644 --- a/meta/lib/oeqa/selftest/cases/recipetool.py +++ b/meta/lib/oeqa/selftest/cases/recipetool.py | |||
@@ -120,9 +120,15 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
120 | self._try_recipetool_appendfile_fail('/dev/console', self.testfile, ['ERROR: /dev/console cannot be handled by this tool']) | 120 | self._try_recipetool_appendfile_fail('/dev/console', self.testfile, ['ERROR: /dev/console cannot be handled by this tool']) |
121 | 121 | ||
122 | def test_recipetool_appendfile_alternatives(self): | 122 | def test_recipetool_appendfile_alternatives(self): |
123 | lspath = '/bin/ls' | ||
124 | dirname = "base_bindir" | ||
125 | if "usrmerge" in get_bb_var('DISTRO_FEATURES'): | ||
126 | lspath = '/usr/bin/ls' | ||
127 | dirname = "bindir" | ||
128 | |||
123 | # Now try with a file we know should be an alternative | 129 | # Now try with a file we know should be an alternative |
124 | # (this is very much a fake example, but one we know is reliably an alternative) | 130 | # (this is very much a fake example, but one we know is reliably an alternative) |
125 | self._try_recipetool_appendfile_fail('/bin/ls', self.testfile, ['ERROR: File /bin/ls is an alternative possibly provided by the following recipes:', 'coreutils', 'busybox']) | 131 | self._try_recipetool_appendfile_fail(lspath, self.testfile, ['ERROR: File %s is an alternative possibly provided by the following recipes:' % lspath, 'coreutils', 'busybox']) |
126 | # Need a test file - should be executable | 132 | # Need a test file - should be executable |
127 | testfile2 = os.path.join(self.corebase, 'oe-init-build-env') | 133 | testfile2 = os.path.join(self.corebase, 'oe-init-build-env') |
128 | testfile2name = os.path.basename(testfile2) | 134 | testfile2name = os.path.basename(testfile2) |
@@ -131,12 +137,12 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
131 | 'SRC_URI += "file://%s"\n' % testfile2name, | 137 | 'SRC_URI += "file://%s"\n' % testfile2name, |
132 | '\n', | 138 | '\n', |
133 | 'do_install:append() {\n', | 139 | 'do_install:append() {\n', |
134 | ' install -d ${D}${base_bindir}\n', | 140 | ' install -d ${D}${%s}\n' % dirname, |
135 | ' install -m 0755 ${WORKDIR}/%s ${D}${base_bindir}/ls\n' % testfile2name, | 141 | ' install -m 0755 ${UNPACKDIR}/%s ${D}${%s}/ls\n' % (testfile2name, dirname), |
136 | '}\n'] | 142 | '}\n'] |
137 | self._try_recipetool_appendfile('coreutils', '/bin/ls', testfile2, '-r coreutils', expectedlines, [testfile2name]) | 143 | self._try_recipetool_appendfile('coreutils', lspath, testfile2, '-r coreutils', expectedlines, [testfile2name]) |
138 | # Now try bbappending the same file again, contents should not change | 144 | # Now try bbappending the same file again, contents should not change |
139 | bbappendfile, _ = self._try_recipetool_appendfile('coreutils', '/bin/ls', self.testfile, '-r coreutils', expectedlines, [testfile2name]) | 145 | bbappendfile, _ = self._try_recipetool_appendfile('coreutils', lspath, self.testfile, '-r coreutils', expectedlines, [testfile2name]) |
140 | # But file should have | 146 | # But file should have |
141 | copiedfile = os.path.join(os.path.dirname(bbappendfile), 'coreutils', testfile2name) | 147 | copiedfile = os.path.join(os.path.dirname(bbappendfile), 'coreutils', testfile2name) |
142 | result = runCmd('diff -q %s %s' % (testfile2, copiedfile), ignore_status=True) | 148 | result = runCmd('diff -q %s %s' % (testfile2, copiedfile), ignore_status=True) |
@@ -158,7 +164,7 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
158 | '\n', | 164 | '\n', |
159 | 'do_install:append() {\n', | 165 | 'do_install:append() {\n', |
160 | ' install -d ${D}${datadir}\n', | 166 | ' install -d ${D}${datadir}\n', |
161 | ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n', | 167 | ' install -m 0644 ${UNPACKDIR}/testfile ${D}${datadir}/something\n', |
162 | '}\n'] | 168 | '}\n'] |
163 | self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase', expectedlines, ['testfile']) | 169 | self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase', expectedlines, ['testfile']) |
164 | # Try adding another file, this time where the source file is executable | 170 | # Try adding another file, this time where the source file is executable |
@@ -173,8 +179,8 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
173 | '\n', | 179 | '\n', |
174 | 'do_install:append() {\n', | 180 | 'do_install:append() {\n', |
175 | ' install -d ${D}${datadir}\n', | 181 | ' install -d ${D}${datadir}\n', |
176 | ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n', | 182 | ' install -m 0644 ${UNPACKDIR}/testfile ${D}${datadir}/something\n', |
177 | ' install -m 0755 ${WORKDIR}/%s ${D}${datadir}/scriptname\n' % testfile2name, | 183 | ' install -m 0755 ${UNPACKDIR}/%s ${D}${datadir}/scriptname\n' % testfile2name, |
178 | '}\n'] | 184 | '}\n'] |
179 | self._try_recipetool_appendfile('netbase', '/usr/share/scriptname', testfile2, '-r netbase', expectedlines, ['testfile', testfile2name]) | 185 | self._try_recipetool_appendfile('netbase', '/usr/share/scriptname', testfile2, '-r netbase', expectedlines, ['testfile', testfile2name]) |
180 | 186 | ||
@@ -186,7 +192,7 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
186 | '\n', | 192 | '\n', |
187 | 'do_install:append() {\n', | 193 | 'do_install:append() {\n', |
188 | ' install -d ${D}${bindir}\n', | 194 | ' install -d ${D}${bindir}\n', |
189 | ' install -m 0755 ${WORKDIR}/testfile ${D}${bindir}/selftest-recipetool-testbin\n', | 195 | ' install -m 0755 ${UNPACKDIR}/testfile ${D}${bindir}/selftest-recipetool-testbin\n', |
190 | '}\n'] | 196 | '}\n'] |
191 | _, output = self._try_recipetool_appendfile('netbase', '/usr/bin/selftest-recipetool-testbin', self.testfile, '-r netbase', expectedlines, ['testfile']) | 197 | _, output = self._try_recipetool_appendfile('netbase', '/usr/bin/selftest-recipetool-testbin', self.testfile, '-r netbase', expectedlines, ['testfile']) |
192 | self.assertNotIn('WARNING: ', output) | 198 | self.assertNotIn('WARNING: ', output) |
@@ -201,7 +207,7 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
201 | '\n', | 207 | '\n', |
202 | 'do_install:append:mymachine() {\n', | 208 | 'do_install:append:mymachine() {\n', |
203 | ' install -d ${D}${datadir}\n', | 209 | ' install -d ${D}${datadir}\n', |
204 | ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/something\n', | 210 | ' install -m 0644 ${UNPACKDIR}/testfile ${D}${datadir}/something\n', |
205 | '}\n'] | 211 | '}\n'] |
206 | _, output = self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase -m mymachine', expectedlines, ['mymachine/testfile']) | 212 | _, output = self._try_recipetool_appendfile('netbase', '/usr/share/something', self.testfile, '-r netbase -m mymachine', expectedlines, ['mymachine/testfile']) |
207 | self.assertNotIn('WARNING: ', output) | 213 | self.assertNotIn('WARNING: ', output) |
@@ -235,7 +241,7 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
235 | '\n', | 241 | '\n', |
236 | 'do_install:append() {\n', | 242 | 'do_install:append() {\n', |
237 | ' install -d ${D}${datadir}\n', | 243 | ' install -d ${D}${datadir}\n', |
238 | ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-subdir\n', | 244 | ' install -m 0644 ${UNPACKDIR}/testfile ${D}${datadir}/selftest-replaceme-subdir\n', |
239 | '}\n'] | 245 | '}\n'] |
240 | _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-subdir', self.testfile, '', expectedlines, ['testfile']) | 246 | _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-subdir', self.testfile, '', expectedlines, ['testfile']) |
241 | self.assertNotIn('WARNING: ', output) | 247 | self.assertNotIn('WARNING: ', output) |
@@ -262,7 +268,7 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
262 | '\n', | 268 | '\n', |
263 | 'do_install:append() {\n', | 269 | 'do_install:append() {\n', |
264 | ' install -d ${D}${sysconfdir}\n', | 270 | ' install -d ${D}${sysconfdir}\n', |
265 | ' install -m 0644 ${WORKDIR}/testfile ${D}${sysconfdir}/selftest-replaceme-patched\n', | 271 | ' install -m 0644 ${UNPACKDIR}/testfile ${D}${sysconfdir}/selftest-replaceme-patched\n', |
266 | '}\n'] | 272 | '}\n'] |
267 | _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/etc/selftest-replaceme-patched', self.testfile, '', expectedlines, ['testfile']) | 273 | _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/etc/selftest-replaceme-patched', self.testfile, '', expectedlines, ['testfile']) |
268 | for line in output.splitlines(): | 274 | for line in output.splitlines(): |
@@ -280,7 +286,7 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
280 | '\n', | 286 | '\n', |
281 | 'do_install:append() {\n', | 287 | 'do_install:append() {\n', |
282 | ' install -d ${D}${datadir}\n', | 288 | ' install -d ${D}${datadir}\n', |
283 | ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-scripted\n', | 289 | ' install -m 0644 ${UNPACKDIR}/testfile ${D}${datadir}/selftest-replaceme-scripted\n', |
284 | '}\n'] | 290 | '}\n'] |
285 | _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-scripted', self.testfile, '', expectedlines, ['testfile']) | 291 | _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-scripted', self.testfile, '', expectedlines, ['testfile']) |
286 | self.assertNotIn('WARNING: ', output) | 292 | self.assertNotIn('WARNING: ', output) |
@@ -303,7 +309,7 @@ class RecipetoolAppendTests(RecipetoolBase): | |||
303 | '\n', | 309 | '\n', |
304 | 'do_install:append() {\n', | 310 | 'do_install:append() {\n', |
305 | ' install -d ${D}${datadir}\n', | 311 | ' install -d ${D}${datadir}\n', |
306 | ' install -m 0644 ${WORKDIR}/testfile ${D}${datadir}/selftest-replaceme-postinst\n', | 312 | ' install -m 0644 ${UNPACKDIR}/testfile ${D}${datadir}/selftest-replaceme-postinst\n', |
307 | '}\n'] | 313 | '}\n'] |
308 | _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-postinst', self.testfile, '-r selftest-recipetool-appendfile', expectedlines, ['testfile']) | 314 | _, output = self._try_recipetool_appendfile('selftest-recipetool-appendfile', '/usr/share/selftest-replaceme-postinst', self.testfile, '-r selftest-recipetool-appendfile', expectedlines, ['testfile']) |
309 | 315 | ||
@@ -379,7 +385,7 @@ class RecipetoolCreateTests(RecipetoolBase): | |||
379 | checkvars = {} | 385 | checkvars = {} |
380 | checkvars['LICENSE'] = 'LGPL-2.1-only' | 386 | checkvars['LICENSE'] = 'LGPL-2.1-only' |
381 | checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34' | 387 | checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7fbc338309ac38fefcd64b04bb903e34' |
382 | checkvars['S'] = '${WORKDIR}/git' | 388 | checkvars['S'] = None |
383 | checkvars['PV'] = '1.11+git' | 389 | checkvars['PV'] = '1.11+git' |
384 | checkvars['SRC_URI'] = srcuri + ';branch=master' | 390 | checkvars['SRC_URI'] = srcuri + ';branch=master' |
385 | checkvars['DEPENDS'] = set(['libcheck', 'libjpeg-turbo', 'libpng', 'libx11', 'libxext', 'pango']) | 391 | checkvars['DEPENDS'] = set(['libcheck', 'libjpeg-turbo', 'libpng', 'libx11', 'libxext', 'pango']) |
@@ -751,235 +757,42 @@ class RecipetoolCreateTests(RecipetoolBase): | |||
751 | 757 | ||
752 | def test_recipetool_create_go(self): | 758 | def test_recipetool_create_go(self): |
753 | # Basic test to check go recipe generation | 759 | # Basic test to check go recipe generation |
760 | self.maxDiff = None | ||
761 | |||
754 | temprecipe = os.path.join(self.tempdir, 'recipe') | 762 | temprecipe = os.path.join(self.tempdir, 'recipe') |
755 | os.makedirs(temprecipe) | 763 | os.makedirs(temprecipe) |
756 | 764 | ||
757 | recipefile = os.path.join(temprecipe, 'edgex-go_git.bb') | 765 | recipefile = os.path.join(temprecipe, 'recipetool-go-test_git.bb') |
758 | deps_require_file = os.path.join(temprecipe, 'edgex-go', 'edgex-go-modules.inc') | ||
759 | lics_require_file = os.path.join(temprecipe, 'edgex-go', 'edgex-go-licenses.inc') | ||
760 | modules_txt_file = os.path.join(temprecipe, 'edgex-go', 'modules.txt') | ||
761 | 766 | ||
762 | srcuri = 'https://github.com/edgexfoundry/edgex-go.git' | 767 | srcuri = 'https://git.yoctoproject.org/recipetool-go-test.git' |
763 | srcrev = "v3.0.0" | 768 | srcrev = "c3e213c01b6c1406b430df03ef0d1ae77de5d2f7" |
764 | srcbranch = "main" | 769 | srcbranch = "main" |
765 | 770 | ||
766 | result = runCmd('recipetool create -o %s %s -S %s -B %s' % (temprecipe, srcuri, srcrev, srcbranch)) | 771 | result = runCmd('recipetool create -o %s %s -S %s -B %s' % (temprecipe, srcuri, srcrev, srcbranch)) |
767 | 772 | ||
768 | self.maxDiff = None | 773 | inherits = ['go-mod', 'go-mod-update-modules'] |
769 | inherits = ['go-vendor'] | ||
770 | 774 | ||
771 | checkvars = {} | 775 | checkvars = {} |
772 | checkvars['GO_IMPORT'] = "github.com/edgexfoundry/edgex-go" | 776 | checkvars['GO_IMPORT'] = "git.yoctoproject.org/recipetool-go-test" |
773 | checkvars['SRC_URI'] = {'git://${GO_IMPORT};destsuffix=git/src/${GO_IMPORT};nobranch=1;name=${BPN};protocol=https', | 777 | checkvars['SRC_URI'] = {'git://${GO_IMPORT};protocol=https;nobranch=1;destsuffix=${GO_SRCURI_DESTSUFFIX}'} |
774 | 'file://modules.txt'} | 778 | checkvars['LIC_FILES_CHKSUM'] = { |
775 | checkvars['LIC_FILES_CHKSUM'] = {'file://src/${GO_IMPORT}/LICENSE;md5=8f8bc924cf73f6a32381e5fd4c58d603'} | 779 | 'file://src/${GO_IMPORT}/LICENSE;md5=4e3933dd47afbf115e484d11385fb3bd', |
780 | 'file://src/${GO_IMPORT}/is/LICENSE;md5=62beaee5a116dd1e80161667b1df39ab' | ||
781 | } | ||
776 | 782 | ||
777 | self.assertTrue(os.path.isfile(recipefile)) | ||
778 | self._test_recipe_contents(recipefile, checkvars, inherits) | 783 | self._test_recipe_contents(recipefile, checkvars, inherits) |
784 | self.assertNotIn('Traceback', result.output) | ||
779 | 785 | ||
786 | lics_require_file = os.path.join(temprecipe, 'recipetool-go-test-licenses.inc') | ||
787 | self.assertFileExists(lics_require_file) | ||
780 | checkvars = {} | 788 | checkvars = {} |
781 | checkvars['VENDORED_LIC_FILES_CHKSUM'] = set( | 789 | checkvars['LIC_FILES_CHKSUM'] = {'file://pkg/mod/github.com/godbus/dbus/v5@v5.1.0/LICENSE;md5=09042bd5c6c96a2b9e45ddf1bc517eed;spdx=BSD-2-Clause'} |
782 | ['file://src/${GO_IMPORT}/vendor/github.com/Microsoft/go-winio/LICENSE;md5=69205ff73858f2c22b2ca135b557e8ef', | ||
783 | 'file://src/${GO_IMPORT}/vendor/github.com/armon/go-metrics/LICENSE;md5=d2d77030c0183e3d1e66d26dc1f243be', | ||
784 | 'file://src/${GO_IMPORT}/vendor/github.com/cenkalti/backoff/LICENSE;md5=1571d94433e3f3aa05267efd4dbea68b', | ||
785 | 'file://src/${GO_IMPORT}/vendor/github.com/davecgh/go-spew/LICENSE;md5=c06795ed54b2a35ebeeb543cd3a73e56', | ||
786 | 'file://src/${GO_IMPORT}/vendor/github.com/eclipse/paho.mqtt.golang/LICENSE;md5=dcdb33474b60c38efd27356d8f2edec7', | ||
787 | 'file://src/${GO_IMPORT}/vendor/github.com/eclipse/paho.mqtt.golang/edl-v10;md5=3adfcc70f5aeb7a44f3f9b495aa1fbf3', | ||
788 | 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-bootstrap/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', | ||
789 | 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-configuration/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', | ||
790 | 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-core-contracts/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', | ||
791 | 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-messaging/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', | ||
792 | 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-registry/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff', | ||
793 | 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-secrets/v3/LICENSE;md5=f9fa2f4f8e0ef8cc7b5dd150963eb457', | ||
794 | 'file://src/${GO_IMPORT}/vendor/github.com/fatih/color/LICENSE.md;md5=316e6d590bdcde7993fb175662c0dd5a', | ||
795 | 'file://src/${GO_IMPORT}/vendor/github.com/fxamacker/cbor/v2/LICENSE;md5=827f5a2fa861382d35a3943adf9ebb86', | ||
796 | 'file://src/${GO_IMPORT}/vendor/github.com/go-jose/go-jose/v3/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57', | ||
797 | 'file://src/${GO_IMPORT}/vendor/github.com/go-jose/go-jose/v3/json/LICENSE;md5=591778525c869cdde0ab5a1bf283cd81', | ||
798 | 'file://src/${GO_IMPORT}/vendor/github.com/go-kit/log/LICENSE;md5=5b7c15ad5fffe2ff6e9d58a6c161f082', | ||
799 | 'file://src/${GO_IMPORT}/vendor/github.com/go-logfmt/logfmt/LICENSE;md5=98e39517c38127f969de33057067091e', | ||
800 | 'file://src/${GO_IMPORT}/vendor/github.com/go-playground/locales/LICENSE;md5=3ccbda375ee345400ad1da85ba522301', | ||
801 | 'file://src/${GO_IMPORT}/vendor/github.com/go-playground/universal-translator/LICENSE;md5=2e2b21ef8f61057977d27c727c84bef1', | ||
802 | 'file://src/${GO_IMPORT}/vendor/github.com/go-playground/validator/v10/LICENSE;md5=a718a0f318d76f7c5d510cbae84f0b60', | ||
803 | 'file://src/${GO_IMPORT}/vendor/github.com/go-redis/redis/v7/LICENSE;md5=58103aa5ea1ee9b7a369c9c4a95ef9b5', | ||
804 | 'file://src/${GO_IMPORT}/vendor/github.com/golang/protobuf/LICENSE;md5=939cce1ec101726fa754e698ac871622', | ||
805 | 'file://src/${GO_IMPORT}/vendor/github.com/gomodule/redigo/LICENSE;md5=2ee41112a44fe7014dce33e26468ba93', | ||
806 | 'file://src/${GO_IMPORT}/vendor/github.com/google/uuid/LICENSE;md5=88073b6dd8ec00fe09da59e0b6dfded1', | ||
807 | 'file://src/${GO_IMPORT}/vendor/github.com/gorilla/mux/LICENSE;md5=33fa1116c45f9e8de714033f99edde13', | ||
808 | 'file://src/${GO_IMPORT}/vendor/github.com/gorilla/websocket/LICENSE;md5=c007b54a1743d596f46b2748d9f8c044', | ||
809 | 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/consul/api/LICENSE;md5=b8a277a612171b7526e9be072f405ef4', | ||
810 | 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/errwrap/LICENSE;md5=b278a92d2c1509760384428817710378', | ||
811 | 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-cleanhttp/LICENSE;md5=65d26fcc2f35ea6a181ac777e42db1ea', | ||
812 | 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-hclog/LICENSE;md5=ec7f605b74b9ad03347d0a93a5cc7eb8', | ||
813 | 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-immutable-radix/LICENSE;md5=65d26fcc2f35ea6a181ac777e42db1ea', | ||
814 | 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-multierror/LICENSE;md5=d44fdeb607e2d2614db9464dbedd4094', | ||
815 | 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-rootcerts/LICENSE;md5=65d26fcc2f35ea6a181ac777e42db1ea', | ||
816 | 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/golang-lru/LICENSE;md5=f27a50d2e878867827842f2c60e30bfc', | ||
817 | 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/serf/LICENSE;md5=b278a92d2c1509760384428817710378', | ||
818 | 'file://src/${GO_IMPORT}/vendor/github.com/leodido/go-urn/LICENSE;md5=8f50db5538ec1148a9b3d14ed96c3418', | ||
819 | 'file://src/${GO_IMPORT}/vendor/github.com/mattn/go-colorable/LICENSE;md5=24ce168f90aec2456a73de1839037245', | ||
820 | 'file://src/${GO_IMPORT}/vendor/github.com/mattn/go-isatty/LICENSE;md5=f509beadd5a11227c27b5d2ad6c9f2c6', | ||
821 | 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/consulstructure/LICENSE;md5=96ada10a9e51c98c4656f2cede08c673', | ||
822 | 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/copystructure/LICENSE;md5=56da355a12d4821cda57b8f23ec34bc4', | ||
823 | 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/go-homedir/LICENSE;md5=3f7765c3d4f58e1f84c4313cecf0f5bd', | ||
824 | 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/mapstructure/LICENSE;md5=3f7765c3d4f58e1f84c4313cecf0f5bd', | ||
825 | 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/reflectwalk/LICENSE;md5=3f7765c3d4f58e1f84c4313cecf0f5bd', | ||
826 | 'file://src/${GO_IMPORT}/vendor/github.com/nats-io/nats.go/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327', | ||
827 | 'file://src/${GO_IMPORT}/vendor/github.com/nats-io/nkeys/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327', | ||
828 | 'file://src/${GO_IMPORT}/vendor/github.com/nats-io/nuid/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327', | ||
829 | 'file://src/${GO_IMPORT}/vendor/github.com/pmezard/go-difflib/LICENSE;md5=e9a2ebb8de779a07500ddecca806145e', | ||
830 | 'file://src/${GO_IMPORT}/vendor/github.com/rcrowley/go-metrics/LICENSE;md5=1bdf5d819f50f141366dabce3be1460f', | ||
831 | 'file://src/${GO_IMPORT}/vendor/github.com/spiffe/go-spiffe/v2/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327', | ||
832 | 'file://src/${GO_IMPORT}/vendor/github.com/stretchr/objx/LICENSE;md5=d023fd31d3ca39ec61eec65a91732735', | ||
833 | 'file://src/${GO_IMPORT}/vendor/github.com/stretchr/testify/LICENSE;md5=188f01994659f3c0d310612333d2a26f', | ||
834 | 'file://src/${GO_IMPORT}/vendor/github.com/x448/float16/LICENSE;md5=de8f8e025d57fe7ee0b67f30d571323b', | ||
835 | 'file://src/${GO_IMPORT}/vendor/github.com/zeebo/errs/LICENSE;md5=84914ab36fc0eb48edbaa53e66e8d326', | ||
836 | 'file://src/${GO_IMPORT}/vendor/golang.org/x/crypto/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', | ||
837 | 'file://src/${GO_IMPORT}/vendor/golang.org/x/mod/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', | ||
838 | 'file://src/${GO_IMPORT}/vendor/golang.org/x/net/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', | ||
839 | 'file://src/${GO_IMPORT}/vendor/golang.org/x/sync/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', | ||
840 | 'file://src/${GO_IMPORT}/vendor/golang.org/x/sys/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', | ||
841 | 'file://src/${GO_IMPORT}/vendor/golang.org/x/text/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', | ||
842 | 'file://src/${GO_IMPORT}/vendor/golang.org/x/tools/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707', | ||
843 | 'file://src/${GO_IMPORT}/vendor/google.golang.org/genproto/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57', | ||
844 | 'file://src/${GO_IMPORT}/vendor/google.golang.org/grpc/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57', | ||
845 | 'file://src/${GO_IMPORT}/vendor/google.golang.org/protobuf/LICENSE;md5=02d4002e9171d41a8fad93aa7faf3956', | ||
846 | 'file://src/${GO_IMPORT}/vendor/gopkg.in/eapache/queue.v1/LICENSE;md5=1bfd4408d3de090ef6b908b0cc45a316', | ||
847 | 'file://src/${GO_IMPORT}/vendor/gopkg.in/yaml.v3/LICENSE;md5=3c91c17266710e16afdbb2b6d15c761c']) | ||
848 | |||
849 | self.assertTrue(os.path.isfile(lics_require_file)) | ||
850 | self._test_recipe_contents(lics_require_file, checkvars, []) | 790 | self._test_recipe_contents(lics_require_file, checkvars, []) |
851 | 791 | ||
852 | dependencies = \ | 792 | deps_require_file = os.path.join(temprecipe, 'recipetool-go-test-go-mods.inc') |
853 | [ ('github.com/eclipse/paho.mqtt.golang','v1.4.2', '', '', ''), | 793 | self.assertFileExists(deps_require_file) |
854 | ('github.com/edgexfoundry/go-mod-bootstrap','v3.0.1','github.com/edgexfoundry/go-mod-bootstrap/v3','/v3', ''), | ||
855 | ('github.com/edgexfoundry/go-mod-configuration','v3.0.0','github.com/edgexfoundry/go-mod-configuration/v3','/v3', ''), | ||
856 | ('github.com/edgexfoundry/go-mod-core-contracts','v3.0.0','github.com/edgexfoundry/go-mod-core-contracts/v3','/v3', ''), | ||
857 | ('github.com/edgexfoundry/go-mod-messaging','v3.0.0','github.com/edgexfoundry/go-mod-messaging/v3','/v3', ''), | ||
858 | ('github.com/edgexfoundry/go-mod-secrets','v3.0.1','github.com/edgexfoundry/go-mod-secrets/v3','/v3', ''), | ||
859 | ('github.com/fxamacker/cbor','v2.4.0','github.com/fxamacker/cbor/v2','/v2', ''), | ||
860 | ('github.com/gomodule/redigo','v1.8.9', '', '', ''), | ||
861 | ('github.com/google/uuid','v1.3.0', '', '', ''), | ||
862 | ('github.com/gorilla/mux','v1.8.0', '', '', ''), | ||
863 | ('github.com/rcrowley/go-metrics','v0.0.0-20201227073835-cf1acfcdf475', '', '', ''), | ||
864 | ('github.com/spiffe/go-spiffe','v2.1.4','github.com/spiffe/go-spiffe/v2','/v2', ''), | ||
865 | ('github.com/stretchr/testify','v1.8.2', '', '', ''), | ||
866 | ('go.googlesource.com/crypto','v0.8.0','golang.org/x/crypto', '', ''), | ||
867 | ('gopkg.in/eapache/queue.v1','v1.1.0', '', '', ''), | ||
868 | ('gopkg.in/yaml.v3','v3.0.1', '', '', ''), | ||
869 | ('github.com/microsoft/go-winio','v0.6.0','github.com/Microsoft/go-winio', '', ''), | ||
870 | ('github.com/hashicorp/go-metrics','v0.3.10','github.com/armon/go-metrics', '', ''), | ||
871 | ('github.com/cenkalti/backoff','v2.2.1+incompatible', '', '', ''), | ||
872 | ('github.com/davecgh/go-spew','v1.1.1', '', '', ''), | ||
873 | ('github.com/edgexfoundry/go-mod-registry','v3.0.0','github.com/edgexfoundry/go-mod-registry/v3','/v3', ''), | ||
874 | ('github.com/fatih/color','v1.9.0', '', '', ''), | ||
875 | ('github.com/go-jose/go-jose','v3.0.0','github.com/go-jose/go-jose/v3','/v3', ''), | ||
876 | ('github.com/go-kit/log','v0.2.1', '', '', ''), | ||
877 | ('github.com/go-logfmt/logfmt','v0.5.1', '', '', ''), | ||
878 | ('github.com/go-playground/locales','v0.14.1', '', '', ''), | ||
879 | ('github.com/go-playground/universal-translator','v0.18.1', '', '', ''), | ||
880 | ('github.com/go-playground/validator','v10.13.0','github.com/go-playground/validator/v10','/v10', ''), | ||
881 | ('github.com/go-redis/redis','v7.3.0','github.com/go-redis/redis/v7','/v7', ''), | ||
882 | ('github.com/golang/protobuf','v1.5.2', '', '', ''), | ||
883 | ('github.com/gorilla/websocket','v1.4.2', '', '', ''), | ||
884 | ('github.com/hashicorp/consul','v1.20.0','github.com/hashicorp/consul/api', '', 'api'), | ||
885 | ('github.com/hashicorp/errwrap','v1.0.0', '', '', ''), | ||
886 | ('github.com/hashicorp/go-cleanhttp','v0.5.1', '', '', ''), | ||
887 | ('github.com/hashicorp/go-hclog','v0.14.1', '', '', ''), | ||
888 | ('github.com/hashicorp/go-immutable-radix','v1.3.0', '', '', ''), | ||
889 | ('github.com/hashicorp/go-multierror','v1.1.1', '', '', ''), | ||
890 | ('github.com/hashicorp/go-rootcerts','v1.0.2', '', '', ''), | ||
891 | ('github.com/hashicorp/golang-lru','v0.5.4', '', '', ''), | ||
892 | ('github.com/hashicorp/serf','v0.10.1', '', '', ''), | ||
893 | ('github.com/leodido/go-urn','v1.2.3', '', '', ''), | ||
894 | ('github.com/mattn/go-colorable','v0.1.12', '', '', ''), | ||
895 | ('github.com/mattn/go-isatty','v0.0.14', '', '', ''), | ||
896 | ('github.com/mitchellh/consulstructure','v0.0.0-20190329231841-56fdc4d2da54', '', '', ''), | ||
897 | ('github.com/mitchellh/copystructure','v1.2.0', '', '', ''), | ||
898 | ('github.com/mitchellh/go-homedir','v1.1.0', '', '', ''), | ||
899 | ('github.com/mitchellh/mapstructure','v1.5.0', '', '', ''), | ||
900 | ('github.com/mitchellh/reflectwalk','v1.0.2', '', '', ''), | ||
901 | ('github.com/nats-io/nats.go','v1.25.0', '', '', ''), | ||
902 | ('github.com/nats-io/nkeys','v0.4.4', '', '', ''), | ||
903 | ('github.com/nats-io/nuid','v1.0.1', '', '', ''), | ||
904 | ('github.com/pmezard/go-difflib','v1.0.0', '', '', ''), | ||
905 | ('github.com/stretchr/objx','v0.5.0', '', '', ''), | ||
906 | ('github.com/x448/float16','v0.8.4', '', '', ''), | ||
907 | ('github.com/zeebo/errs','v1.3.0', '', '', ''), | ||
908 | ('go.googlesource.com/mod','v0.8.0','golang.org/x/mod', '', ''), | ||
909 | ('go.googlesource.com/net','v0.9.0','golang.org/x/net', '', ''), | ||
910 | ('go.googlesource.com/sync','v0.1.0','golang.org/x/sync', '', ''), | ||
911 | ('go.googlesource.com/sys','v0.7.0','golang.org/x/sys', '', ''), | ||
912 | ('go.googlesource.com/text','v0.9.0','golang.org/x/text', '', ''), | ||
913 | ('go.googlesource.com/tools','v0.6.0','golang.org/x/tools', '', ''), | ||
914 | ('github.com/googleapis/go-genproto','v0.0.0-20230223222841-637eb2293923','google.golang.org/genproto', '', ''), | ||
915 | ('github.com/grpc/grpc-go','v1.53.0','google.golang.org/grpc', '', ''), | ||
916 | ('go.googlesource.com/protobuf','v1.28.1','google.golang.org/protobuf', '', ''), | ||
917 | ] | ||
918 | |||
919 | src_uri = set() | ||
920 | for d in dependencies: | ||
921 | src_uri.add(self._go_urifiy(*d)) | ||
922 | |||
923 | checkvars = {} | 794 | checkvars = {} |
924 | checkvars['GO_DEPENDENCIES_SRC_URI'] = src_uri | 795 | checkvars['SRC_URI'] = {'gomod://github.com/godbus/dbus/v5;version=v5.1.0;sha256sum=03dfa8e71089a6f477310d15c4d3a036d82d028532881b50fee254358e782ad9'} |
925 | |||
926 | self.assertTrue(os.path.isfile(deps_require_file)) | ||
927 | self._test_recipe_contents(deps_require_file, checkvars, []) | ||
928 | |||
929 | def test_recipetool_create_go_replace_modules(self): | ||
930 | # Check handling of replaced modules | ||
931 | temprecipe = os.path.join(self.tempdir, 'recipe') | ||
932 | os.makedirs(temprecipe) | ||
933 | |||
934 | recipefile = os.path.join(temprecipe, 'openapi-generator_git.bb') | ||
935 | deps_require_file = os.path.join(temprecipe, 'openapi-generator', 'go-modules.inc') | ||
936 | lics_require_file = os.path.join(temprecipe, 'openapi-generator', 'go-licenses.inc') | ||
937 | modules_txt_file = os.path.join(temprecipe, 'openapi-generator', 'modules.txt') | ||
938 | |||
939 | srcuri = 'https://github.com/OpenAPITools/openapi-generator.git' | ||
940 | srcrev = "v7.2.0" | ||
941 | srcbranch = "master" | ||
942 | srcsubdir = "samples/openapi3/client/petstore/go" | ||
943 | |||
944 | result = runCmd('recipetool create -o %s %s -S %s -B %s --src-subdir %s' % (temprecipe, srcuri, srcrev, srcbranch, srcsubdir)) | ||
945 | |||
946 | self.maxDiff = None | ||
947 | inherits = ['go-vendor'] | ||
948 | |||
949 | checkvars = {} | ||
950 | checkvars['GO_IMPORT'] = "github.com/OpenAPITools/openapi-generator/samples/openapi3/client/petstore/go" | ||
951 | checkvars['SRC_URI'] = {'git://${GO_IMPORT};destsuffix=git/src/${GO_IMPORT};nobranch=1;name=${BPN};protocol=https', | ||
952 | 'file://modules.txt'} | ||
953 | |||
954 | self.assertNotIn('Traceback', result.output) | ||
955 | self.assertIn('No license file was detected for the main module', result.output) | ||
956 | self.assertTrue(os.path.isfile(recipefile)) | ||
957 | self._test_recipe_contents(recipefile, checkvars, inherits) | ||
958 | |||
959 | # make sure that dependencies don't mention local directory ./go-petstore | ||
960 | dependencies = \ | ||
961 | [ ('github.com/stretchr/testify','v1.8.4', '', '', ''), | ||
962 | ('go.googlesource.com/oauth2','v0.10.0','golang.org/x/oauth2', '', ''), | ||
963 | ('github.com/davecgh/go-spew','v1.1.1', '', '', ''), | ||
964 | ('github.com/golang/protobuf','v1.5.3', '', '', ''), | ||
965 | ('github.com/kr/pretty','v0.3.0', '', '', ''), | ||
966 | ('github.com/pmezard/go-difflib','v1.0.0', '', '', ''), | ||
967 | ('github.com/rogpeppe/go-internal','v1.9.0', '', '', ''), | ||
968 | ('go.googlesource.com/net','v0.12.0','golang.org/x/net', '', ''), | ||
969 | ('github.com/golang/appengine','v1.6.7','google.golang.org/appengine', '', ''), | ||
970 | ('go.googlesource.com/protobuf','v1.31.0','google.golang.org/protobuf', '', ''), | ||
971 | ('gopkg.in/check.v1','v1.0.0-20201130134442-10cb98267c6c', '', '', ''), | ||
972 | ('gopkg.in/yaml.v3','v3.0.1', '', '', ''), | ||
973 | ] | ||
974 | |||
975 | src_uri = set() | ||
976 | for d in dependencies: | ||
977 | src_uri.add(self._go_urifiy(*d)) | ||
978 | |||
979 | checkvars = {} | ||
980 | checkvars['GO_DEPENDENCIES_SRC_URI'] = src_uri | ||
981 | |||
982 | self.assertTrue(os.path.isfile(deps_require_file)) | ||
983 | self._test_recipe_contents(deps_require_file, checkvars, []) | 796 | self._test_recipe_contents(deps_require_file, checkvars, []) |
984 | 797 | ||
985 | class RecipetoolTests(RecipetoolBase): | 798 | class RecipetoolTests(RecipetoolBase): |
@@ -1062,6 +875,7 @@ class RecipetoolTests(RecipetoolBase): | |||
1062 | 875 | ||
1063 | d = DataConnectorCopy | 876 | d = DataConnectorCopy |
1064 | d.getVar = Mock(return_value=commonlicdir) | 877 | d.getVar = Mock(return_value=commonlicdir) |
878 | d.expand = Mock(side_effect=lambda x: x) | ||
1065 | 879 | ||
1066 | srctree = tempfile.mkdtemp(prefix='recipetoolqa') | 880 | srctree = tempfile.mkdtemp(prefix='recipetoolqa') |
1067 | self.track_for_cleanup(srctree) | 881 | self.track_for_cleanup(srctree) |
@@ -1317,10 +1131,10 @@ class RecipetoolAppendsrcTests(RecipetoolAppendsrcBase): | |||
1317 | 1131 | ||
1318 | def test_recipetool_appendsrcfile_srcdir_basic(self): | 1132 | def test_recipetool_appendsrcfile_srcdir_basic(self): |
1319 | testrecipe = 'bash' | 1133 | testrecipe = 'bash' |
1320 | bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe) | 1134 | bb_vars = get_bb_vars(['S', 'UNPACKDIR'], testrecipe) |
1321 | srcdir = bb_vars['S'] | 1135 | srcdir = bb_vars['S'] |
1322 | workdir = bb_vars['WORKDIR'] | 1136 | unpackdir = bb_vars['UNPACKDIR'] |
1323 | subdir = os.path.relpath(srcdir, workdir) | 1137 | subdir = os.path.relpath(srcdir, unpackdir) |
1324 | self._test_appendsrcfile(testrecipe, 'a-file', srcdir=subdir) | 1138 | self._test_appendsrcfile(testrecipe, 'a-file', srcdir=subdir) |
1325 | 1139 | ||
1326 | def test_recipetool_appendsrcfile_existing_in_src_uri(self): | 1140 | def test_recipetool_appendsrcfile_existing_in_src_uri(self): |
@@ -1369,10 +1183,10 @@ class RecipetoolAppendsrcTests(RecipetoolAppendsrcBase): | |||
1369 | def test_recipetool_appendsrcfile_replace_file_srcdir(self): | 1183 | def test_recipetool_appendsrcfile_replace_file_srcdir(self): |
1370 | testrecipe = 'bash' | 1184 | testrecipe = 'bash' |
1371 | filepath = 'Makefile.in' | 1185 | filepath = 'Makefile.in' |
1372 | bb_vars = get_bb_vars(['S', 'WORKDIR'], testrecipe) | 1186 | bb_vars = get_bb_vars(['S', 'UNPACKDIR'], testrecipe) |
1373 | srcdir = bb_vars['S'] | 1187 | srcdir = bb_vars['S'] |
1374 | workdir = bb_vars['WORKDIR'] | 1188 | unpackdir = bb_vars['UNPACKDIR'] |
1375 | subdir = os.path.relpath(srcdir, workdir) | 1189 | subdir = os.path.relpath(srcdir, unpackdir) |
1376 | 1190 | ||
1377 | self._test_appendsrcfile(testrecipe, filepath, srcdir=subdir) | 1191 | self._test_appendsrcfile(testrecipe, filepath, srcdir=subdir) |
1378 | bitbake('%s:do_unpack' % testrecipe) | 1192 | bitbake('%s:do_unpack' % testrecipe) |
diff --git a/meta/lib/oeqa/selftest/cases/recipeutils.py b/meta/lib/oeqa/selftest/cases/recipeutils.py index 2cb4445f81..e697fd2920 100644 --- a/meta/lib/oeqa/selftest/cases/recipeutils.py +++ b/meta/lib/oeqa/selftest/cases/recipeutils.py | |||
@@ -72,7 +72,7 @@ class RecipeUtilsTests(OESelftestTestCase): | |||
72 | expected_patch = """ | 72 | expected_patch = """ |
73 | --- a/recipes-test/recipeutils/recipeutils-test_1.2.bb | 73 | --- a/recipes-test/recipeutils/recipeutils-test_1.2.bb |
74 | +++ b/recipes-test/recipeutils/recipeutils-test_1.2.bb | 74 | +++ b/recipes-test/recipeutils/recipeutils-test_1.2.bb |
75 | @@ -8,6 +8,4 @@ | 75 | @@ -10,6 +10,4 @@ |
76 | 76 | ||
77 | BBCLASSEXTEND = "native nativesdk" | 77 | BBCLASSEXTEND = "native nativesdk" |
78 | 78 | ||
@@ -97,7 +97,7 @@ class RecipeUtilsTests(OESelftestTestCase): | |||
97 | expected_patch = """ | 97 | expected_patch = """ |
98 | --- a/recipes-test/recipeutils/recipeutils-test_1.2.bb | 98 | --- a/recipes-test/recipeutils/recipeutils-test_1.2.bb |
99 | +++ b/recipes-test/recipeutils/recipeutils-test_1.2.bb | 99 | +++ b/recipes-test/recipeutils/recipeutils-test_1.2.bb |
100 | @@ -8,6 +8,3 @@ | 100 | @@ -10,6 +10,3 @@ |
101 | 101 | ||
102 | BBCLASSEXTEND = "native nativesdk" | 102 | BBCLASSEXTEND = "native nativesdk" |
103 | 103 | ||
diff --git a/meta/lib/oeqa/selftest/cases/reproducible.py b/meta/lib/oeqa/selftest/cases/reproducible.py index 80e830136f..f06027cb03 100644 --- a/meta/lib/oeqa/selftest/cases/reproducible.py +++ b/meta/lib/oeqa/selftest/cases/reproducible.py | |||
@@ -97,8 +97,10 @@ def compare_file(reference, test, diffutils_sysroot): | |||
97 | result.status = SAME | 97 | result.status = SAME |
98 | return result | 98 | return result |
99 | 99 | ||
100 | def run_diffoscope(a_dir, b_dir, html_dir, max_report_size=0, **kwargs): | 100 | def run_diffoscope(a_dir, b_dir, html_dir, max_report_size=0, max_diff_block_lines=1024, max_diff_block_lines_saved=0, **kwargs): |
101 | return runCmd(['diffoscope', '--no-default-limits', '--max-report-size', str(max_report_size), | 101 | return runCmd(['diffoscope', '--no-default-limits', '--max-report-size', str(max_report_size), |
102 | '--max-diff-block-lines-saved', str(max_diff_block_lines_saved), | ||
103 | '--max-diff-block-lines', str(max_diff_block_lines), | ||
102 | '--exclude-directory-metadata', 'yes', '--html-dir', html_dir, a_dir, b_dir], | 104 | '--exclude-directory-metadata', 'yes', '--html-dir', html_dir, a_dir, b_dir], |
103 | **kwargs) | 105 | **kwargs) |
104 | 106 | ||
@@ -132,8 +134,14 @@ class ReproducibleTests(OESelftestTestCase): | |||
132 | # Maximum report size, in bytes | 134 | # Maximum report size, in bytes |
133 | max_report_size = 250 * 1024 * 1024 | 135 | max_report_size = 250 * 1024 * 1024 |
134 | 136 | ||
137 | # Maximum diff blocks size, in lines | ||
138 | max_diff_block_lines = 1024 | ||
139 | # Maximum diff blocks size (saved in memory), in lines | ||
140 | max_diff_block_lines_saved = max_diff_block_lines | ||
141 | |||
135 | # targets are the things we want to test the reproducibility of | 142 | # targets are the things we want to test the reproducibility of |
136 | targets = ['core-image-minimal', 'core-image-sato', 'core-image-full-cmdline', 'core-image-weston', 'world'] | 143 | # Have to add the virtual targets manually for now as builds may or may not include them as they're exclude from world |
144 | targets = ['core-image-minimal', 'core-image-sato', 'core-image-full-cmdline', 'core-image-weston', 'world', 'virtual/librpc', 'virtual/libsdl2', 'virtual/crypt'] | ||
137 | 145 | ||
138 | # sstate targets are things to pull from sstate to potentially cut build/debugging time | 146 | # sstate targets are things to pull from sstate to potentially cut build/debugging time |
139 | sstate_targets = [] | 147 | sstate_targets = [] |
@@ -161,6 +169,7 @@ class ReproducibleTests(OESelftestTestCase): | |||
161 | 'OEQA_REPRODUCIBLE_TEST_TARGET', | 169 | 'OEQA_REPRODUCIBLE_TEST_TARGET', |
162 | 'OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS', | 170 | 'OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS', |
163 | 'OEQA_REPRODUCIBLE_EXCLUDED_PACKAGES', | 171 | 'OEQA_REPRODUCIBLE_EXCLUDED_PACKAGES', |
172 | 'OEQA_REPRODUCIBLE_TEST_LEAF_TARGETS', | ||
164 | ] | 173 | ] |
165 | bb_vars = get_bb_vars(needed_vars) | 174 | bb_vars = get_bb_vars(needed_vars) |
166 | for v in needed_vars: | 175 | for v in needed_vars: |
@@ -169,19 +178,20 @@ class ReproducibleTests(OESelftestTestCase): | |||
169 | if bb_vars['OEQA_REPRODUCIBLE_TEST_PACKAGE']: | 178 | if bb_vars['OEQA_REPRODUCIBLE_TEST_PACKAGE']: |
170 | self.package_classes = bb_vars['OEQA_REPRODUCIBLE_TEST_PACKAGE'].split() | 179 | self.package_classes = bb_vars['OEQA_REPRODUCIBLE_TEST_PACKAGE'].split() |
171 | 180 | ||
172 | if bb_vars['OEQA_REPRODUCIBLE_TEST_TARGET']: | 181 | if bb_vars['OEQA_REPRODUCIBLE_TEST_TARGET'] or bb_vars['OEQA_REPRODUCIBLE_TEST_LEAF_TARGETS']: |
173 | self.targets = bb_vars['OEQA_REPRODUCIBLE_TEST_TARGET'].split() | 182 | self.targets = (bb_vars['OEQA_REPRODUCIBLE_TEST_TARGET'] or "").split() + (bb_vars['OEQA_REPRODUCIBLE_TEST_LEAF_TARGETS'] or "").split() |
174 | 183 | ||
175 | if bb_vars['OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS']: | 184 | if bb_vars['OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS']: |
176 | self.sstate_targets = bb_vars['OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS'].split() | 185 | self.sstate_targets = bb_vars['OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS'].split() |
177 | 186 | ||
187 | if bb_vars['OEQA_REPRODUCIBLE_TEST_LEAF_TARGETS']: | ||
188 | # Setup to build every DEPENDS of leaf recipes using sstate | ||
189 | for leaf_recipe in bb_vars['OEQA_REPRODUCIBLE_TEST_LEAF_TARGETS'].split(): | ||
190 | self.sstate_targets.extend(get_bb_var('DEPENDS', leaf_recipe).split()) | ||
191 | |||
178 | self.extraresults = {} | 192 | self.extraresults = {} |
179 | self.extraresults.setdefault('reproducible.rawlogs', {})['log'] = '' | ||
180 | self.extraresults.setdefault('reproducible', {}).setdefault('files', {}) | 193 | self.extraresults.setdefault('reproducible', {}).setdefault('files', {}) |
181 | 194 | ||
182 | def append_to_log(self, msg): | ||
183 | self.extraresults['reproducible.rawlogs']['log'] += msg | ||
184 | |||
185 | def compare_packages(self, reference_dir, test_dir, diffutils_sysroot): | 195 | def compare_packages(self, reference_dir, test_dir, diffutils_sysroot): |
186 | result = PackageCompareResults(self.oeqa_reproducible_excluded_packages) | 196 | result = PackageCompareResults(self.oeqa_reproducible_excluded_packages) |
187 | 197 | ||
@@ -208,7 +218,7 @@ class ReproducibleTests(OESelftestTestCase): | |||
208 | 218 | ||
209 | def write_package_list(self, package_class, name, packages): | 219 | def write_package_list(self, package_class, name, packages): |
210 | self.extraresults['reproducible']['files'].setdefault(package_class, {})[name] = [ | 220 | self.extraresults['reproducible']['files'].setdefault(package_class, {})[name] = [ |
211 | {'reference': p.reference, 'test': p.test} for p in packages] | 221 | p.reference.split("/./")[1] for p in packages] |
212 | 222 | ||
213 | def copy_file(self, source, dest): | 223 | def copy_file(self, source, dest): |
214 | bb.utils.mkdirhier(os.path.dirname(dest)) | 224 | bb.utils.mkdirhier(os.path.dirname(dest)) |
@@ -220,7 +230,6 @@ class ReproducibleTests(OESelftestTestCase): | |||
220 | tmpdir = os.path.join(self.topdir, name, 'tmp') | 230 | tmpdir = os.path.join(self.topdir, name, 'tmp') |
221 | if os.path.exists(tmpdir): | 231 | if os.path.exists(tmpdir): |
222 | bb.utils.remove(tmpdir, recurse=True) | 232 | bb.utils.remove(tmpdir, recurse=True) |
223 | |||
224 | config = textwrap.dedent('''\ | 233 | config = textwrap.dedent('''\ |
225 | PACKAGE_CLASSES = "{package_classes}" | 234 | PACKAGE_CLASSES = "{package_classes}" |
226 | TMPDIR = "{tmpdir}" | 235 | TMPDIR = "{tmpdir}" |
@@ -233,11 +242,41 @@ class ReproducibleTests(OESelftestTestCase): | |||
233 | ''').format(package_classes=' '.join('package_%s' % c for c in self.package_classes), | 242 | ''').format(package_classes=' '.join('package_%s' % c for c in self.package_classes), |
234 | tmpdir=tmpdir) | 243 | tmpdir=tmpdir) |
235 | 244 | ||
245 | # Export BB_CONSOLELOG to the calling function and make it constant to | ||
246 | # avoid a case where bitbake would get a timestamp-based filename but | ||
247 | # oe-selftest would, later, get another. | ||
248 | capture_vars.append("BB_CONSOLELOG") | ||
249 | config += 'BB_CONSOLELOG = "${LOG_DIR}/cooker/${MACHINE}/console.log"\n' | ||
250 | |||
251 | # We want different log files for each build, but a persistent bitbake | ||
252 | # may reuse the previous log file so restart the bitbake server. | ||
253 | bitbake("--kill-server") | ||
254 | |||
255 | def print_condensed_error_log(logs, context_lines=10, tail_lines=20): | ||
256 | """Prints errors with context and the end of the log.""" | ||
257 | |||
258 | logs = logs.split("\n") | ||
259 | for i, line in enumerate(logs): | ||
260 | if line.startswith("ERROR"): | ||
261 | self.logger.info("Found ERROR (line %d):" % (i + 1)) | ||
262 | for l in logs[i-context_lines:i+context_lines]: | ||
263 | self.logger.info(" " + l) | ||
264 | |||
265 | self.logger.info("End of log:") | ||
266 | for l in logs[-tail_lines:]: | ||
267 | self.logger.info(" " + l) | ||
268 | |||
269 | bitbake_failure_count = 0 | ||
236 | if not use_sstate: | 270 | if not use_sstate: |
237 | if self.sstate_targets: | 271 | if self.sstate_targets: |
238 | self.logger.info("Building prebuild for %s (sstate allowed)..." % (name)) | 272 | self.logger.info("Building prebuild for %s (sstate allowed)..." % (name)) |
239 | self.write_config(config) | 273 | self.write_config(config) |
240 | bitbake(' '.join(self.sstate_targets)) | 274 | try: |
275 | bitbake("--continue "+' '.join(self.sstate_targets)) | ||
276 | except AssertionError as e: | ||
277 | bitbake_failure_count += 1 | ||
278 | self.logger.error("Bitbake failed! but keep going... Log:") | ||
279 | print_condensed_error_log(str(e)) | ||
241 | 280 | ||
242 | # This config fragment will disable using shared and the sstate | 281 | # This config fragment will disable using shared and the sstate |
243 | # mirror, forcing a complete build from scratch | 282 | # mirror, forcing a complete build from scratch |
@@ -249,9 +288,24 @@ class ReproducibleTests(OESelftestTestCase): | |||
249 | self.logger.info("Building %s (sstate%s allowed)..." % (name, '' if use_sstate else ' NOT')) | 288 | self.logger.info("Building %s (sstate%s allowed)..." % (name, '' if use_sstate else ' NOT')) |
250 | self.write_config(config) | 289 | self.write_config(config) |
251 | d = get_bb_vars(capture_vars) | 290 | d = get_bb_vars(capture_vars) |
252 | # targets used to be called images | 291 | try: |
253 | bitbake(' '.join(getattr(self, 'images', self.targets))) | 292 | # targets used to be called images |
254 | return d | 293 | bitbake("--continue "+' '.join(getattr(self, 'images', self.targets))) |
294 | except AssertionError as e: | ||
295 | bitbake_failure_count += 1 | ||
296 | self.logger.error("Bitbake failed! but keep going... Log:") | ||
297 | print_condensed_error_log(str(e)) | ||
298 | |||
299 | # The calling function expects the existence of the deploy | ||
300 | # directories containing the packages. | ||
301 | # If bitbake failed to create them, do it manually | ||
302 | for c in self.package_classes: | ||
303 | deploy = d['DEPLOY_DIR_' + c.upper()] | ||
304 | if not os.path.exists(deploy): | ||
305 | self.logger.info("Manually creating %s" % deploy) | ||
306 | bb.utils.mkdirhier(deploy) | ||
307 | |||
308 | return (d, bitbake_failure_count) | ||
255 | 309 | ||
256 | def test_reproducible_builds(self): | 310 | def test_reproducible_builds(self): |
257 | def strip_topdir(s): | 311 | def strip_topdir(s): |
@@ -273,15 +327,30 @@ class ReproducibleTests(OESelftestTestCase): | |||
273 | os.chmod(save_dir, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) | 327 | os.chmod(save_dir, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) |
274 | self.logger.info('Non-reproducible packages will be copied to %s', save_dir) | 328 | self.logger.info('Non-reproducible packages will be copied to %s', save_dir) |
275 | 329 | ||
276 | vars_A = self.do_test_build('reproducibleA', self.build_from_sstate) | 330 | # The below bug shows that a few reproducible issues are depends on build dir path length. |
331 | # https://bugzilla.yoctoproject.org/show_bug.cgi?id=15554 | ||
332 | # So, the reproducibleA & reproducibleB directories are changed to reproducibleA & reproducibleB-extended to have different size. | ||
277 | 333 | ||
278 | vars_B = self.do_test_build('reproducibleB', False) | 334 | fails = [] |
335 | vars_list = [None, None] | ||
336 | |||
337 | for i, (name, use_sstate) in enumerate( | ||
338 | (('reproducibleA', self.build_from_sstate), | ||
339 | ('reproducibleB-extended', False))): | ||
340 | (variables, bitbake_failure_count) = self.do_test_build(name, use_sstate) | ||
341 | if bitbake_failure_count > 0: | ||
342 | self.logger.error('%s build failed. Trying to compute built packages differences but the test will fail.' % name) | ||
343 | fails.append("Bitbake %s failure" % name) | ||
344 | if self.save_results: | ||
345 | failure_log_path = os.path.join(save_dir, "bitbake-%s.log" % name) | ||
346 | self.logger.info('Failure log for %s will be copied to %s'% (name, failure_log_path)) | ||
347 | self.copy_file(variables["BB_CONSOLELOG"], failure_log_path) | ||
348 | vars_list[i] = variables | ||
279 | 349 | ||
350 | vars_A, vars_B = vars_list | ||
280 | # NOTE: The temp directories from the reproducible build are purposely | 351 | # NOTE: The temp directories from the reproducible build are purposely |
281 | # kept after the build so it can be diffed for debugging. | 352 | # kept after the build so it can be diffed for debugging. |
282 | 353 | ||
283 | fails = [] | ||
284 | |||
285 | for c in self.package_classes: | 354 | for c in self.package_classes: |
286 | with self.subTest(package_class=c): | 355 | with self.subTest(package_class=c): |
287 | package_class = 'package_' + c | 356 | package_class = 'package_' + c |
@@ -294,8 +363,6 @@ class ReproducibleTests(OESelftestTestCase): | |||
294 | 363 | ||
295 | self.logger.info('Reproducibility summary for %s: %s' % (c, result)) | 364 | self.logger.info('Reproducibility summary for %s: %s' % (c, result)) |
296 | 365 | ||
297 | self.append_to_log('\n'.join("%s: %s" % (r.status, r.test) for r in result.total)) | ||
298 | |||
299 | self.write_package_list(package_class, 'missing', result.missing) | 366 | self.write_package_list(package_class, 'missing', result.missing) |
300 | self.write_package_list(package_class, 'different', result.different) | 367 | self.write_package_list(package_class, 'different', result.different) |
301 | self.write_package_list(package_class, 'different_excluded', result.different_excluded) | 368 | self.write_package_list(package_class, 'different_excluded', result.different_excluded) |
@@ -330,7 +397,9 @@ class ReproducibleTests(OESelftestTestCase): | |||
330 | # Copy jquery to improve the diffoscope output usability | 397 | # Copy jquery to improve the diffoscope output usability |
331 | self.copy_file(os.path.join(jquery_sysroot, 'usr/share/javascript/jquery/jquery.min.js'), os.path.join(package_html_dir, 'jquery.js')) | 398 | self.copy_file(os.path.join(jquery_sysroot, 'usr/share/javascript/jquery/jquery.min.js'), os.path.join(package_html_dir, 'jquery.js')) |
332 | 399 | ||
333 | run_diffoscope('reproducibleA', 'reproducibleB', package_html_dir, max_report_size=self.max_report_size, | 400 | run_diffoscope('reproducibleA', 'reproducibleB-extended', package_html_dir, max_report_size=self.max_report_size, |
401 | max_diff_block_lines_saved=self.max_diff_block_lines_saved, | ||
402 | max_diff_block_lines=self.max_diff_block_lines, | ||
334 | native_sysroot=diffoscope_sysroot, ignore_status=True, cwd=package_dir) | 403 | native_sysroot=diffoscope_sysroot, ignore_status=True, cwd=package_dir) |
335 | 404 | ||
336 | if fails: | 405 | if fails: |
diff --git a/meta/lib/oeqa/selftest/cases/retain.py b/meta/lib/oeqa/selftest/cases/retain.py new file mode 100644 index 0000000000..892be45857 --- /dev/null +++ b/meta/lib/oeqa/selftest/cases/retain.py | |||
@@ -0,0 +1,241 @@ | |||
1 | # Tests for retain.bbclass | ||
2 | # | ||
3 | # Copyright OpenEmbedded Contributors | ||
4 | # | ||
5 | # SPDX-License-Identifier: MIT | ||
6 | # | ||
7 | |||
8 | import os | ||
9 | import glob | ||
10 | import fnmatch | ||
11 | import oe.path | ||
12 | import shutil | ||
13 | import tarfile | ||
14 | from oeqa.utils.commands import bitbake, get_bb_vars | ||
15 | from oeqa.selftest.case import OESelftestTestCase | ||
16 | |||
17 | class Retain(OESelftestTestCase): | ||
18 | |||
19 | def test_retain_always(self): | ||
20 | """ | ||
21 | Summary: Test retain class with RETAIN_DIRS_ALWAYS | ||
22 | Expected: Archive written to RETAIN_OUTDIR when build of test recipe completes | ||
23 | Product: oe-core | ||
24 | Author: Paul Eggleton <paul.eggleton@microsoft.com> | ||
25 | """ | ||
26 | |||
27 | test_recipe = 'quilt-native' | ||
28 | |||
29 | features = 'INHERIT += "retain"\n' | ||
30 | features += 'RETAIN_DIRS_ALWAYS = "${T}"\n' | ||
31 | self.write_config(features) | ||
32 | |||
33 | bitbake('-c clean %s' % test_recipe) | ||
34 | |||
35 | bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR']) | ||
36 | retain_outdir = bb_vars['RETAIN_OUTDIR'] or '' | ||
37 | tmpdir = bb_vars['TMPDIR'] | ||
38 | if len(retain_outdir) < 5: | ||
39 | self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir) | ||
40 | if not oe.path.is_path_parent(tmpdir, retain_outdir): | ||
41 | self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir)) | ||
42 | try: | ||
43 | shutil.rmtree(retain_outdir) | ||
44 | except FileNotFoundError: | ||
45 | pass | ||
46 | |||
47 | bitbake(test_recipe) | ||
48 | if not glob.glob(os.path.join(retain_outdir, '%s_temp_*.tar.gz' % test_recipe)): | ||
49 | self.fail('No output archive for %s created' % test_recipe) | ||
50 | |||
51 | |||
52 | def test_retain_failure(self): | ||
53 | """ | ||
54 | Summary: Test retain class default behaviour | ||
55 | Expected: Archive written to RETAIN_OUTDIR only when build of test | ||
56 | recipe fails, and archive contents are as expected | ||
57 | Product: oe-core | ||
58 | Author: Paul Eggleton <paul.eggleton@microsoft.com> | ||
59 | """ | ||
60 | |||
61 | test_recipe_fail = 'error' | ||
62 | |||
63 | features = 'INHERIT += "retain"\n' | ||
64 | self.write_config(features) | ||
65 | |||
66 | bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR', 'RETAIN_DIRS_ALWAYS', 'RETAIN_DIRS_GLOBAL_ALWAYS']) | ||
67 | if bb_vars['RETAIN_DIRS_ALWAYS']: | ||
68 | self.fail('RETAIN_DIRS_ALWAYS is set, this interferes with the test') | ||
69 | if bb_vars['RETAIN_DIRS_GLOBAL_ALWAYS']: | ||
70 | self.fail('RETAIN_DIRS_GLOBAL_ALWAYS is set, this interferes with the test') | ||
71 | retain_outdir = bb_vars['RETAIN_OUTDIR'] or '' | ||
72 | tmpdir = bb_vars['TMPDIR'] | ||
73 | if len(retain_outdir) < 5: | ||
74 | self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir) | ||
75 | if not oe.path.is_path_parent(tmpdir, retain_outdir): | ||
76 | self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir)) | ||
77 | |||
78 | try: | ||
79 | shutil.rmtree(retain_outdir) | ||
80 | except FileNotFoundError: | ||
81 | pass | ||
82 | |||
83 | bitbake('-c clean %s' % test_recipe_fail) | ||
84 | |||
85 | if os.path.exists(retain_outdir): | ||
86 | retain_dirlist = os.listdir(retain_outdir) | ||
87 | if retain_dirlist: | ||
88 | self.fail('RETAIN_OUTDIR should be empty without failure, contents:\n%s' % '\n'.join(retain_dirlist)) | ||
89 | |||
90 | result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True) | ||
91 | if result.status == 0: | ||
92 | self.fail('Build of %s did not fail as expected' % test_recipe_fail) | ||
93 | |||
94 | archives = glob.glob(os.path.join(retain_outdir, '%s_*.tar.gz' % test_recipe_fail)) | ||
95 | if not archives: | ||
96 | self.fail('No output archive for %s created' % test_recipe_fail) | ||
97 | if len(archives) > 1: | ||
98 | self.fail('More than one archive for %s created' % test_recipe_fail) | ||
99 | for archive in archives: | ||
100 | found = False | ||
101 | archive_prefix = os.path.basename(archive).split('.tar')[0] | ||
102 | expected_prefix_start = '%s_workdir' % test_recipe_fail | ||
103 | if not archive_prefix.startswith(expected_prefix_start): | ||
104 | self.fail('Archive %s name does not start with expected prefix "%s"' % (os.path.basename(archive), expected_prefix_start)) | ||
105 | with tarfile.open(archive) as tf: | ||
106 | for ti in tf: | ||
107 | if not fnmatch.fnmatch(ti.name, '%s/*' % archive_prefix): | ||
108 | self.fail('File without tarball-named subdirectory within tarball %s: %s' % (os.path.basename(archive), ti.name)) | ||
109 | if ti.name.endswith('/temp/log.do_compile'): | ||
110 | found = True | ||
111 | if not found: | ||
112 | self.fail('Did not find log.do_compile in output archive %s' % os.path.basename(archive)) | ||
113 | |||
114 | |||
115 | def test_retain_global(self): | ||
116 | """ | ||
117 | Summary: Test retain class RETAIN_DIRS_GLOBAL_* behaviour | ||
118 | Expected: Ensure RETAIN_DIRS_GLOBAL_ALWAYS always causes an | ||
119 | archive to be created, and RETAIN_DIRS_GLOBAL_FAILURE | ||
120 | only causes an archive to be created on failure. | ||
121 | Also test archive naming (with : character) as an | ||
122 | added bonus. | ||
123 | Product: oe-core | ||
124 | Author: Paul Eggleton <paul.eggleton@microsoft.com> | ||
125 | """ | ||
126 | |||
127 | test_recipe = 'quilt-native' | ||
128 | test_recipe_fail = 'error' | ||
129 | |||
130 | features = 'INHERIT += "retain"\n' | ||
131 | features += 'RETAIN_DIRS_GLOBAL_ALWAYS = "${LOG_DIR};prefix=buildlogs"\n' | ||
132 | features += 'RETAIN_DIRS_GLOBAL_FAILURE = "${STAMPS_DIR}"\n' | ||
133 | self.write_config(features) | ||
134 | |||
135 | bitbake('-c clean %s' % test_recipe) | ||
136 | |||
137 | bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR', 'STAMPS_DIR']) | ||
138 | retain_outdir = bb_vars['RETAIN_OUTDIR'] or '' | ||
139 | tmpdir = bb_vars['TMPDIR'] | ||
140 | if len(retain_outdir) < 5: | ||
141 | self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir) | ||
142 | if not oe.path.is_path_parent(tmpdir, retain_outdir): | ||
143 | self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir)) | ||
144 | try: | ||
145 | shutil.rmtree(retain_outdir) | ||
146 | except FileNotFoundError: | ||
147 | pass | ||
148 | |||
149 | # Test success case | ||
150 | bitbake(test_recipe) | ||
151 | if not glob.glob(os.path.join(retain_outdir, 'buildlogs_*.tar.gz')): | ||
152 | self.fail('No output archive for LOG_DIR created') | ||
153 | stamps_dir = bb_vars['STAMPS_DIR'] | ||
154 | if glob.glob(os.path.join(retain_outdir, '%s_*.tar.gz' % os.path.basename(stamps_dir))): | ||
155 | self.fail('Output archive for STAMPS_DIR created when it should not have been') | ||
156 | |||
157 | # Test failure case | ||
158 | result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True) | ||
159 | if result.status == 0: | ||
160 | self.fail('Build of %s did not fail as expected' % test_recipe_fail) | ||
161 | if not glob.glob(os.path.join(retain_outdir, '%s_*.tar.gz' % os.path.basename(stamps_dir))): | ||
162 | self.fail('Output archive for STAMPS_DIR not created') | ||
163 | if len(glob.glob(os.path.join(retain_outdir, 'buildlogs_*.tar.gz'))) != 2: | ||
164 | self.fail('Should be exactly two buildlogs archives in output dir') | ||
165 | |||
166 | |||
167 | def test_retain_misc(self): | ||
168 | """ | ||
169 | Summary: Test retain class with RETAIN_ENABLED and RETAIN_TARBALL_SUFFIX | ||
170 | Expected: Archive written to RETAIN_OUTDIR only when RETAIN_ENABLED is set | ||
171 | and archive contents are as expected. Also test archive naming | ||
172 | (with : character) as an added bonus. | ||
173 | Product: oe-core | ||
174 | Author: Paul Eggleton <paul.eggleton@microsoft.com> | ||
175 | """ | ||
176 | |||
177 | test_recipe_fail = 'error' | ||
178 | |||
179 | features = 'INHERIT += "retain"\n' | ||
180 | features += 'RETAIN_DIRS_ALWAYS = "${T}"\n' | ||
181 | features += 'RETAIN_ENABLED = "0"\n' | ||
182 | self.write_config(features) | ||
183 | |||
184 | bb_vars = get_bb_vars(['RETAIN_OUTDIR', 'TMPDIR']) | ||
185 | retain_outdir = bb_vars['RETAIN_OUTDIR'] or '' | ||
186 | tmpdir = bb_vars['TMPDIR'] | ||
187 | if len(retain_outdir) < 5: | ||
188 | self.fail('RETAIN_OUTDIR value "%s" is invalid' % retain_outdir) | ||
189 | if not oe.path.is_path_parent(tmpdir, retain_outdir): | ||
190 | self.fail('RETAIN_OUTDIR (%s) is not underneath TMPDIR (%s)' % (retain_outdir, tmpdir)) | ||
191 | |||
192 | try: | ||
193 | shutil.rmtree(retain_outdir) | ||
194 | except FileNotFoundError: | ||
195 | pass | ||
196 | |||
197 | bitbake('-c clean %s' % test_recipe_fail) | ||
198 | result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True) | ||
199 | if result.status == 0: | ||
200 | self.fail('Build of %s did not fail as expected' % test_recipe_fail) | ||
201 | |||
202 | if os.path.exists(retain_outdir) and os.listdir(retain_outdir): | ||
203 | self.fail('RETAIN_OUTDIR should be empty with RETAIN_ENABLED = "0"') | ||
204 | |||
205 | features = 'INHERIT += "retain"\n' | ||
206 | features += 'RETAIN_DIRS_ALWAYS = "${T};prefix=recipelogs"\n' | ||
207 | features += 'RETAIN_TARBALL_SUFFIX = "${DATETIME}-testsuffix.tar.bz2"\n' | ||
208 | features += 'RETAIN_ENABLED = "1"\n' | ||
209 | self.write_config(features) | ||
210 | |||
211 | result = bitbake('-c compile %s' % test_recipe_fail, ignore_status=True) | ||
212 | if result.status == 0: | ||
213 | self.fail('Build of %s did not fail as expected' % test_recipe_fail) | ||
214 | |||
215 | archives = glob.glob(os.path.join(retain_outdir, '%s_*-testsuffix.tar.bz2' % test_recipe_fail)) | ||
216 | if not archives: | ||
217 | self.fail('No output archive for %s created' % test_recipe_fail) | ||
218 | if len(archives) != 2: | ||
219 | self.fail('Two archives for %s expected, but %d exist' % (test_recipe_fail, len(archives))) | ||
220 | recipelogs_found = False | ||
221 | workdir_found = False | ||
222 | for archive in archives: | ||
223 | contents_found = False | ||
224 | archive_prefix = os.path.basename(archive).split('.tar')[0] | ||
225 | if archive_prefix.startswith('%s_recipelogs' % test_recipe_fail): | ||
226 | recipelogs_found = True | ||
227 | if archive_prefix.startswith('%s_workdir' % test_recipe_fail): | ||
228 | workdir_found = True | ||
229 | with tarfile.open(archive, 'r:bz2') as tf: | ||
230 | for ti in tf: | ||
231 | if not fnmatch.fnmatch(ti.name, '%s/*' % (archive_prefix)): | ||
232 | self.fail('File without tarball-named subdirectory within tarball %s: %s' % (os.path.basename(archive), ti.name)) | ||
233 | if ti.name.endswith('/log.do_compile'): | ||
234 | contents_found = True | ||
235 | if not contents_found: | ||
236 | # Both archives should contain this file | ||
237 | self.fail('Did not find log.do_compile in output archive %s' % os.path.basename(archive)) | ||
238 | if not recipelogs_found: | ||
239 | self.fail('No archive with expected "recipelogs" prefix found') | ||
240 | if not workdir_found: | ||
241 | self.fail('No archive with expected "workdir" prefix found') | ||
diff --git a/meta/lib/oeqa/selftest/cases/runtime_test.py b/meta/lib/oeqa/selftest/cases/runtime_test.py index 12000aac16..d58ffa80f5 100644 --- a/meta/lib/oeqa/selftest/cases/runtime_test.py +++ b/meta/lib/oeqa/selftest/cases/runtime_test.py | |||
@@ -174,7 +174,6 @@ TEST_RUNQEMUPARAMS += " slirp" | |||
174 | features += 'PACKAGE_FEED_GPG_NAME = "testuser"\n' | 174 | features += 'PACKAGE_FEED_GPG_NAME = "testuser"\n' |
175 | features += 'PACKAGE_FEED_GPG_PASSPHRASE_FILE = "%s"\n' % os.path.join(signing_key_dir, 'key.passphrase') | 175 | features += 'PACKAGE_FEED_GPG_PASSPHRASE_FILE = "%s"\n' % os.path.join(signing_key_dir, 'key.passphrase') |
176 | features += 'GPG_PATH = "%s"\n' % self.gpg_home | 176 | features += 'GPG_PATH = "%s"\n' % self.gpg_home |
177 | features += 'PSEUDO_IGNORE_PATHS .= ",%s"\n' % self.gpg_home | ||
178 | self.write_config(features) | 177 | self.write_config(features) |
179 | 178 | ||
180 | bitbake('core-image-full-cmdline socat') | 179 | bitbake('core-image-full-cmdline socat') |
@@ -211,7 +210,6 @@ TEST_RUNQEMUPARAMS += " slirp" | |||
211 | features += 'PACKAGE_FEED_GPG_NAME = "testuser"\n' | 210 | features += 'PACKAGE_FEED_GPG_NAME = "testuser"\n' |
212 | features += 'PACKAGE_FEED_GPG_PASSPHRASE_FILE = "%s"\n' % os.path.join(signing_key_dir, 'key.passphrase') | 211 | features += 'PACKAGE_FEED_GPG_PASSPHRASE_FILE = "%s"\n' % os.path.join(signing_key_dir, 'key.passphrase') |
213 | features += 'GPG_PATH = "%s"\n' % self.gpg_home | 212 | features += 'GPG_PATH = "%s"\n' % self.gpg_home |
214 | features += 'PSEUDO_IGNORE_PATHS .= ",%s"\n' % self.gpg_home | ||
215 | self.write_config(features) | 213 | self.write_config(features) |
216 | 214 | ||
217 | # Build core-image-sato and testimage | 215 | # Build core-image-sato and testimage |
@@ -273,7 +271,9 @@ TEST_RUNQEMUPARAMS += " slirp" | |||
273 | import subprocess, os | 271 | import subprocess, os |
274 | 272 | ||
275 | distro = oe.lsb.distro_identifier() | 273 | distro = oe.lsb.distro_identifier() |
276 | if distro and (distro in ['debian-9', 'debian-10', 'centos-7', 'centos-8', 'ubuntu-16.04', 'ubuntu-18.04'] or | 274 | # Merge request to address the issue on centos/rhel/derivatives: |
275 | # https://gitlab.com/cki-project/kernel-ark/-/merge_requests/3449 | ||
276 | if distro and (distro in ['debian-9', 'debian-10', 'centos-7', 'centos-8', 'centos-9', 'ubuntu-16.04', 'ubuntu-18.04'] or | ||
277 | distro.startswith('almalinux') or distro.startswith('rocky')): | 277 | distro.startswith('almalinux') or distro.startswith('rocky')): |
278 | self.skipTest('virgl headless cannot be tested with %s' %(distro)) | 278 | self.skipTest('virgl headless cannot be tested with %s' %(distro)) |
279 | 279 | ||
@@ -310,10 +310,7 @@ class Postinst(OESelftestTestCase): | |||
310 | features += 'IMAGE_FEATURES += "package-management empty-root-password"\n' | 310 | features += 'IMAGE_FEATURES += "package-management empty-root-password"\n' |
311 | features += 'PACKAGE_CLASSES = "%s"\n' % classes | 311 | features += 'PACKAGE_CLASSES = "%s"\n' % classes |
312 | if init_manager == "systemd": | 312 | if init_manager == "systemd": |
313 | features += 'DISTRO_FEATURES:append = " systemd usrmerge"\n' | 313 | features += 'INIT_MANAGER = "systemd"\n' |
314 | features += 'VIRTUAL-RUNTIME_init_manager = "systemd"\n' | ||
315 | features += 'DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"\n' | ||
316 | features += 'VIRTUAL-RUNTIME_initscripts = ""\n' | ||
317 | self.write_config(features) | 314 | self.write_config(features) |
318 | 315 | ||
319 | bitbake('core-image-minimal') | 316 | bitbake('core-image-minimal') |
diff --git a/meta/lib/oeqa/selftest/cases/rust.py b/meta/lib/oeqa/selftest/cases/rust.py index ad14189c6d..d99a58d6b9 100644 --- a/meta/lib/oeqa/selftest/cases/rust.py +++ b/meta/lib/oeqa/selftest/cases/rust.py | |||
@@ -1,11 +1,11 @@ | |||
1 | # SPDX-License-Identifier: MIT | 1 | # SPDX-License-Identifier: MIT |
2 | import os | ||
3 | import subprocess | 2 | import subprocess |
4 | import time | 3 | import time |
5 | from oeqa.core.decorator import OETestTag | 4 | from oeqa.core.decorator import OETestTag |
5 | from oeqa.core.decorator.data import skipIfArch | ||
6 | from oeqa.core.case import OEPTestResultTestCase | 6 | from oeqa.core.case import OEPTestResultTestCase |
7 | from oeqa.selftest.case import OESelftestTestCase | 7 | from oeqa.selftest.case import OESelftestTestCase |
8 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu, Command | 8 | from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu |
9 | from oeqa.utils.sshcontrol import SSHControl | 9 | from oeqa.utils.sshcontrol import SSHControl |
10 | 10 | ||
11 | def parse_results(filename): | 11 | def parse_results(filename): |
@@ -38,15 +38,9 @@ def parse_results(filename): | |||
38 | @OETestTag("toolchain-user") | 38 | @OETestTag("toolchain-user") |
39 | @OETestTag("runqemu") | 39 | @OETestTag("runqemu") |
40 | class RustSelfTestSystemEmulated(OESelftestTestCase, OEPTestResultTestCase): | 40 | class RustSelfTestSystemEmulated(OESelftestTestCase, OEPTestResultTestCase): |
41 | def test_rust(self, *args, **kwargs): | ||
42 | # Disable Rust Oe-selftest | ||
43 | #self.skipTest("The Rust Oe-selftest is disabled.") | ||
44 | |||
45 | # Skip mips32 target since it is unstable with rust tests | ||
46 | machine = get_bb_var('MACHINE') | ||
47 | if machine == "qemumips": | ||
48 | self.skipTest("The mips32 target is skipped for Rust Oe-selftest.") | ||
49 | 41 | ||
42 | @skipIfArch(['mips', 'mips64']) | ||
43 | def test_rust(self, *args, **kwargs): | ||
50 | # build remote-test-server before image build | 44 | # build remote-test-server before image build |
51 | recipe = "rust" | 45 | recipe = "rust" |
52 | start_time = time.time() | 46 | start_time = time.time() |
@@ -66,132 +60,43 @@ class RustSelfTestSystemEmulated(OESelftestTestCase, OEPTestResultTestCase): | |||
66 | # bless: First runs rustfmt to format the codebase, | 60 | # bless: First runs rustfmt to format the codebase, |
67 | # then runs tidy checks. | 61 | # then runs tidy checks. |
68 | exclude_list = [ | 62 | exclude_list = [ |
69 | 'compiler/rustc', | 63 | 'src/bootstrap', |
70 | 'compiler/rustc_interface/src/tests.rs', | ||
71 | 'library/panic_abort', | ||
72 | 'library/panic_unwind', | ||
73 | 'library/test/src/stats/tests.rs', | ||
74 | 'src/bootstrap/builder/tests.rs', | ||
75 | 'src/doc/rustc', | 64 | 'src/doc/rustc', |
76 | 'src/doc/rustdoc', | 65 | 'src/doc/rustdoc', |
77 | 'src/doc/unstable-book', | 66 | 'src/doc/unstable-book', |
67 | 'src/etc/test-float-parse', | ||
78 | 'src/librustdoc', | 68 | 'src/librustdoc', |
79 | 'src/rustdoc-json-types', | 69 | 'src/rustdoc-json-types', |
80 | 'src/tools/compiletest/src/common.rs', | 70 | 'src/tools/jsondoclint', |
81 | 'src/tools/lint-docs', | 71 | 'src/tools/lint-docs', |
72 | 'src/tools/replace-version-placeholder', | ||
82 | 'src/tools/rust-analyzer', | 73 | 'src/tools/rust-analyzer', |
83 | 'src/tools/rustdoc-themes', | 74 | 'src/tools/rustdoc-themes', |
84 | 'src/tools/tidy', | 75 | 'src/tools/rust-installer', |
76 | 'src/tools/suggest-tests', | ||
85 | 'tests/assembly/asm/aarch64-outline-atomics.rs', | 77 | 'tests/assembly/asm/aarch64-outline-atomics.rs', |
86 | 'tests/codegen/abi-main-signature-32bit-c-int.rs', | 78 | 'tests/codegen/issues/issue-122805.rs', |
87 | 'tests/codegen/abi-repr-ext.rs', | ||
88 | 'tests/codegen/abi-x86-interrupt.rs', | ||
89 | 'tests/codegen/branch-protection.rs', | ||
90 | 'tests/codegen/catch-unwind.rs', | ||
91 | 'tests/codegen/cf-protection.rs', | ||
92 | 'tests/codegen/enum-bounds-check-derived-idx.rs', | ||
93 | 'tests/codegen/force-unwind-tables.rs', | ||
94 | 'tests/codegen/intrinsic-no-unnamed-attr.rs', | ||
95 | 'tests/codegen/issues/issue-103840.rs', | ||
96 | 'tests/codegen/issues/issue-47278.rs', | ||
97 | 'tests/codegen/issues/issue-73827-bounds-check-index-in-subexpr.rs', | ||
98 | 'tests/codegen/lifetime_start_end.rs', | ||
99 | 'tests/codegen/local-generics-in-exe-internalized.rs', | ||
100 | 'tests/codegen/match-unoptimized.rs', | ||
101 | 'tests/codegen/noalias-rwlockreadguard.rs', | ||
102 | 'tests/codegen/non-terminate/nonempty-infinite-loop.rs', | ||
103 | 'tests/codegen/noreturn-uninhabited.rs', | ||
104 | 'tests/codegen/repr-transparent-aggregates-3.rs', | ||
105 | 'tests/codegen/riscv-abi/call-llvm-intrinsics.rs', | ||
106 | 'tests/codegen/riscv-abi/riscv64-lp64f-lp64d-abi.rs', | ||
107 | 'tests/codegen/riscv-abi/riscv64-lp64d-abi.rs', | ||
108 | 'tests/codegen/sse42-implies-crc32.rs', | ||
109 | 'tests/codegen/thread-local.rs', | 79 | 'tests/codegen/thread-local.rs', |
110 | 'tests/codegen/uninit-consts.rs', | 80 | 'tests/mir-opt/', |
111 | 'tests/pretty/raw-str-nonexpr.rs', | ||
112 | 'tests/run-make', | 81 | 'tests/run-make', |
113 | 'tests/run-make-fulldeps', | 82 | 'tests/run-make-fulldeps', |
114 | 'tests/rustdoc', | 83 | 'tests/rustdoc', |
115 | 'tests/rustdoc-json', | 84 | 'tests/rustdoc-json', |
116 | 'tests/rustdoc-js-std', | 85 | 'tests/rustdoc-js-std', |
117 | 'tests/rustdoc-ui/cfg-test.rs', | ||
118 | 'tests/rustdoc-ui/check-cfg-test.rs', | ||
119 | 'tests/rustdoc-ui/display-output.rs', | ||
120 | 'tests/rustdoc-ui/doc-comment-multi-line-attr.rs', | ||
121 | 'tests/rustdoc-ui/doc-comment-multi-line-cfg-attr.rs', | ||
122 | 'tests/rustdoc-ui/doc-test-doctest-feature.rs', | ||
123 | 'tests/rustdoc-ui/doctest-multiline-crate-attribute.rs', | ||
124 | 'tests/rustdoc-ui/doctest-output.rs', | ||
125 | 'tests/rustdoc-ui/doc-test-rustdoc-feature.rs', | ||
126 | 'tests/rustdoc-ui/failed-doctest-compile-fail.rs', | ||
127 | 'tests/rustdoc-ui/issue-80992.rs', | ||
128 | 'tests/rustdoc-ui/issue-91134.rs', | ||
129 | 'tests/rustdoc-ui/nocapture-fail.rs', | ||
130 | 'tests/rustdoc-ui/nocapture.rs', | ||
131 | 'tests/rustdoc-ui/no-run-flag.rs', | ||
132 | 'tests/rustdoc-ui/run-directory.rs', | ||
133 | 'tests/rustdoc-ui/test-no_std.rs', | ||
134 | 'tests/rustdoc-ui/test-type.rs', | ||
135 | 'tests/rustdoc/unit-return.rs', | ||
136 | 'tests/ui/abi/stack-probes-lto.rs', | 86 | 'tests/ui/abi/stack-probes-lto.rs', |
137 | 'tests/ui/abi/stack-probes.rs', | 87 | 'tests/ui/abi/stack-probes.rs', |
138 | 'tests/ui/array-slice-vec/subslice-patterns-const-eval-match.rs', | 88 | 'tests/ui/codegen/mismatched-data-layouts.rs', |
139 | 'tests/ui/asm/x86_64/sym.rs', | 89 | 'tests/codegen/rust-abi-arch-specific-adjustment.rs', |
140 | 'tests/ui/associated-type-bounds/fn-apit.rs', | ||
141 | 'tests/ui/associated-type-bounds/fn-dyn-apit.rs', | ||
142 | 'tests/ui/associated-type-bounds/fn-wrap-apit.rs', | ||
143 | 'tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs', | 90 | 'tests/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs', |
144 | 'tests/ui/drop/dynamic-drop.rs', | 91 | 'tests/ui/feature-gates/version_check.rs', |
145 | 'tests/ui/empty_global_asm.rs', | ||
146 | 'tests/ui/functions-closures/fn-help-with-err.rs', | ||
147 | 'tests/ui/linkage-attr/issue-10755.rs', | ||
148 | 'tests/ui/macros/restricted-shadowing-legacy.rs', | ||
149 | 'tests/ui/process/nofile-limit.rs', | ||
150 | 'tests/ui/process/process-panic-after-fork.rs', | ||
151 | 'tests/ui/process/process-sigpipe.rs', | ||
152 | 'tests/ui/simd/target-feature-mixup.rs', | ||
153 | 'tests/ui/structs-enums/multiple-reprs.rs', | ||
154 | 'src/tools/jsondoclint', | ||
155 | 'src/tools/replace-version-placeholder', | ||
156 | 'tests/codegen/abi-efiapi.rs', | ||
157 | 'tests/codegen/abi-sysv64.rs', | ||
158 | 'tests/codegen/align-byval.rs', | ||
159 | 'tests/codegen/align-fn.rs', | ||
160 | 'tests/codegen/asm-powerpc-clobbers.rs', | ||
161 | 'tests/codegen/async-fn-debug-awaitee-field.rs', | ||
162 | 'tests/codegen/binary-search-index-no-bound-check.rs', | ||
163 | 'tests/codegen/call-metadata.rs', | ||
164 | 'tests/codegen/debug-column.rs', | ||
165 | 'tests/codegen/debug-limited.rs', | ||
166 | 'tests/codegen/debuginfo-generic-closure-env-names.rs', | ||
167 | 'tests/codegen/drop.rs', | ||
168 | 'tests/codegen/dst-vtable-align-nonzero.rs', | ||
169 | 'tests/codegen/enable-lto-unit-splitting.rs', | ||
170 | 'tests/codegen/enum/enum-u128.rs', | ||
171 | 'tests/codegen/fn-impl-trait-self.rs', | ||
172 | 'tests/codegen/inherit_overflow.rs', | ||
173 | 'tests/codegen/inline-function-args-debug-info.rs', | ||
174 | 'tests/codegen/intrinsics/mask.rs', | ||
175 | 'tests/codegen/intrinsics/transmute-niched.rs', | ||
176 | 'tests/codegen/issues/issue-73258.rs', | ||
177 | 'tests/codegen/issues/issue-75546.rs', | ||
178 | 'tests/codegen/issues/issue-77812.rs', | ||
179 | 'tests/codegen/issues/issue-98156-const-arg-temp-lifetime.rs', | ||
180 | 'tests/codegen/llvm-ident.rs', | ||
181 | 'tests/codegen/mainsubprogram.rs', | ||
182 | 'tests/codegen/move-operands.rs', | ||
183 | 'tests/codegen/repr/transparent-mips64.rs', | ||
184 | 'tests/mir-opt/', | ||
185 | 'tests/rustdoc-json', | ||
186 | 'tests/rustdoc-ui/doc-test-rustdoc-feature.rs', | ||
187 | 'tests/rustdoc-ui/no-run-flag.rs', | ||
188 | 'tests/ui-fulldeps/', | 92 | 'tests/ui-fulldeps/', |
189 | 'tests/ui/numbers-arithmetic/u128.rs' | 93 | 'tests/ui/process/nofile-limit.rs', |
94 | 'tidyselftest' | ||
190 | ] | 95 | ] |
191 | 96 | ||
192 | exclude_fail_tests = " ".join([" --exclude " + item for item in exclude_list]) | 97 | exclude_fail_tests = " ".join([" --exclude " + item for item in exclude_list]) |
193 | # Add exclude_fail_tests with other test arguments | 98 | # Add exclude_fail_tests with other test arguments |
194 | testargs = exclude_fail_tests + " --doc --no-fail-fast --bless" | 99 | testargs = exclude_fail_tests + " --no-fail-fast --bless" |
195 | 100 | ||
196 | # wrap the execution with a qemu instance. | 101 | # wrap the execution with a qemu instance. |
197 | # Tests are run with 512 tasks in parallel to execute all tests very quickly | 102 | # Tests are run with 512 tasks in parallel to execute all tests very quickly |
@@ -199,7 +104,7 @@ class RustSelfTestSystemEmulated(OESelftestTestCase, OEPTestResultTestCase): | |||
199 | # Copy remote-test-server to image through scp | 104 | # Copy remote-test-server to image through scp |
200 | host_sys = get_bb_var("RUST_BUILD_SYS", "rust") | 105 | host_sys = get_bb_var("RUST_BUILD_SYS", "rust") |
201 | ssh = SSHControl(ip=qemu.ip, logfile=qemu.sshlog, user="root") | 106 | ssh = SSHControl(ip=qemu.ip, logfile=qemu.sshlog, user="root") |
202 | ssh.copy_to(builddir + "/build/" + host_sys + "/stage1-tools-bin/remote-test-server","~/") | 107 | ssh.copy_to(builddir + "/build/" + host_sys + "/stage2-tools-bin/remote-test-server","~/") |
203 | # Execute remote-test-server on image through background ssh | 108 | # Execute remote-test-server on image through background ssh |
204 | command = '~/remote-test-server --bind 0.0.0.0:12345 -v' | 109 | command = '~/remote-test-server --bind 0.0.0.0:12345 -v' |
205 | sshrun=subprocess.Popen(("ssh", '-o', 'UserKnownHostsFile=/dev/null', '-o', 'StrictHostKeyChecking=no', '-f', "root@%s" % qemu.ip, command), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | 110 | sshrun=subprocess.Popen(("ssh", '-o', 'UserKnownHostsFile=/dev/null', '-o', 'StrictHostKeyChecking=no', '-f', "root@%s" % qemu.ip, command), shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
@@ -210,9 +115,8 @@ class RustSelfTestSystemEmulated(OESelftestTestCase, OEPTestResultTestCase): | |||
210 | tmpdir = get_bb_var("TMPDIR", "rust") | 115 | tmpdir = get_bb_var("TMPDIR", "rust") |
211 | 116 | ||
212 | # Set path for target-poky-linux-gcc, RUST_TARGET_PATH and hosttools. | 117 | # Set path for target-poky-linux-gcc, RUST_TARGET_PATH and hosttools. |
213 | cmd = " export PATH=%s/recipe-sysroot-native/usr/bin:$PATH;" % rustlibpath | 118 | cmd = "export TARGET_VENDOR=\"-poky\";" |
214 | cmd = cmd + " export TARGET_VENDOR=\"-poky\";" | 119 | cmd = cmd + " export PATH=%s/recipe-sysroot-native/usr/bin/python3-native:%s/recipe-sysroot-native/usr/bin:%s/recipe-sysroot-native/usr/bin/%s:%s/hosttools:$PATH;" % (rustlibpath, rustlibpath, rustlibpath, tcpath, tmpdir) |
215 | cmd = cmd + " export PATH=%s/recipe-sysroot-native/usr/bin/%s:%s/hosttools:$PATH;" % (rustlibpath, tcpath, tmpdir) | ||
216 | cmd = cmd + " export RUST_TARGET_PATH=%s/rust-targets;" % rustlibpath | 120 | cmd = cmd + " export RUST_TARGET_PATH=%s/rust-targets;" % rustlibpath |
217 | # Trigger testing. | 121 | # Trigger testing. |
218 | cmd = cmd + " export TEST_DEVICE_ADDR=\"%s:12345\";" % qemu.ip | 122 | cmd = cmd + " export TEST_DEVICE_ADDR=\"%s:12345\";" % qemu.ip |
diff --git a/meta/lib/oeqa/selftest/cases/sdk.py b/meta/lib/oeqa/selftest/cases/sdk.py new file mode 100644 index 0000000000..3971365029 --- /dev/null +++ b/meta/lib/oeqa/selftest/cases/sdk.py | |||
@@ -0,0 +1,39 @@ | |||
1 | # | ||
2 | # Copyright OpenEmbedded Contributors | ||
3 | # | ||
4 | # SPDX-License-Identifier: MIT | ||
5 | # | ||
6 | |||
7 | import os.path | ||
8 | |||
9 | from oeqa.selftest.case import OESelftestTestCase | ||
10 | from oeqa.utils.commands import bitbake, get_bb_vars | ||
11 | |||
12 | class SDKTests(OESelftestTestCase): | ||
13 | |||
14 | def load_manifest(self, filename): | ||
15 | manifest = {} | ||
16 | with open(filename) as f: | ||
17 | for line in f: | ||
18 | name, arch, version = line.split(maxsplit=3) | ||
19 | manifest[name] = (version, arch) | ||
20 | return manifest | ||
21 | |||
22 | def test_sdk_manifests(self): | ||
23 | image = "core-image-minimal" | ||
24 | |||
25 | self.write_config(""" | ||
26 | TOOLCHAIN_HOST_TASK:append = " nativesdk-selftest-hello" | ||
27 | IMAGE_INSTALL:append = " selftest-hello" | ||
28 | """) | ||
29 | |||
30 | bitbake(f"{image} -c populate_sdk") | ||
31 | vars = get_bb_vars(['SDK_DEPLOY', 'TOOLCHAIN_OUTPUTNAME'], image) | ||
32 | |||
33 | path = os.path.join(vars["SDK_DEPLOY"], vars["TOOLCHAIN_OUTPUTNAME"] + ".host.manifest") | ||
34 | self.assertNotEqual(os.path.getsize(path), 0, msg="Host manifest is empty") | ||
35 | self.assertIn("nativesdk-selftest-hello", self.load_manifest(path)) | ||
36 | |||
37 | path = os.path.join(vars["SDK_DEPLOY"], vars["TOOLCHAIN_OUTPUTNAME"] + ".target.manifest") | ||
38 | self.assertNotEqual(os.path.getsize(path), 0, msg="Target manifest is empty") | ||
39 | self.assertIn("selftest-hello", self.load_manifest(path)) | ||
diff --git a/meta/lib/oeqa/selftest/cases/signing.py b/meta/lib/oeqa/selftest/cases/signing.py index 18cce0ba25..4df45ba032 100644 --- a/meta/lib/oeqa/selftest/cases/signing.py +++ b/meta/lib/oeqa/selftest/cases/signing.py | |||
@@ -83,6 +83,8 @@ class Signing(OESelftestTestCase): | |||
83 | feature += 'RPM_GPG_PASSPHRASE = "test123"\n' | 83 | feature += 'RPM_GPG_PASSPHRASE = "test123"\n' |
84 | feature += 'RPM_GPG_NAME = "testuser"\n' | 84 | feature += 'RPM_GPG_NAME = "testuser"\n' |
85 | feature += 'GPG_PATH = "%s"\n' % self.gpg_dir | 85 | feature += 'GPG_PATH = "%s"\n' % self.gpg_dir |
86 | feature += 'PACKAGECONFIG:append:pn-rpm-native = " sequoia"\n' | ||
87 | feature += 'PACKAGECONFIG:append:pn-rpm = " sequoia"\n' | ||
86 | 88 | ||
87 | self.write_config(feature) | 89 | self.write_config(feature) |
88 | 90 | ||
diff --git a/meta/lib/oeqa/selftest/cases/spdx.py b/meta/lib/oeqa/selftest/cases/spdx.py index 05fc4e390b..8cd4e83ca2 100644 --- a/meta/lib/oeqa/selftest/cases/spdx.py +++ b/meta/lib/oeqa/selftest/cases/spdx.py | |||
@@ -6,29 +6,39 @@ | |||
6 | 6 | ||
7 | import json | 7 | import json |
8 | import os | 8 | import os |
9 | import textwrap | ||
10 | import hashlib | ||
11 | from pathlib import Path | ||
9 | from oeqa.selftest.case import OESelftestTestCase | 12 | from oeqa.selftest.case import OESelftestTestCase |
10 | from oeqa.utils.commands import bitbake, get_bb_var, runCmd | 13 | from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runCmd |
14 | import oe.spdx30 | ||
11 | 15 | ||
12 | class SPDXCheck(OESelftestTestCase): | ||
13 | 16 | ||
17 | class SPDX22Check(OESelftestTestCase): | ||
14 | @classmethod | 18 | @classmethod |
15 | def setUpClass(cls): | 19 | def setUpClass(cls): |
16 | super(SPDXCheck, cls).setUpClass() | 20 | super().setUpClass() |
17 | bitbake("python3-spdx-tools-native") | 21 | bitbake("python3-spdx-tools-native") |
18 | bitbake("-c addto_recipe_sysroot python3-spdx-tools-native") | 22 | bitbake("-c addto_recipe_sysroot python3-spdx-tools-native") |
19 | 23 | ||
20 | def check_recipe_spdx(self, high_level_dir, spdx_file, target_name): | 24 | def check_recipe_spdx(self, high_level_dir, spdx_file, target_name): |
21 | config = """ | 25 | config = textwrap.dedent( |
22 | INHERIT += "create-spdx" | 26 | """\ |
23 | """ | 27 | INHERIT:remove = "create-spdx" |
28 | INHERIT += "create-spdx-2.2" | ||
29 | """ | ||
30 | ) | ||
24 | self.write_config(config) | 31 | self.write_config(config) |
25 | 32 | ||
26 | deploy_dir = get_bb_var("DEPLOY_DIR") | 33 | deploy_dir = get_bb_var("DEPLOY_DIR") |
27 | machine_var = get_bb_var("MACHINE") | 34 | arch_dir = get_bb_var("PACKAGE_ARCH", target_name) |
35 | spdx_version = get_bb_var("SPDX_VERSION") | ||
28 | # qemux86-64 creates the directory qemux86_64 | 36 | # qemux86-64 creates the directory qemux86_64 |
29 | machine_dir = machine_var.replace("-", "_") | 37 | #arch_dir = arch_var.replace("-", "_") |
30 | 38 | ||
31 | full_file_path = os.path.join(deploy_dir, "spdx", machine_dir, high_level_dir, spdx_file) | 39 | full_file_path = os.path.join( |
40 | deploy_dir, "spdx", spdx_version, arch_dir, high_level_dir, spdx_file | ||
41 | ) | ||
32 | 42 | ||
33 | try: | 43 | try: |
34 | os.remove(full_file_path) | 44 | os.remove(full_file_path) |
@@ -43,8 +53,13 @@ INHERIT += "create-spdx" | |||
43 | self.assertNotEqual(report, None) | 53 | self.assertNotEqual(report, None) |
44 | self.assertNotEqual(report["SPDXID"], None) | 54 | self.assertNotEqual(report["SPDXID"], None) |
45 | 55 | ||
46 | python = os.path.join(get_bb_var('STAGING_BINDIR', 'python3-spdx-tools-native'), 'nativepython3') | 56 | python = os.path.join( |
47 | validator = os.path.join(get_bb_var('STAGING_BINDIR', 'python3-spdx-tools-native'), 'pyspdxtools') | 57 | get_bb_var("STAGING_BINDIR", "python3-spdx-tools-native"), |
58 | "nativepython3", | ||
59 | ) | ||
60 | validator = os.path.join( | ||
61 | get_bb_var("STAGING_BINDIR", "python3-spdx-tools-native"), "pyspdxtools" | ||
62 | ) | ||
48 | result = runCmd("{} {} -i {}".format(python, validator, filename)) | 63 | result = runCmd("{} {} -i {}".format(python, validator, filename)) |
49 | 64 | ||
50 | self.assertExists(full_file_path) | 65 | self.assertExists(full_file_path) |
@@ -52,3 +67,222 @@ INHERIT += "create-spdx" | |||
52 | 67 | ||
53 | def test_spdx_base_files(self): | 68 | def test_spdx_base_files(self): |
54 | self.check_recipe_spdx("packages", "base-files.spdx.json", "base-files") | 69 | self.check_recipe_spdx("packages", "base-files.spdx.json", "base-files") |
70 | |||
71 | def test_spdx_tar(self): | ||
72 | self.check_recipe_spdx("packages", "tar.spdx.json", "tar") | ||
73 | |||
74 | |||
75 | class SPDX3CheckBase(object): | ||
76 | """ | ||
77 | Base class for checking SPDX 3 based tests | ||
78 | """ | ||
79 | |||
80 | def check_spdx_file(self, filename): | ||
81 | self.assertExists(filename) | ||
82 | |||
83 | # Read the file | ||
84 | objset = oe.spdx30.SHACLObjectSet() | ||
85 | with open(filename, "r") as f: | ||
86 | d = oe.spdx30.JSONLDDeserializer() | ||
87 | d.read(f, objset) | ||
88 | |||
89 | return objset | ||
90 | |||
91 | def check_recipe_spdx(self, target_name, spdx_path, *, task=None, extraconf=""): | ||
92 | config = ( | ||
93 | textwrap.dedent( | ||
94 | f"""\ | ||
95 | INHERIT:remove = "create-spdx" | ||
96 | INHERIT += "{self.SPDX_CLASS}" | ||
97 | """ | ||
98 | ) | ||
99 | + textwrap.dedent(extraconf) | ||
100 | ) | ||
101 | |||
102 | self.write_config(config) | ||
103 | |||
104 | if task: | ||
105 | bitbake(f"-c {task} {target_name}") | ||
106 | else: | ||
107 | bitbake(target_name) | ||
108 | |||
109 | filename = spdx_path.format( | ||
110 | **get_bb_vars( | ||
111 | [ | ||
112 | "DEPLOY_DIR_IMAGE", | ||
113 | "DEPLOY_DIR_SPDX", | ||
114 | "MACHINE", | ||
115 | "MACHINE_ARCH", | ||
116 | "SDKMACHINE", | ||
117 | "SDK_DEPLOY", | ||
118 | "SPDX_VERSION", | ||
119 | "SSTATE_PKGARCH", | ||
120 | "TOOLCHAIN_OUTPUTNAME", | ||
121 | ], | ||
122 | target_name, | ||
123 | ) | ||
124 | ) | ||
125 | |||
126 | return self.check_spdx_file(filename) | ||
127 | |||
128 | def check_objset_missing_ids(self, objset): | ||
129 | for o in objset.foreach_type(oe.spdx30.SpdxDocument): | ||
130 | doc = o | ||
131 | break | ||
132 | else: | ||
133 | self.assertTrue(False, "Unable to find SpdxDocument") | ||
134 | |||
135 | missing_ids = objset.missing_ids - set(i.externalSpdxId for i in doc.import_) | ||
136 | if missing_ids: | ||
137 | self.assertTrue( | ||
138 | False, | ||
139 | "The following SPDXIDs are unresolved:\n " + "\n ".join(missing_ids), | ||
140 | ) | ||
141 | |||
142 | |||
143 | class SPDX30Check(SPDX3CheckBase, OESelftestTestCase): | ||
144 | SPDX_CLASS = "create-spdx-3.0" | ||
145 | |||
146 | def test_base_files(self): | ||
147 | self.check_recipe_spdx( | ||
148 | "base-files", | ||
149 | "{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/packages/package-base-files.spdx.json", | ||
150 | ) | ||
151 | |||
152 | def test_gcc_include_source(self): | ||
153 | objset = self.check_recipe_spdx( | ||
154 | "gcc", | ||
155 | "{DEPLOY_DIR_SPDX}/{SSTATE_PKGARCH}/recipes/recipe-gcc.spdx.json", | ||
156 | extraconf="""\ | ||
157 | SPDX_INCLUDE_SOURCES = "1" | ||
158 | """, | ||
159 | ) | ||
160 | |||
161 | gcc_pv = get_bb_var("PV", "gcc") | ||
162 | filename = f"gcc-{gcc_pv}/README" | ||
163 | found = False | ||
164 | for software_file in objset.foreach_type(oe.spdx30.software_File): | ||
165 | if software_file.name == filename: | ||
166 | found = True | ||
167 | self.logger.info( | ||
168 | f"The spdxId of {filename} in recipe-gcc.spdx.json is {software_file.spdxId}" | ||
169 | ) | ||
170 | break | ||
171 | |||
172 | self.assertTrue( | ||
173 | found, f"Not found source file {filename} in recipe-gcc.spdx.json\n" | ||
174 | ) | ||
175 | |||
176 | def test_core_image_minimal(self): | ||
177 | objset = self.check_recipe_spdx( | ||
178 | "core-image-minimal", | ||
179 | "{DEPLOY_DIR_IMAGE}/core-image-minimal-{MACHINE}.rootfs.spdx.json", | ||
180 | ) | ||
181 | |||
182 | # Document should be fully linked | ||
183 | self.check_objset_missing_ids(objset) | ||
184 | |||
185 | def test_core_image_minimal_sdk(self): | ||
186 | objset = self.check_recipe_spdx( | ||
187 | "core-image-minimal", | ||
188 | "{SDK_DEPLOY}/{TOOLCHAIN_OUTPUTNAME}.spdx.json", | ||
189 | task="populate_sdk", | ||
190 | ) | ||
191 | |||
192 | # Document should be fully linked | ||
193 | self.check_objset_missing_ids(objset) | ||
194 | |||
195 | def test_baremetal_helloworld(self): | ||
196 | objset = self.check_recipe_spdx( | ||
197 | "baremetal-helloworld", | ||
198 | "{DEPLOY_DIR_IMAGE}/baremetal-helloworld-image-{MACHINE}.spdx.json", | ||
199 | extraconf="""\ | ||
200 | TCLIBC = "baremetal" | ||
201 | """, | ||
202 | ) | ||
203 | |||
204 | # Document should be fully linked | ||
205 | self.check_objset_missing_ids(objset) | ||
206 | |||
207 | def test_extra_opts(self): | ||
208 | HOST_SPDXID = "http://foo.bar/spdx/bar2" | ||
209 | |||
210 | EXTRACONF = textwrap.dedent( | ||
211 | f"""\ | ||
212 | SPDX_INVOKED_BY_name = "CI Tool" | ||
213 | SPDX_INVOKED_BY_type = "software" | ||
214 | |||
215 | SPDX_ON_BEHALF_OF_name = "John Doe" | ||
216 | SPDX_ON_BEHALF_OF_type = "person" | ||
217 | SPDX_ON_BEHALF_OF_id_email = "John.Doe@noreply.com" | ||
218 | |||
219 | SPDX_PACKAGE_SUPPLIER_name = "ACME Embedded Widgets" | ||
220 | SPDX_PACKAGE_SUPPLIER_type = "organization" | ||
221 | |||
222 | SPDX_AUTHORS += "authorA" | ||
223 | SPDX_AUTHORS_authorA_ref = "SPDX_ON_BEHALF_OF" | ||
224 | |||
225 | SPDX_BUILD_HOST = "host" | ||
226 | |||
227 | SPDX_IMPORTS += "host" | ||
228 | SPDX_IMPORTS_host_spdxid = "{HOST_SPDXID}" | ||
229 | |||
230 | SPDX_INCLUDE_BUILD_VARIABLES = "1" | ||
231 | SPDX_INCLUDE_BITBAKE_PARENT_BUILD = "1" | ||
232 | SPDX_INCLUDE_TIMESTAMPS = "1" | ||
233 | |||
234 | SPDX_PRETTY = "1" | ||
235 | """ | ||
236 | ) | ||
237 | extraconf_hash = hashlib.sha1(EXTRACONF.encode("utf-8")).hexdigest() | ||
238 | |||
239 | objset = self.check_recipe_spdx( | ||
240 | "core-image-minimal", | ||
241 | "{DEPLOY_DIR_IMAGE}/core-image-minimal-{MACHINE}.rootfs.spdx.json", | ||
242 | # Many SPDX variables do not trigger a rebuild, since they are | ||
243 | # intended to record information at the time of the build. As such, | ||
244 | # the extra configuration alone may not trigger a rebuild, and even | ||
245 | # if it does, the task hash won't necessarily be unique. In order | ||
246 | # to make sure rebuilds happen, but still allow these test objects | ||
247 | # to be pulled from sstate (e.g. remain reproducible), change the | ||
248 | # namespace prefix to include the hash of the extra configuration | ||
249 | extraconf=textwrap.dedent( | ||
250 | f"""\ | ||
251 | SPDX_NAMESPACE_PREFIX = "http://spdx.org/spdxdocs/{extraconf_hash}" | ||
252 | """ | ||
253 | ) | ||
254 | + EXTRACONF, | ||
255 | ) | ||
256 | |||
257 | # Document should be fully linked | ||
258 | self.check_objset_missing_ids(objset) | ||
259 | |||
260 | for o in objset.foreach_type(oe.spdx30.SoftwareAgent): | ||
261 | if o.name == "CI Tool": | ||
262 | break | ||
263 | else: | ||
264 | self.assertTrue(False, "Unable to find software tool") | ||
265 | |||
266 | for o in objset.foreach_type(oe.spdx30.Person): | ||
267 | if o.name == "John Doe": | ||
268 | break | ||
269 | else: | ||
270 | self.assertTrue(False, "Unable to find person") | ||
271 | |||
272 | for o in objset.foreach_type(oe.spdx30.Organization): | ||
273 | if o.name == "ACME Embedded Widgets": | ||
274 | break | ||
275 | else: | ||
276 | self.assertTrue(False, "Unable to find organization") | ||
277 | |||
278 | for o in objset.foreach_type(oe.spdx30.SpdxDocument): | ||
279 | doc = o | ||
280 | break | ||
281 | else: | ||
282 | self.assertTrue(False, "Unable to find SpdxDocument") | ||
283 | |||
284 | for i in doc.import_: | ||
285 | if i.externalSpdxId == HOST_SPDXID: | ||
286 | break | ||
287 | else: | ||
288 | self.assertTrue(False, "Unable to find imported Host SpdxID") | ||
diff --git a/meta/lib/oeqa/selftest/cases/sstatetests.py b/meta/lib/oeqa/selftest/cases/sstatetests.py index 86d6cd7464..08f94b168a 100644 --- a/meta/lib/oeqa/selftest/cases/sstatetests.py +++ b/meta/lib/oeqa/selftest/cases/sstatetests.py | |||
@@ -27,17 +27,15 @@ class SStateBase(OESelftestTestCase): | |||
27 | def setUpLocal(self): | 27 | def setUpLocal(self): |
28 | super(SStateBase, self).setUpLocal() | 28 | super(SStateBase, self).setUpLocal() |
29 | self.temp_sstate_location = None | 29 | self.temp_sstate_location = None |
30 | needed_vars = ['SSTATE_DIR', 'NATIVELSBSTRING', 'TCLIBC', 'TUNE_ARCH', | 30 | needed_vars = ['SSTATE_DIR', 'TCLIBC', 'TUNE_ARCH', |
31 | 'TOPDIR', 'TARGET_VENDOR', 'TARGET_OS'] | 31 | 'TOPDIR', 'TARGET_VENDOR', 'TARGET_OS'] |
32 | bb_vars = get_bb_vars(needed_vars) | 32 | bb_vars = get_bb_vars(needed_vars) |
33 | self.sstate_path = bb_vars['SSTATE_DIR'] | 33 | self.sstate_path = bb_vars['SSTATE_DIR'] |
34 | self.hostdistro = bb_vars['NATIVELSBSTRING'] | ||
35 | self.tclibc = bb_vars['TCLIBC'] | 34 | self.tclibc = bb_vars['TCLIBC'] |
36 | self.tune_arch = bb_vars['TUNE_ARCH'] | 35 | self.tune_arch = bb_vars['TUNE_ARCH'] |
37 | self.topdir = bb_vars['TOPDIR'] | 36 | self.topdir = bb_vars['TOPDIR'] |
38 | self.target_vendor = bb_vars['TARGET_VENDOR'] | 37 | self.target_vendor = bb_vars['TARGET_VENDOR'] |
39 | self.target_os = bb_vars['TARGET_OS'] | 38 | self.target_os = bb_vars['TARGET_OS'] |
40 | self.distro_specific_sstate = os.path.join(self.sstate_path, self.hostdistro) | ||
41 | 39 | ||
42 | def track_for_cleanup(self, path): | 40 | def track_for_cleanup(self, path): |
43 | if not keep_temp_files: | 41 | if not keep_temp_files: |
@@ -52,10 +50,7 @@ class SStateBase(OESelftestTestCase): | |||
52 | config_temp_sstate = "SSTATE_DIR = \"%s\"" % temp_sstate_path | 50 | config_temp_sstate = "SSTATE_DIR = \"%s\"" % temp_sstate_path |
53 | self.append_config(config_temp_sstate) | 51 | self.append_config(config_temp_sstate) |
54 | self.track_for_cleanup(temp_sstate_path) | 52 | self.track_for_cleanup(temp_sstate_path) |
55 | bb_vars = get_bb_vars(['SSTATE_DIR', 'NATIVELSBSTRING']) | 53 | self.sstate_path = get_bb_var('SSTATE_DIR') |
56 | self.sstate_path = bb_vars['SSTATE_DIR'] | ||
57 | self.hostdistro = bb_vars['NATIVELSBSTRING'] | ||
58 | self.distro_specific_sstate = os.path.join(self.sstate_path, self.hostdistro) | ||
59 | 54 | ||
60 | if add_local_mirrors: | 55 | if add_local_mirrors: |
61 | config_set_sstate_if_not_set = 'SSTATE_MIRRORS ?= ""' | 56 | config_set_sstate_if_not_set = 'SSTATE_MIRRORS ?= ""' |
@@ -65,8 +60,16 @@ class SStateBase(OESelftestTestCase): | |||
65 | config_sstate_mirror = "SSTATE_MIRRORS += \"file://.* file:///%s/PATH\"" % local_mirror | 60 | config_sstate_mirror = "SSTATE_MIRRORS += \"file://.* file:///%s/PATH\"" % local_mirror |
66 | self.append_config(config_sstate_mirror) | 61 | self.append_config(config_sstate_mirror) |
67 | 62 | ||
63 | def set_hostdistro(self): | ||
64 | # This needs to be read after a BuildStarted event in case it gets changed by event | ||
65 | # handling in uninative.bbclass | ||
66 | self.hostdistro = get_bb_var('NATIVELSBSTRING') | ||
67 | self.distro_specific_sstate = os.path.join(self.sstate_path, self.hostdistro) | ||
68 | |||
68 | # Returns a list containing sstate files | 69 | # Returns a list containing sstate files |
69 | def search_sstate(self, filename_regex, distro_specific=True, distro_nonspecific=True): | 70 | def search_sstate(self, filename_regex, distro_specific=True, distro_nonspecific=True): |
71 | self.set_hostdistro() | ||
72 | |||
70 | result = [] | 73 | result = [] |
71 | for root, dirs, files in os.walk(self.sstate_path): | 74 | for root, dirs, files in os.walk(self.sstate_path): |
72 | if distro_specific and re.search(r"%s/%s/[a-z0-9]{2}/[a-z0-9]{2}$" % (self.sstate_path, self.hostdistro), root): | 75 | if distro_specific and re.search(r"%s/%s/[a-z0-9]{2}/[a-z0-9]{2}$" % (self.sstate_path, self.hostdistro), root): |
@@ -80,55 +83,43 @@ class SStateBase(OESelftestTestCase): | |||
80 | return result | 83 | return result |
81 | 84 | ||
82 | # Test sstate files creation and their location and directory perms | 85 | # Test sstate files creation and their location and directory perms |
83 | def run_test_sstate_creation(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True, should_pass=True): | 86 | def run_test_sstate_creation(self, targets, hostdistro_specific): |
84 | self.config_sstate(temp_sstate_location, [self.sstate_path]) | 87 | self.config_sstate(True, [self.sstate_path]) |
88 | |||
89 | bitbake(['-cclean'] + targets) | ||
85 | 90 | ||
86 | if self.temp_sstate_location: | ||
87 | bitbake(['-cclean'] + targets) | ||
88 | else: | ||
89 | bitbake(['-ccleansstate'] + targets) | ||
90 | |||
91 | # We need to test that the env umask have does not effect sstate directory creation | ||
92 | # So, first, we'll get the current umask and set it to something we know incorrect | ||
93 | # See: sstate_task_postfunc for correct umask of os.umask(0o002) | ||
94 | import os | ||
95 | def current_umask(): | ||
96 | current_umask = os.umask(0) | ||
97 | os.umask(current_umask) | ||
98 | return current_umask | ||
99 | |||
100 | orig_umask = current_umask() | ||
101 | # Set it to a umask we know will be 'wrong' | 91 | # Set it to a umask we know will be 'wrong' |
102 | os.umask(0o022) | 92 | with bb.utils.umask(0o022): |
93 | bitbake(targets) | ||
103 | 94 | ||
104 | bitbake(targets) | 95 | # Distro specific files |
105 | file_tracker = [] | 96 | distro_specific_files = self.search_sstate('|'.join(map(str, targets)), True, False) |
106 | results = self.search_sstate('|'.join(map(str, targets)), distro_specific, distro_nonspecific) | ||
107 | if distro_nonspecific: | ||
108 | for r in results: | ||
109 | if r.endswith(("_populate_lic.tar.zst", "_populate_lic.tar.zst.siginfo", "_fetch.tar.zst.siginfo", "_unpack.tar.zst.siginfo", "_patch.tar.zst.siginfo")): | ||
110 | continue | ||
111 | file_tracker.append(r) | ||
112 | else: | ||
113 | file_tracker = results | ||
114 | 97 | ||
115 | if should_pass: | 98 | # Distro non-specific |
116 | self.assertTrue(file_tracker , msg="Could not find sstate files for: %s" % ', '.join(map(str, targets))) | 99 | distro_non_specific_files = [] |
100 | results = self.search_sstate('|'.join(map(str, targets)), False, True) | ||
101 | for r in results: | ||
102 | if r.endswith(("_populate_lic.tar.zst", "_populate_lic.tar.zst.siginfo", "_fetch.tar.zst.siginfo", "_unpack.tar.zst.siginfo", "_patch.tar.zst.siginfo")): | ||
103 | continue | ||
104 | distro_non_specific_files.append(r) | ||
105 | |||
106 | if hostdistro_specific: | ||
107 | self.assertTrue(distro_specific_files , msg="Could not find sstate files for: %s" % ', '.join(map(str, targets))) | ||
108 | self.assertFalse(distro_non_specific_files, msg="Found sstate files in the wrong place for: %s (found %s)" % (', '.join(map(str, targets)), str(distro_non_specific_files))) | ||
117 | else: | 109 | else: |
118 | self.assertTrue(not file_tracker , msg="Found sstate files in the wrong place for: %s (found %s)" % (', '.join(map(str, targets)), str(file_tracker))) | 110 | self.assertTrue(distro_non_specific_files , msg="Could not find sstate files for: %s" % ', '.join(map(str, targets))) |
111 | self.assertFalse(distro_specific_files, msg="Found sstate files in the wrong place for: %s (found %s)" % (', '.join(map(str, targets)), str(distro_specific_files))) | ||
119 | 112 | ||
120 | # Now we'll walk the tree to check the mode and see if things are incorrect. | 113 | # Now we'll walk the tree to check the mode and see if things are incorrect. |
121 | badperms = [] | 114 | badperms = [] |
122 | for root, dirs, files in os.walk(self.sstate_path): | 115 | for root, dirs, files in os.walk(self.sstate_path): |
123 | for directory in dirs: | 116 | for directory in dirs: |
124 | if (os.stat(os.path.join(root, directory)).st_mode & 0o777) != 0o775: | 117 | mode = os.stat(os.path.join(root, directory)).st_mode & 0o777 |
125 | badperms.append(os.path.join(root, directory)) | 118 | if mode != 0o775: |
126 | 119 | badperms.append("%s: %s vs %s" % (os.path.join(root, directory), mode, 0o775)) | |
127 | # Return to original umask | ||
128 | os.umask(orig_umask) | ||
129 | 120 | ||
130 | if should_pass: | 121 | # Check badperms is empty |
131 | self.assertTrue(badperms , msg="Found sstate directories with the wrong permissions: %s (found %s)" % (', '.join(map(str, targets)), str(badperms))) | 122 | self.assertFalse(badperms , msg="Found sstate directories with the wrong permissions: %s (found %s)" % (', '.join(map(str, targets)), str(badperms))) |
132 | 123 | ||
133 | # Test the sstate files deletion part of the do_cleansstate task | 124 | # Test the sstate files deletion part of the do_cleansstate task |
134 | def run_test_cleansstate_task(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True): | 125 | def run_test_cleansstate_task(self, targets, distro_specific=True, distro_nonspecific=True, temp_sstate_location=True): |
@@ -153,6 +144,8 @@ class SStateBase(OESelftestTestCase): | |||
153 | 144 | ||
154 | bitbake(['-ccleansstate'] + targets) | 145 | bitbake(['-ccleansstate'] + targets) |
155 | 146 | ||
147 | self.set_hostdistro() | ||
148 | |||
156 | bitbake(targets) | 149 | bitbake(targets) |
157 | results = self.search_sstate('|'.join(map(str, [s + r'.*?\.tar.zst$' for s in targets])), distro_specific=False, distro_nonspecific=True) | 150 | results = self.search_sstate('|'.join(map(str, [s + r'.*?\.tar.zst$' for s in targets])), distro_specific=False, distro_nonspecific=True) |
158 | filtered_results = [] | 151 | filtered_results = [] |
@@ -251,17 +244,11 @@ class SStateTests(SStateBase): | |||
251 | bitbake("dbus-wait-test -c unpack") | 244 | bitbake("dbus-wait-test -c unpack") |
252 | 245 | ||
253 | class SStateCreation(SStateBase): | 246 | class SStateCreation(SStateBase): |
254 | def test_sstate_creation_distro_specific_pass(self): | 247 | def test_sstate_creation_distro_specific(self): |
255 | self.run_test_sstate_creation(['binutils-cross-'+ self.tune_arch, 'binutils-native'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True) | 248 | self.run_test_sstate_creation(['binutils-cross-'+ self.tune_arch, 'binutils-native'], hostdistro_specific=True) |
256 | |||
257 | def test_sstate_creation_distro_specific_fail(self): | ||
258 | 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) | ||
259 | 249 | ||
260 | def test_sstate_creation_distro_nonspecific_pass(self): | 250 | def test_sstate_creation_distro_nonspecific(self): |
261 | self.run_test_sstate_creation(['linux-libc-headers'], distro_specific=False, distro_nonspecific=True, temp_sstate_location=True) | 251 | self.run_test_sstate_creation(['linux-libc-headers'], hostdistro_specific=False) |
262 | |||
263 | def test_sstate_creation_distro_nonspecific_fail(self): | ||
264 | self.run_test_sstate_creation(['linux-libc-headers'], distro_specific=True, distro_nonspecific=False, temp_sstate_location=True, should_pass=False) | ||
265 | 252 | ||
266 | class SStateCleanup(SStateBase): | 253 | class SStateCleanup(SStateBase): |
267 | def test_cleansstate_task_distro_specific_nonspecific(self): | 254 | def test_cleansstate_task_distro_specific_nonspecific(self): |
@@ -367,18 +354,11 @@ class SStateCacheManagement(SStateBase): | |||
367 | self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic']) | 354 | self.run_test_sstate_cache_management_script('m4', global_config, target_config, ignore_patterns=['populate_lic']) |
368 | 355 | ||
369 | class SStateHashSameSigs(SStateBase): | 356 | class SStateHashSameSigs(SStateBase): |
370 | def test_sstate_32_64_same_hash(self): | 357 | def sstate_hashtest(self, sdkmachine): |
371 | """ | ||
372 | The sstate checksums for both native and target should not vary whether | ||
373 | they're built on a 32 or 64 bit system. Rather than requiring two different | ||
374 | build machines and running a builds, override the variables calling uname() | ||
375 | manually and check using bitbake -S. | ||
376 | """ | ||
377 | 358 | ||
378 | self.write_config(""" | 359 | self.write_config(""" |
379 | MACHINE = "qemux86" | 360 | MACHINE = "qemux86" |
380 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash" | 361 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash" |
381 | TCLIBCAPPEND = "" | ||
382 | BUILD_ARCH = "x86_64" | 362 | BUILD_ARCH = "x86_64" |
383 | BUILD_OS = "linux" | 363 | BUILD_OS = "linux" |
384 | SDKMACHINE = "x86_64" | 364 | SDKMACHINE = "x86_64" |
@@ -390,13 +370,12 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
390 | self.write_config(""" | 370 | self.write_config(""" |
391 | MACHINE = "qemux86" | 371 | MACHINE = "qemux86" |
392 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash2" | 372 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash2" |
393 | TCLIBCAPPEND = "" | ||
394 | BUILD_ARCH = "i686" | 373 | BUILD_ARCH = "i686" |
395 | BUILD_OS = "linux" | 374 | BUILD_OS = "linux" |
396 | SDKMACHINE = "i686" | 375 | SDKMACHINE = "%s" |
397 | PACKAGE_CLASSES = "package_rpm package_ipk package_deb" | 376 | PACKAGE_CLASSES = "package_rpm package_ipk package_deb" |
398 | BB_SIGNATURE_HANDLER = "OEBasicHash" | 377 | BB_SIGNATURE_HANDLER = "OEBasicHash" |
399 | """) | 378 | """ % sdkmachine) |
400 | self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2") | 379 | self.track_for_cleanup(self.topdir + "/tmp-sstatesamehash2") |
401 | bitbake("core-image-weston -S none") | 380 | bitbake("core-image-weston -S none") |
402 | 381 | ||
@@ -416,6 +395,20 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
416 | self.maxDiff = None | 395 | self.maxDiff = None |
417 | self.assertCountEqual(files1, files2) | 396 | self.assertCountEqual(files1, files2) |
418 | 397 | ||
398 | def test_sstate_32_64_same_hash(self): | ||
399 | """ | ||
400 | The sstate checksums for both native and target should not vary whether | ||
401 | they're built on a 32 or 64 bit system. Rather than requiring two different | ||
402 | build machines and running a builds, override the variables calling uname() | ||
403 | manually and check using bitbake -S. | ||
404 | """ | ||
405 | self.sstate_hashtest("i686") | ||
406 | |||
407 | def test_sstate_sdk_arch_same_hash(self): | ||
408 | """ | ||
409 | Similarly, test an arm SDK has the same hashes | ||
410 | """ | ||
411 | self.sstate_hashtest("aarch64") | ||
419 | 412 | ||
420 | def test_sstate_nativelsbstring_same_hash(self): | 413 | def test_sstate_nativelsbstring_same_hash(self): |
421 | """ | 414 | """ |
@@ -426,7 +419,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
426 | 419 | ||
427 | self.write_config(""" | 420 | self.write_config(""" |
428 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" | 421 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" |
429 | TCLIBCAPPEND = \"\" | ||
430 | NATIVELSBSTRING = \"DistroA\" | 422 | NATIVELSBSTRING = \"DistroA\" |
431 | BB_SIGNATURE_HANDLER = "OEBasicHash" | 423 | BB_SIGNATURE_HANDLER = "OEBasicHash" |
432 | """) | 424 | """) |
@@ -434,7 +426,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
434 | bitbake("core-image-weston -S none") | 426 | bitbake("core-image-weston -S none") |
435 | self.write_config(""" | 427 | self.write_config(""" |
436 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" | 428 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" |
437 | TCLIBCAPPEND = \"\" | ||
438 | NATIVELSBSTRING = \"DistroB\" | 429 | NATIVELSBSTRING = \"DistroB\" |
439 | BB_SIGNATURE_HANDLER = "OEBasicHash" | 430 | BB_SIGNATURE_HANDLER = "OEBasicHash" |
440 | """) | 431 | """) |
@@ -463,17 +454,17 @@ class SStateHashSameSigs2(SStateBase): | |||
463 | 454 | ||
464 | configA = """ | 455 | configA = """ |
465 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" | 456 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" |
466 | TCLIBCAPPEND = \"\" | ||
467 | MACHINE = \"qemux86-64\" | 457 | MACHINE = \"qemux86-64\" |
468 | BB_SIGNATURE_HANDLER = "OEBasicHash" | 458 | BB_SIGNATURE_HANDLER = "OEBasicHash" |
469 | """ | 459 | """ |
470 | #OLDEST_KERNEL is arch specific so set to a different value here for testing | 460 | #OLDEST_KERNEL is arch specific so set to a different value here for testing |
471 | configB = """ | 461 | configB = """ |
472 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" | 462 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" |
473 | TCLIBCAPPEND = \"\" | ||
474 | MACHINE = \"qemuarm\" | 463 | MACHINE = \"qemuarm\" |
475 | OLDEST_KERNEL = \"3.3.0\" | 464 | OLDEST_KERNEL = \"3.3.0\" |
476 | BB_SIGNATURE_HANDLER = "OEBasicHash" | 465 | BB_SIGNATURE_HANDLER = "OEBasicHash" |
466 | ERROR_QA:append = " somenewoption" | ||
467 | WARN_QA:append = " someotheroption" | ||
477 | """ | 468 | """ |
478 | self.sstate_common_samesigs(configA, configB, allarch=True) | 469 | self.sstate_common_samesigs(configA, configB, allarch=True) |
479 | 470 | ||
@@ -484,7 +475,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
484 | 475 | ||
485 | configA = """ | 476 | configA = """ |
486 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" | 477 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" |
487 | TCLIBCAPPEND = \"\" | ||
488 | MACHINE = \"qemux86-64\" | 478 | MACHINE = \"qemux86-64\" |
489 | require conf/multilib.conf | 479 | require conf/multilib.conf |
490 | MULTILIBS = \"multilib:lib32\" | 480 | MULTILIBS = \"multilib:lib32\" |
@@ -493,7 +483,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
493 | """ | 483 | """ |
494 | configB = """ | 484 | configB = """ |
495 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" | 485 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" |
496 | TCLIBCAPPEND = \"\" | ||
497 | MACHINE = \"qemuarm\" | 486 | MACHINE = \"qemuarm\" |
498 | require conf/multilib.conf | 487 | require conf/multilib.conf |
499 | MULTILIBS = \"\" | 488 | MULTILIBS = \"\" |
@@ -511,7 +500,6 @@ class SStateHashSameSigs3(SStateBase): | |||
511 | 500 | ||
512 | self.write_config(""" | 501 | self.write_config(""" |
513 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" | 502 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" |
514 | TCLIBCAPPEND = \"\" | ||
515 | MACHINE = \"qemux86\" | 503 | MACHINE = \"qemux86\" |
516 | require conf/multilib.conf | 504 | require conf/multilib.conf |
517 | MULTILIBS = "multilib:lib32" | 505 | MULTILIBS = "multilib:lib32" |
@@ -522,7 +510,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
522 | bitbake("world meta-toolchain -S none") | 510 | bitbake("world meta-toolchain -S none") |
523 | self.write_config(""" | 511 | self.write_config(""" |
524 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" | 512 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" |
525 | TCLIBCAPPEND = \"\" | ||
526 | MACHINE = \"qemux86copy\" | 513 | MACHINE = \"qemux86copy\" |
527 | require conf/multilib.conf | 514 | require conf/multilib.conf |
528 | MULTILIBS = "multilib:lib32" | 515 | MULTILIBS = "multilib:lib32" |
@@ -559,7 +546,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
559 | 546 | ||
560 | self.write_config(""" | 547 | self.write_config(""" |
561 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" | 548 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" |
562 | TCLIBCAPPEND = \"\" | ||
563 | MACHINE = \"qemux86\" | 549 | MACHINE = \"qemux86\" |
564 | require conf/multilib.conf | 550 | require conf/multilib.conf |
565 | MULTILIBS = "multilib:lib32" | 551 | MULTILIBS = "multilib:lib32" |
@@ -570,7 +556,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
570 | bitbake("binutils-native -S none") | 556 | bitbake("binutils-native -S none") |
571 | self.write_config(""" | 557 | self.write_config(""" |
572 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" | 558 | TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" |
573 | TCLIBCAPPEND = \"\" | ||
574 | MACHINE = \"qemux86copy\" | 559 | MACHINE = \"qemux86copy\" |
575 | BB_SIGNATURE_HANDLER = "OEBasicHash" | 560 | BB_SIGNATURE_HANDLER = "OEBasicHash" |
576 | """) | 561 | """) |
@@ -598,7 +583,6 @@ class SStateHashSameSigs4(SStateBase): | |||
598 | 583 | ||
599 | self.write_config(""" | 584 | self.write_config(""" |
600 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash" | 585 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash" |
601 | TCLIBCAPPEND = "" | ||
602 | BB_NUMBER_THREADS = "${@oe.utils.cpu_count()}" | 586 | BB_NUMBER_THREADS = "${@oe.utils.cpu_count()}" |
603 | PARALLEL_MAKE = "-j 1" | 587 | PARALLEL_MAKE = "-j 1" |
604 | DL_DIR = "${TOPDIR}/download1" | 588 | DL_DIR = "${TOPDIR}/download1" |
@@ -613,7 +597,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash" | |||
613 | bitbake("world meta-toolchain -S none") | 597 | bitbake("world meta-toolchain -S none") |
614 | self.write_config(""" | 598 | self.write_config(""" |
615 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash2" | 599 | TMPDIR = "${TOPDIR}/tmp-sstatesamehash2" |
616 | TCLIBCAPPEND = "" | ||
617 | BB_NUMBER_THREADS = "${@oe.utils.cpu_count()+1}" | 600 | BB_NUMBER_THREADS = "${@oe.utils.cpu_count()+1}" |
618 | PARALLEL_MAKE = "-j 2" | 601 | PARALLEL_MAKE = "-j 2" |
619 | DL_DIR = "${TOPDIR}/download2" | 602 | DL_DIR = "${TOPDIR}/download2" |
@@ -724,7 +707,6 @@ class SStateFindSiginfo(SStateBase): | |||
724 | """ | 707 | """ |
725 | self.write_config(""" | 708 | self.write_config(""" |
726 | TMPDIR = \"${TOPDIR}/tmp-sstates-findsiginfo\" | 709 | TMPDIR = \"${TOPDIR}/tmp-sstates-findsiginfo\" |
727 | TCLIBCAPPEND = \"\" | ||
728 | MACHINE = \"qemux86-64\" | 710 | MACHINE = \"qemux86-64\" |
729 | require conf/multilib.conf | 711 | require conf/multilib.conf |
730 | MULTILIBS = "multilib:lib32" | 712 | MULTILIBS = "multilib:lib32" |
@@ -917,15 +899,24 @@ INHERIT += "base-do-configure-modified" | |||
917 | """, | 899 | """, |
918 | expected_sametmp_output, expected_difftmp_output) | 900 | expected_sametmp_output, expected_difftmp_output) |
919 | 901 | ||
920 | @OETestTag("yocto-mirrors") | 902 | class SStateCheckObjectPresence(SStateBase): |
921 | class SStateMirrors(SStateBase): | 903 | def check_bb_output(self, output, targets, exceptions, check_cdn): |
922 | def check_bb_output(self, output, exceptions, check_cdn): | ||
923 | def is_exception(object, exceptions): | 904 | def is_exception(object, exceptions): |
924 | for e in exceptions: | 905 | for e in exceptions: |
925 | if re.search(e, object): | 906 | if re.search(e, object): |
926 | return True | 907 | return True |
927 | return False | 908 | return False |
928 | 909 | ||
910 | # sstate is checked for existence of these, but they never get written out to begin with | ||
911 | exceptions += ["{}.*image_qa".format(t) for t in targets.split()] | ||
912 | exceptions += ["{}.*deploy_source_date_epoch".format(t) for t in targets.split()] | ||
913 | exceptions += ["{}.*image_complete".format(t) for t in targets.split()] | ||
914 | exceptions += ["linux-yocto.*shared_workdir"] | ||
915 | # these get influnced by IMAGE_FSTYPES tweaks in yocto-autobuilder-helper's config.json (on x86-64) | ||
916 | # additionally, they depend on noexec (thus, absent stamps) package, install, etc. image tasks, | ||
917 | # which makes tracing other changes difficult | ||
918 | exceptions += ["{}.*create_.*spdx".format(t) for t in targets.split()] | ||
919 | |||
929 | output_l = output.splitlines() | 920 | output_l = output.splitlines() |
930 | for l in output_l: | 921 | for l in output_l: |
931 | if l.startswith("Sstate summary"): | 922 | if l.startswith("Sstate summary"): |
@@ -960,24 +951,15 @@ class SStateMirrors(SStateBase): | |||
960 | self.assertEqual(len(failed_urls), missing_objects, "Amount of reported missing objects does not match failed URLs: {}\nFailed URLs:\n{}\nFetcher diagnostics:\n{}".format(missing_objects, "\n".join(failed_urls), "\n".join(failed_urls_extrainfo))) | 951 | self.assertEqual(len(failed_urls), missing_objects, "Amount of reported missing objects does not match failed URLs: {}\nFailed URLs:\n{}\nFetcher diagnostics:\n{}".format(missing_objects, "\n".join(failed_urls), "\n".join(failed_urls_extrainfo))) |
961 | self.assertEqual(len(failed_urls), 0, "Missing objects in the cache:\n{}\nFetcher diagnostics:\n{}".format("\n".join(failed_urls), "\n".join(failed_urls_extrainfo))) | 952 | self.assertEqual(len(failed_urls), 0, "Missing objects in the cache:\n{}\nFetcher diagnostics:\n{}".format("\n".join(failed_urls), "\n".join(failed_urls_extrainfo))) |
962 | 953 | ||
954 | @OETestTag("yocto-mirrors") | ||
955 | class SStateMirrors(SStateCheckObjectPresence): | ||
963 | def run_test(self, machine, targets, exceptions, check_cdn = True, ignore_errors = False): | 956 | def run_test(self, machine, targets, exceptions, check_cdn = True, ignore_errors = False): |
964 | # sstate is checked for existence of these, but they never get written out to begin with | ||
965 | exceptions += ["{}.*image_qa".format(t) for t in targets.split()] | ||
966 | exceptions += ["{}.*deploy_source_date_epoch".format(t) for t in targets.split()] | ||
967 | exceptions += ["{}.*image_complete".format(t) for t in targets.split()] | ||
968 | exceptions += ["linux-yocto.*shared_workdir"] | ||
969 | # these get influnced by IMAGE_FSTYPES tweaks in yocto-autobuilder-helper's config.json (on x86-64) | ||
970 | # additionally, they depend on noexec (thus, absent stamps) package, install, etc. image tasks, | ||
971 | # which makes tracing other changes difficult | ||
972 | exceptions += ["{}.*create_spdx".format(t) for t in targets.split()] | ||
973 | exceptions += ["{}.*create_runtime_spdx".format(t) for t in targets.split()] | ||
974 | |||
975 | if check_cdn: | 957 | if check_cdn: |
976 | self.config_sstate(True) | 958 | self.config_sstate(True) |
977 | self.append_config(""" | 959 | self.append_config(""" |
978 | MACHINE = "{}" | 960 | MACHINE = "{}" |
979 | BB_HASHSERVE_UPSTREAM = "hashserv.yocto.io:8687" | 961 | BB_HASHSERVE_UPSTREAM = "hashserv.yoctoproject.org:8686" |
980 | SSTATE_MIRRORS ?= "file://.* http://cdn.jsdelivr.net/yocto/sstate/all/PATH;downloadfilename=PATH" | 962 | SSTATE_MIRRORS ?= "file://.* http://sstate.yoctoproject.org/all/PATH;downloadfilename=PATH" |
981 | """.format(machine)) | 963 | """.format(machine)) |
982 | else: | 964 | else: |
983 | self.append_config(""" | 965 | self.append_config(""" |
@@ -987,7 +969,7 @@ MACHINE = "{}" | |||
987 | bitbake("-S none {}".format(targets)) | 969 | bitbake("-S none {}".format(targets)) |
988 | if ignore_errors: | 970 | if ignore_errors: |
989 | return | 971 | return |
990 | self.check_bb_output(result.output, exceptions, check_cdn) | 972 | self.check_bb_output(result.output, targets, exceptions, check_cdn) |
991 | 973 | ||
992 | def test_cdn_mirror_qemux86_64(self): | 974 | def test_cdn_mirror_qemux86_64(self): |
993 | exceptions = [] | 975 | exceptions = [] |
diff --git a/meta/lib/oeqa/selftest/cases/toolchain.py b/meta/lib/oeqa/selftest/cases/toolchain.py new file mode 100644 index 0000000000..b4b280d037 --- /dev/null +++ b/meta/lib/oeqa/selftest/cases/toolchain.py | |||
@@ -0,0 +1,71 @@ | |||
1 | # | ||
2 | # Copyright OpenEmbedded Contributors | ||
3 | # | ||
4 | # SPDX-License-Identifier: MIT | ||
5 | # | ||
6 | |||
7 | import shutil | ||
8 | import subprocess | ||
9 | import tempfile | ||
10 | from types import SimpleNamespace | ||
11 | |||
12 | import oe.path | ||
13 | from oeqa.selftest.case import OESelftestTestCase | ||
14 | from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars | ||
15 | |||
16 | class ToolchainTests(OESelftestTestCase): | ||
17 | |||
18 | def test_toolchain_switching(self): | ||
19 | """ | ||
20 | Test that a configuration that uses GCC by default but clang for one | ||
21 | specific recipe does infact do that. | ||
22 | """ | ||
23 | |||
24 | def extract_comment(objcopy, filename): | ||
25 | """ | ||
26 | Using the specified `objcopy`, return the .comment segment from | ||
27 | `filename` as a bytes(). | ||
28 | """ | ||
29 | with tempfile.NamedTemporaryFile(prefix="comment-") as f: | ||
30 | cmd = [objcopy, "--dump-section", ".comment=" + f.name, filename] | ||
31 | subprocess.run(cmd, check=True) | ||
32 | # clang's objcopy writes to a temporary file and renames, so we need to re-open. | ||
33 | with open(f.name, "rb") as f2: | ||
34 | return f2.read() | ||
35 | |||
36 | def check_recipe(recipe, filename, override, comment_present, comment_absent=None): | ||
37 | """ | ||
38 | Check that `filename` in `recipe`'s bindir contains `comment`, and | ||
39 | the overrides contain `override`. | ||
40 | """ | ||
41 | d = SimpleNamespace(**get_bb_vars(("D", "bindir", "OBJCOPY", "OVERRIDES", "PATH"), target=recipe)) | ||
42 | |||
43 | self.assertIn(override, d.OVERRIDES) | ||
44 | |||
45 | binary = oe.path.join(d.D, d.bindir, filename) | ||
46 | |||
47 | objcopy = shutil.which(d.OBJCOPY, path=d.PATH) | ||
48 | self.assertIsNotNone(objcopy) | ||
49 | |||
50 | comment = extract_comment(objcopy, binary) | ||
51 | self.assertIn(comment_present, comment) | ||
52 | if comment_absent: | ||
53 | self.assertNotIn(comment_absent, comment) | ||
54 | |||
55 | |||
56 | # GCC by default, clang for selftest-hello. | ||
57 | self.write_config(""" | ||
58 | TOOLCHAIN = "gcc" | ||
59 | TOOLCHAIN:pn-selftest-hello = "clang" | ||
60 | """) | ||
61 | |||
62 | # Force these recipes to re-install so we can extract the .comments from | ||
63 | # the install directory, as they're stripped out of the final packages. | ||
64 | bitbake("m4 selftest-hello -C install") | ||
65 | |||
66 | # m4 should be built with GCC and only GCC | ||
67 | check_recipe("m4", "m4", "toolchain-gcc", b"GCC: (GNU)", b"clang") | ||
68 | |||
69 | # helloworld should be built with clang. We can't assert that GCC is not | ||
70 | # present as it will be linked against glibc which is built with GCC. | ||
71 | check_recipe("selftest-hello", "helloworld", "toolchain-clang", b"clang version") | ||
diff --git a/meta/lib/oeqa/selftest/cases/uboot.py b/meta/lib/oeqa/selftest/cases/uboot.py new file mode 100644 index 0000000000..980ea327f0 --- /dev/null +++ b/meta/lib/oeqa/selftest/cases/uboot.py | |||
@@ -0,0 +1,98 @@ | |||
1 | # Qemu-based u-boot bootloader integration testing | ||
2 | # | ||
3 | # Copyright OpenEmbedded Contributors | ||
4 | # | ||
5 | # SPDX-License-Identifier: MIT | ||
6 | # | ||
7 | |||
8 | from oeqa.selftest.case import OESelftestTestCase | ||
9 | from oeqa.utils.commands import bitbake, runqemu, get_bb_var, get_bb_vars, runCmd | ||
10 | from oeqa.core.decorator.data import skipIfNotArch, skipIfNotBuildArch | ||
11 | from oeqa.core.decorator import OETestTag | ||
12 | |||
13 | uboot_boot_patterns = { | ||
14 | 'search_reached_prompt': "stop autoboot", | ||
15 | 'search_login_succeeded': "=>", | ||
16 | 'search_cmd_finished': "=>" | ||
17 | } | ||
18 | |||
19 | |||
20 | class UBootTest(OESelftestTestCase): | ||
21 | |||
22 | @skipIfNotArch(['arm', 'aarch64']) | ||
23 | @OETestTag("runqemu") | ||
24 | def test_boot_uboot(self): | ||
25 | """ | ||
26 | Tests building u-boot and booting it with QEMU | ||
27 | """ | ||
28 | |||
29 | self.write_config(""" | ||
30 | QB_DEFAULT_BIOS = "u-boot.bin" | ||
31 | PREFERRED_PROVIDER_virtual/bootloader = "u-boot" | ||
32 | QEMU_USE_KVM = "False" | ||
33 | """) | ||
34 | bitbake("virtual/bootloader core-image-minimal") | ||
35 | |||
36 | with runqemu('core-image-minimal', ssh=False, runqemuparams='nographic', | ||
37 | boot_patterns=uboot_boot_patterns) as qemu: | ||
38 | |||
39 | # test if u-boot console works | ||
40 | cmd = "version" | ||
41 | status, output = qemu.run_serial(cmd) | ||
42 | self.assertEqual(status, 1, msg=output) | ||
43 | self.assertTrue("U-Boot" in output, msg=output) | ||
44 | |||
45 | @skipIfNotArch(['aarch64']) | ||
46 | @skipIfNotBuildArch(['aarch64']) | ||
47 | @OETestTag("runqemu") | ||
48 | def test_boot_uboot_kvm_to_full_target(self): | ||
49 | """ | ||
50 | Tests building u-boot and booting it with QEMU and KVM. | ||
51 | Requires working KVM on build host. See "kvm-ok" output. | ||
52 | """ | ||
53 | |||
54 | runCmd("kvm-ok") | ||
55 | |||
56 | image = "core-image-minimal" | ||
57 | vars = get_bb_vars(['HOST_ARCH', 'BUILD_ARCH'], image) | ||
58 | host_arch = vars['HOST_ARCH'] | ||
59 | build_arch = vars['BUILD_ARCH'] | ||
60 | |||
61 | self.assertEqual(host_arch, build_arch, 'HOST_ARCH %s and BUILD_ARCH %s must match for KVM' % (host_arch, build_arch)) | ||
62 | |||
63 | self.write_config(""" | ||
64 | QEMU_USE_KVM = "1" | ||
65 | |||
66 | # Using u-boot in EFI mode, need ESP partition for grub/systemd-boot/kernel etc | ||
67 | IMAGE_FSTYPES:pn-core-image-minimal:append = " wic" | ||
68 | |||
69 | # easiest to follow genericarm64 setup with wks file, initrd and EFI loader | ||
70 | INITRAMFS_IMAGE = "core-image-initramfs-boot" | ||
71 | EFI_PROVIDER = "${@bb.utils.contains("DISTRO_FEATURES", "systemd", "systemd-boot", "grub-efi", d)}" | ||
72 | WKS_FILE = "genericarm64.wks.in" | ||
73 | |||
74 | # use wic image with ESP for u-boot, not ext4 | ||
75 | QB_DEFAULT_FSTYPE = "wic" | ||
76 | |||
77 | PREFERRED_PROVIDER_virtual/bootloader = "u-boot" | ||
78 | QB_DEFAULT_BIOS = "u-boot.bin" | ||
79 | |||
80 | # let u-boot or EFI loader load kernel from ESP | ||
81 | QB_DEFAULT_KERNEL = "none" | ||
82 | |||
83 | # virt pci, not scsi because support not in u-boot to find ESP | ||
84 | QB_DRIVE_TYPE = "/dev/vd" | ||
85 | """) | ||
86 | bitbake("virtual/bootloader %s" % image) | ||
87 | |||
88 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', image) or "" | ||
89 | with runqemu(image, ssh=False, runqemuparams='nographic kvm %s' % runqemu_params) as qemu: | ||
90 | |||
91 | # boot to target and login worked, should have been fast with kvm | ||
92 | cmd = "dmesg" | ||
93 | status, output = qemu.run_serial(cmd) | ||
94 | self.assertEqual(status, 1, msg=output) | ||
95 | # Machine is qemu | ||
96 | self.assertTrue("Machine model: linux,dummy-virt" in output, msg=output) | ||
97 | # with KVM enabled | ||
98 | self.assertTrue("KVM: hypervisor services detected" in output, msg=output) | ||
diff --git a/meta/lib/oeqa/selftest/cases/uki.py b/meta/lib/oeqa/selftest/cases/uki.py new file mode 100644 index 0000000000..9a1aa4e269 --- /dev/null +++ b/meta/lib/oeqa/selftest/cases/uki.py | |||
@@ -0,0 +1,141 @@ | |||
1 | # Based on runqemu.py test file | ||
2 | # | ||
3 | # Copyright (c) 2017 Wind River Systems, Inc. | ||
4 | # | ||
5 | # SPDX-License-Identifier: MIT | ||
6 | # | ||
7 | |||
8 | from oeqa.selftest.case import OESelftestTestCase | ||
9 | from oeqa.utils.commands import bitbake, runqemu, get_bb_var | ||
10 | from oeqa.core.decorator.data import skipIfNotArch | ||
11 | from oeqa.core.decorator import OETestTag | ||
12 | import oe.types | ||
13 | |||
14 | class UkiTest(OESelftestTestCase): | ||
15 | """Boot Unified Kernel Image (UKI) generated with uki.bbclass on UEFI firmware (omvf/edk2)""" | ||
16 | |||
17 | @skipIfNotArch(['i586', 'i686', 'x86_64']) | ||
18 | @OETestTag("runqemu") | ||
19 | def test_uki_boot_systemd(self): | ||
20 | """Build and boot into UEFI firmware (omvf/edk2), systemd-boot, initrd without systemd, rootfs with systemd""" | ||
21 | image = "core-image-minimal" | ||
22 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', image) or "" | ||
23 | cmd = "runqemu %s nographic serial wic ovmf" % (runqemu_params) | ||
24 | if oe.types.qemu_use_kvm(self.td.get('QEMU_USE_KVM', 0), self.td["TARGET_ARCH"]): | ||
25 | cmd += " kvm" | ||
26 | |||
27 | self.write_config(""" | ||
28 | # efi firmware must load systemd-boot, not grub | ||
29 | EFI_PROVIDER = "systemd-boot" | ||
30 | |||
31 | # image format must be wic, needs esp partition for firmware etc | ||
32 | IMAGE_FSTYPES:pn-%s:append = " wic" | ||
33 | WKS_FILE = "efi-uki-bootdisk.wks.in" | ||
34 | |||
35 | # efi, uki and systemd features must be enabled | ||
36 | INIT_MANAGER = "systemd" | ||
37 | MACHINE_FEATURES:append = " efi" | ||
38 | IMAGE_CLASSES:append:pn-core-image-minimal = " uki" | ||
39 | |||
40 | # uki embeds also an initrd | ||
41 | INITRAMFS_IMAGE = "core-image-minimal-initramfs" | ||
42 | |||
43 | # runqemu must not load kernel separately, it's in the uki | ||
44 | QB_KERNEL_ROOT = "" | ||
45 | QB_DEFAULT_KERNEL = "none" | ||
46 | |||
47 | # boot command line provided via uki, not via bootloader | ||
48 | UKI_CMDLINE = "rootwait root=LABEL=root console=${KERNEL_CONSOLE}" | ||
49 | |||
50 | # disable kvm, breaks boot | ||
51 | QEMU_USE_KVM = "" | ||
52 | |||
53 | IMAGE_CLASSES:remove = 'testimage' | ||
54 | """ % (image)) | ||
55 | |||
56 | uki_filename = get_bb_var('UKI_FILENAME', image) | ||
57 | |||
58 | bitbake(image + " ovmf") | ||
59 | with runqemu(image, ssh=False, launch_cmd=cmd) as qemu: | ||
60 | self.assertTrue(qemu.runner.logged, "Failed: %s" % cmd) | ||
61 | |||
62 | # Verify from efivars that firmware was: | ||
63 | # x86_64, qemux86_64, ovmf = edk2 | ||
64 | cmd = "echo $( cat /sys/firmware/efi/efivars/LoaderFirmwareInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ) | grep 'EDK II'" | ||
65 | status, output = qemu.run_serial(cmd) | ||
66 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
67 | |||
68 | # Check that systemd-boot was the loader | ||
69 | cmd = "echo $( cat /sys/firmware/efi/efivars/LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ) | grep systemd-boot" | ||
70 | status, output = qemu.run_serial(cmd) | ||
71 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
72 | |||
73 | # Check that systemd-stub was used | ||
74 | cmd = "echo $( cat /sys/firmware/efi/efivars/StubInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ) | grep systemd-stub" | ||
75 | status, output = qemu.run_serial(cmd) | ||
76 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
77 | |||
78 | # Check that the compiled uki file was booted into | ||
79 | cmd = "echo $( cat /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ) | grep '%s'" % (uki_filename) | ||
80 | status, output = qemu.run_serial(cmd) | ||
81 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
82 | |||
83 | @skipIfNotArch(['i586', 'i686', 'x86_64']) | ||
84 | @OETestTag("runqemu") | ||
85 | def test_uki_sysvinit(self): | ||
86 | """Build and boot into UEFI firmware (omvf/edk2), systemd-boot, initrd with sysvinit, rootfs with sysvinit""" | ||
87 | config = """ | ||
88 | # efi firmware must load systemd-boot, not grub | ||
89 | EFI_PROVIDER = "systemd-boot" | ||
90 | |||
91 | # image format must be wic, needs esp partition for firmware etc | ||
92 | IMAGE_FSTYPES:pn-core-image-base:append = " wic" | ||
93 | WKS_FILE = "efi-uki-bootdisk.wks.in" | ||
94 | |||
95 | # efi, uki and systemd features must be enabled | ||
96 | MACHINE_FEATURES:append = " efi" | ||
97 | IMAGE_CLASSES:append:pn-core-image-base = " uki" | ||
98 | |||
99 | # uki embeds also an initrd, no systemd or udev | ||
100 | INITRAMFS_IMAGE = "core-image-initramfs-boot" | ||
101 | |||
102 | # runqemu must not load kernel separately, it's in the uki | ||
103 | QB_KERNEL_ROOT = "" | ||
104 | QB_DEFAULT_KERNEL = "none" | ||
105 | |||
106 | # boot command line provided via uki, not via bootloader | ||
107 | UKI_CMDLINE = "rootwait root=LABEL=root console=${KERNEL_CONSOLE}" | ||
108 | |||
109 | # disable kvm, breaks boot | ||
110 | QEMU_USE_KVM = "" | ||
111 | |||
112 | IMAGE_CLASSES:remove = 'testimage' | ||
113 | """ | ||
114 | self.append_config(config) | ||
115 | bitbake('core-image-base ovmf') | ||
116 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or "" | ||
117 | uki_filename = get_bb_var('UKI_FILENAME', 'core-image-base') | ||
118 | self.remove_config(config) | ||
119 | |||
120 | with runqemu('core-image-base', ssh=False, | ||
121 | runqemuparams='%s slirp nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu: | ||
122 | # Verify from efivars that firmware was: | ||
123 | # x86_64, qemux86_64, ovmf = edk2 | ||
124 | cmd = "echo $( cat /sys/firmware/efi/efivars/LoaderFirmwareInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ) | grep 'EDK II'" | ||
125 | status, output = qemu.run_serial(cmd) | ||
126 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
127 | |||
128 | # Check that systemd-boot was the loader | ||
129 | cmd = "echo $( cat /sys/firmware/efi/efivars/LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ) | grep systemd-boot" | ||
130 | status, output = qemu.run_serial(cmd) | ||
131 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
132 | |||
133 | # Check that systemd-stub was used | ||
134 | cmd = "echo $( cat /sys/firmware/efi/efivars/StubInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ) | grep systemd-stub" | ||
135 | status, output = qemu.run_serial(cmd) | ||
136 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
137 | |||
138 | # Check that the compiled uki file was booted into | ||
139 | cmd = "echo $( cat /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f ) | grep '%s'" % (uki_filename) | ||
140 | status, output = qemu.run_serial(cmd) | ||
141 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
diff --git a/meta/lib/oeqa/selftest/cases/wic.py b/meta/lib/oeqa/selftest/cases/wic.py index b616759209..680f99d381 100644 --- a/meta/lib/oeqa/selftest/cases/wic.py +++ b/meta/lib/oeqa/selftest/cases/wic.py | |||
@@ -12,6 +12,7 @@ import os | |||
12 | import sys | 12 | import sys |
13 | import unittest | 13 | import unittest |
14 | import hashlib | 14 | import hashlib |
15 | import subprocess | ||
15 | 16 | ||
16 | from glob import glob | 17 | from glob import glob |
17 | from shutil import rmtree, copy | 18 | from shutil import rmtree, copy |
@@ -152,7 +153,7 @@ class Wic(WicTestCase): | |||
152 | # create a temporary file for the WKS content | 153 | # create a temporary file for the WKS content |
153 | with NamedTemporaryFile("w", suffix=".wks") as wks: | 154 | with NamedTemporaryFile("w", suffix=".wks") as wks: |
154 | wks.write( | 155 | wks.write( |
155 | 'part --source bootimg-efi ' | 156 | 'part --source bootimg_efi ' |
156 | '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=false" ' | 157 | '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=false" ' |
157 | '--label boot --active\n' | 158 | '--label boot --active\n' |
158 | ) | 159 | ) |
@@ -185,7 +186,7 @@ class Wic(WicTestCase): | |||
185 | # create a temporary file for the WKS content | 186 | # create a temporary file for the WKS content |
186 | with NamedTemporaryFile("w", suffix=".wks") as wks: | 187 | with NamedTemporaryFile("w", suffix=".wks") as wks: |
187 | wks.write( | 188 | wks.write( |
188 | 'part --source bootimg-efi ' | 189 | 'part --source bootimg_efi ' |
189 | '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=true" ' | 190 | '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=true" ' |
190 | '--label boot --active\n' | 191 | '--label boot --active\n' |
191 | ) | 192 | ) |
@@ -445,8 +446,9 @@ class Wic(WicTestCase): | |||
445 | wks.write(""" | 446 | wks.write(""" |
446 | part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr | 447 | part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr |
447 | part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr | 448 | part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr |
448 | part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr""" | 449 | part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr |
449 | % (rootfs_dir, rootfs_dir)) | 450 | part /mnt --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/whoami --rootfs-dir %s/usr""" |
451 | % (rootfs_dir, rootfs_dir, rootfs_dir)) | ||
450 | runCmd("wic create %s -e core-image-minimal -o %s" \ | 452 | runCmd("wic create %s -e core-image-minimal -o %s" \ |
451 | % (wks_file, self.resultdir)) | 453 | % (wks_file, self.resultdir)) |
452 | 454 | ||
@@ -457,7 +459,7 @@ part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --r | |||
457 | wicimg = wicout[0] | 459 | wicimg = wicout[0] |
458 | 460 | ||
459 | # verify partition size with wic | 461 | # verify partition size with wic |
460 | res = runCmd("parted -m %s unit b p 2>/dev/null" % wicimg) | 462 | res = runCmd("parted -m %s unit b p" % wicimg, stderr=subprocess.PIPE) |
461 | 463 | ||
462 | # parse parted output which looks like this: | 464 | # parse parted output which looks like this: |
463 | # BYT;\n | 465 | # BYT;\n |
@@ -465,9 +467,9 @@ part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --r | |||
465 | # 1:0.00MiB:200MiB:200MiB:ext4::;\n | 467 | # 1:0.00MiB:200MiB:200MiB:ext4::;\n |
466 | partlns = res.output.splitlines()[2:] | 468 | partlns = res.output.splitlines()[2:] |
467 | 469 | ||
468 | self.assertEqual(3, len(partlns)) | 470 | self.assertEqual(4, len(partlns)) |
469 | 471 | ||
470 | for part in [1, 2, 3]: | 472 | for part in [1, 2, 3, 4]: |
471 | part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part) | 473 | part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part) |
472 | partln = partlns[part-1].split(":") | 474 | partln = partlns[part-1].split(":") |
473 | self.assertEqual(7, len(partln)) | 475 | self.assertEqual(7, len(partln)) |
@@ -478,16 +480,16 @@ part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --r | |||
478 | 480 | ||
479 | # Test partition 1, should contain the normal root directories, except | 481 | # Test partition 1, should contain the normal root directories, except |
480 | # /usr. | 482 | # /usr. |
481 | res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \ | 483 | res = runCmd("debugfs -R 'ls -p' %s" % \ |
482 | os.path.join(self.resultdir, "selftest_img.part1")) | 484 | os.path.join(self.resultdir, "selftest_img.part1"), stderr=subprocess.PIPE) |
483 | files = extract_files(res.output) | 485 | files = extract_files(res.output) |
484 | self.assertIn("etc", files) | 486 | self.assertIn("etc", files) |
485 | self.assertNotIn("usr", files) | 487 | self.assertNotIn("usr", files) |
486 | 488 | ||
487 | # Partition 2, should contain common directories for /usr, not root | 489 | # Partition 2, should contain common directories for /usr, not root |
488 | # directories. | 490 | # directories. |
489 | res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \ | 491 | res = runCmd("debugfs -R 'ls -p' %s" % \ |
490 | os.path.join(self.resultdir, "selftest_img.part2")) | 492 | os.path.join(self.resultdir, "selftest_img.part2"), stderr=subprocess.PIPE) |
491 | files = extract_files(res.output) | 493 | files = extract_files(res.output) |
492 | self.assertNotIn("etc", files) | 494 | self.assertNotIn("etc", files) |
493 | self.assertNotIn("usr", files) | 495 | self.assertNotIn("usr", files) |
@@ -495,27 +497,78 @@ part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --r | |||
495 | 497 | ||
496 | # Partition 3, should contain the same as partition 2, including the bin | 498 | # Partition 3, should contain the same as partition 2, including the bin |
497 | # directory, but not the files inside it. | 499 | # directory, but not the files inside it. |
498 | res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % \ | 500 | res = runCmd("debugfs -R 'ls -p' %s" % \ |
499 | os.path.join(self.resultdir, "selftest_img.part3")) | 501 | os.path.join(self.resultdir, "selftest_img.part3"), stderr=subprocess.PIPE) |
500 | files = extract_files(res.output) | 502 | files = extract_files(res.output) |
501 | self.assertNotIn("etc", files) | 503 | self.assertNotIn("etc", files) |
502 | self.assertNotIn("usr", files) | 504 | self.assertNotIn("usr", files) |
503 | self.assertIn("share", files) | 505 | self.assertIn("share", files) |
504 | self.assertIn("bin", files) | 506 | self.assertIn("bin", files) |
505 | res = runCmd("debugfs -R 'ls -p bin' %s 2>/dev/null" % \ | 507 | res = runCmd("debugfs -R 'ls -p bin' %s" % \ |
506 | os.path.join(self.resultdir, "selftest_img.part3")) | 508 | os.path.join(self.resultdir, "selftest_img.part3"), stderr=subprocess.PIPE) |
507 | files = extract_files(res.output) | 509 | files = extract_files(res.output) |
508 | self.assertIn(".", files) | 510 | self.assertIn(".", files) |
509 | self.assertIn("..", files) | 511 | self.assertIn("..", files) |
510 | self.assertEqual(2, len(files)) | 512 | self.assertEqual(2, len(files)) |
511 | 513 | ||
512 | for part in [1, 2, 3]: | 514 | # Partition 4, should contain the same as partition 2, including the bin |
515 | # directory, but not whoami (a symlink to busybox.nosuid) inside it. | ||
516 | res = runCmd("debugfs -R 'ls -p' %s" % \ | ||
517 | os.path.join(self.resultdir, "selftest_img.part4"), stderr=subprocess.PIPE) | ||
518 | files = extract_files(res.output) | ||
519 | self.assertNotIn("etc", files) | ||
520 | self.assertNotIn("usr", files) | ||
521 | self.assertIn("share", files) | ||
522 | self.assertIn("bin", files) | ||
523 | res = runCmd("debugfs -R 'ls -p bin' %s" % \ | ||
524 | os.path.join(self.resultdir, "selftest_img.part4"), stderr=subprocess.PIPE) | ||
525 | files = extract_files(res.output) | ||
526 | self.assertIn(".", files) | ||
527 | self.assertIn("..", files) | ||
528 | self.assertIn("who", files) | ||
529 | self.assertNotIn("whoami", files) | ||
530 | |||
531 | for part in [1, 2, 3, 4]: | ||
513 | part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part) | 532 | part_file = os.path.join(self.resultdir, "selftest_img.part%d" % part) |
514 | os.remove(part_file) | 533 | os.remove(part_file) |
515 | 534 | ||
516 | finally: | 535 | finally: |
517 | os.environ['PATH'] = oldpath | 536 | os.environ['PATH'] = oldpath |
518 | 537 | ||
538 | def test_exclude_path_with_extra_space(self): | ||
539 | """Test having --exclude-path with IMAGE_ROOTFS_EXTRA_SPACE. [Yocto #15555]""" | ||
540 | |||
541 | with NamedTemporaryFile("w", suffix=".wks") as wks: | ||
542 | wks.writelines( | ||
543 | ['bootloader --ptable gpt\n', | ||
544 | 'part /boot --size=100M --active --fstype=ext4 --label boot\n', | ||
545 | 'part / --source rootfs --fstype=ext4 --label root --exclude-path boot/\n']) | ||
546 | wks.flush() | ||
547 | config = 'IMAGE_ROOTFS_EXTRA_SPACE = "500000"\n'\ | ||
548 | 'DEPENDS:pn-core-image-minimal += "wic-tools"\n'\ | ||
549 | 'IMAGE_FSTYPES += "wic ext4"\n'\ | ||
550 | 'WKS_FILE = "%s"\n' % wks.name | ||
551 | self.append_config(config) | ||
552 | bitbake('core-image-minimal') | ||
553 | |||
554 | """ | ||
555 | the output of "wic ls <image>.wic" will look something like: | ||
556 | Num Start End Size Fstype | ||
557 | 1 17408 136332287 136314880 ext4 | ||
558 | 2 136332288 171464703 35132416 ext4 | ||
559 | we are looking for the size of partition 2 | ||
560 | i.e. in this case the number 35,132,416 | ||
561 | without the fix the size will be around 85,403,648 | ||
562 | with the fix the size should be around 799,960,064 | ||
563 | """ | ||
564 | bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'MACHINE'], 'core-image-minimal') | ||
565 | deploy_dir = bb_vars['DEPLOY_DIR_IMAGE'] | ||
566 | machine = bb_vars['MACHINE'] | ||
567 | nativesysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') | ||
568 | wicout = glob(os.path.join(deploy_dir, "core-image-minimal-%s.rootfs-*.wic" % machine))[0] | ||
569 | size_of_root_partition = int(runCmd("wic ls %s --native-sysroot %s" % (wicout, nativesysroot)).output.split('\n')[2].split()[3]) | ||
570 | self.assertGreater(size_of_root_partition, 500000000) | ||
571 | |||
519 | def test_include_path(self): | 572 | def test_include_path(self): |
520 | """Test --include-path wks option.""" | 573 | """Test --include-path wks option.""" |
521 | 574 | ||
@@ -541,13 +594,13 @@ part /part2 --source rootfs --ondisk mmcblk0 --fstype=ext4 --include-path %s""" | |||
541 | part2 = glob(os.path.join(self.resultdir, 'temp-*.direct.p2'))[0] | 594 | part2 = glob(os.path.join(self.resultdir, 'temp-*.direct.p2'))[0] |
542 | 595 | ||
543 | # Test partition 1, should not contain 'test-file' | 596 | # Test partition 1, should not contain 'test-file' |
544 | res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part1)) | 597 | res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE) |
545 | files = extract_files(res.output) | 598 | files = extract_files(res.output) |
546 | self.assertNotIn('test-file', files) | 599 | self.assertNotIn('test-file', files) |
547 | self.assertEqual(True, files_own_by_root(res.output)) | 600 | self.assertEqual(True, files_own_by_root(res.output)) |
548 | 601 | ||
549 | # Test partition 2, should contain 'test-file' | 602 | # Test partition 2, should contain 'test-file' |
550 | res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part2)) | 603 | res = runCmd("debugfs -R 'ls -p' %s" % (part2), stderr=subprocess.PIPE) |
551 | files = extract_files(res.output) | 604 | files = extract_files(res.output) |
552 | self.assertIn('test-file', files) | 605 | self.assertIn('test-file', files) |
553 | self.assertEqual(True, files_own_by_root(res.output)) | 606 | self.assertEqual(True, files_own_by_root(res.output)) |
@@ -576,12 +629,12 @@ part / --source rootfs --fstype=ext4 --include-path %s --include-path core-imag | |||
576 | 629 | ||
577 | part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0] | 630 | part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0] |
578 | 631 | ||
579 | res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part1)) | 632 | res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE) |
580 | files = extract_files(res.output) | 633 | files = extract_files(res.output) |
581 | self.assertIn('test-file', files) | 634 | self.assertIn('test-file', files) |
582 | self.assertEqual(True, files_own_by_root(res.output)) | 635 | self.assertEqual(True, files_own_by_root(res.output)) |
583 | 636 | ||
584 | res = runCmd("debugfs -R 'ls -p /export/etc/' %s 2>/dev/null" % (part1)) | 637 | res = runCmd("debugfs -R 'ls -p /export/etc/' %s" % (part1), stderr=subprocess.PIPE) |
585 | files = extract_files(res.output) | 638 | files = extract_files(res.output) |
586 | self.assertIn('passwd', files) | 639 | self.assertIn('passwd', files) |
587 | self.assertEqual(True, files_own_by_root(res.output)) | 640 | self.assertEqual(True, files_own_by_root(res.output)) |
@@ -668,7 +721,7 @@ part /etc --source rootfs --fstype=ext4 --change-directory=etc | |||
668 | % (wks_file, self.resultdir)) | 721 | % (wks_file, self.resultdir)) |
669 | 722 | ||
670 | for part in glob(os.path.join(self.resultdir, 'temp-*.direct.p*')): | 723 | for part in glob(os.path.join(self.resultdir, 'temp-*.direct.p*')): |
671 | res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part)) | 724 | res = runCmd("debugfs -R 'ls -p' %s" % (part), stderr=subprocess.PIPE) |
672 | self.assertEqual(True, files_own_by_root(res.output)) | 725 | self.assertEqual(True, files_own_by_root(res.output)) |
673 | 726 | ||
674 | config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "%s"\n' % wks_file | 727 | config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "%s"\n' % wks_file |
@@ -678,7 +731,7 @@ part /etc --source rootfs --fstype=ext4 --change-directory=etc | |||
678 | 731 | ||
679 | # check each partition for permission | 732 | # check each partition for permission |
680 | for part in glob(os.path.join(tmpdir, 'temp-*.direct.p*')): | 733 | for part in glob(os.path.join(tmpdir, 'temp-*.direct.p*')): |
681 | res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part)) | 734 | res = runCmd("debugfs -R 'ls -p' %s" % (part), stderr=subprocess.PIPE) |
682 | self.assertTrue(files_own_by_root(res.output) | 735 | self.assertTrue(files_own_by_root(res.output) |
683 | ,msg='Files permission incorrect using wks set "%s"' % test) | 736 | ,msg='Files permission incorrect using wks set "%s"' % test) |
684 | 737 | ||
@@ -706,7 +759,7 @@ part /etc --source rootfs --fstype=ext4 --change-directory=etc | |||
706 | 759 | ||
707 | part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0] | 760 | part1 = glob(os.path.join(self.resultdir, 'temp-*.direct.p1'))[0] |
708 | 761 | ||
709 | res = runCmd("debugfs -R 'ls -p' %s 2>/dev/null" % (part1)) | 762 | res = runCmd("debugfs -R 'ls -p' %s" % (part1), stderr=subprocess.PIPE) |
710 | files = extract_files(res.output) | 763 | files = extract_files(res.output) |
711 | self.assertIn('passwd', files) | 764 | self.assertIn('passwd', files) |
712 | 765 | ||
@@ -741,7 +794,7 @@ part /etc --source rootfs --fstype=ext4 --change-directory=etc | |||
741 | bitbake('base-files -c do_install') | 794 | bitbake('base-files -c do_install') |
742 | bf_fstab = os.path.join(get_bb_var('D', 'base-files'), 'etc', 'fstab') | 795 | bf_fstab = os.path.join(get_bb_var('D', 'base-files'), 'etc', 'fstab') |
743 | self.assertEqual(True, os.path.exists(bf_fstab)) | 796 | self.assertEqual(True, os.path.exists(bf_fstab)) |
744 | bf_fstab_md5sum = runCmd('md5sum %s 2>/dev/null' % bf_fstab).output.split(" ")[0] | 797 | bf_fstab_md5sum = runCmd('md5sum %s ' % bf_fstab).output.split(" ")[0] |
745 | 798 | ||
746 | try: | 799 | try: |
747 | no_fstab_update_path = os.path.join(self.resultdir, 'test-no-fstab-update') | 800 | no_fstab_update_path = os.path.join(self.resultdir, 'test-no-fstab-update') |
@@ -757,7 +810,7 @@ part /etc --source rootfs --fstype=ext4 --change-directory=etc | |||
757 | part_fstab_md5sum = [] | 810 | part_fstab_md5sum = [] |
758 | for i in range(1, 3): | 811 | for i in range(1, 3): |
759 | part = glob(os.path.join(self.resultdir, 'temp-*.direct.p') + str(i))[0] | 812 | part = glob(os.path.join(self.resultdir, 'temp-*.direct.p') + str(i))[0] |
760 | part_fstab = runCmd("debugfs -R 'cat etc/fstab' %s 2>/dev/null" % (part)) | 813 | part_fstab = runCmd("debugfs -R 'cat etc/fstab' %s" % (part), stderr=subprocess.PIPE) |
761 | part_fstab_md5sum.append(hashlib.md5((part_fstab.output + "\n\n").encode('utf-8')).hexdigest()) | 814 | part_fstab_md5sum.append(hashlib.md5((part_fstab.output + "\n\n").encode('utf-8')).hexdigest()) |
762 | 815 | ||
763 | # '/etc/fstab' in partition 2 should contain the same stock fstab file | 816 | # '/etc/fstab' in partition 2 should contain the same stock fstab file |
@@ -839,6 +892,61 @@ bootloader --ptable gpt""") | |||
839 | finally: | 892 | finally: |
840 | os.remove(wks_file) | 893 | os.remove(wks_file) |
841 | 894 | ||
895 | def test_wic_sector_size(self): | ||
896 | """Test generation image sector size""" | ||
897 | |||
898 | oldpath = os.environ['PATH'] | ||
899 | os.environ['PATH'] = get_bb_var("PATH", "wic-tools") | ||
900 | |||
901 | try: | ||
902 | # Add WIC_SECTOR_SIZE into config | ||
903 | config = 'WIC_SECTOR_SIZE = "4096"\n'\ | ||
904 | 'WICVARS:append = " WIC_SECTOR_SIZE"\n' | ||
905 | self.append_config(config) | ||
906 | bitbake('core-image-minimal') | ||
907 | |||
908 | # Check WIC_SECTOR_SIZE apply to bitbake variable | ||
909 | wic_sector_size_str = get_bb_var('WIC_SECTOR_SIZE', 'core-image-minimal') | ||
910 | wic_sector_size = int(wic_sector_size_str) | ||
911 | self.assertEqual(4096, wic_sector_size) | ||
912 | |||
913 | self.logger.info("Test wic_sector_size: %d \n" % wic_sector_size) | ||
914 | |||
915 | with NamedTemporaryFile("w", suffix=".wks") as wks: | ||
916 | wks.writelines( | ||
917 | ['bootloader --ptable gpt\n', | ||
918 | 'part --fstype ext4 --source rootfs --label rofs-a --mkfs-extraopts "-b 4096"\n', | ||
919 | 'part --fstype ext4 --source rootfs --use-uuid --mkfs-extraopts "-b 4096"\n']) | ||
920 | wks.flush() | ||
921 | cmd = "wic create %s -e core-image-minimal -o %s" % (wks.name, self.resultdir) | ||
922 | runCmd(cmd) | ||
923 | wksname = os.path.splitext(os.path.basename(wks.name))[0] | ||
924 | images = glob(os.path.join(self.resultdir, "%s-*direct" % wksname)) | ||
925 | self.assertEqual(1, len(images)) | ||
926 | |||
927 | sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') | ||
928 | # list partitions | ||
929 | result = runCmd("wic ls %s -n %s" % (images[0], sysroot)) | ||
930 | self.assertEqual(3, len(result.output.split('\n'))) | ||
931 | |||
932 | # verify partition size with wic | ||
933 | res = runCmd("export PARTED_SECTOR_SIZE=%d; parted -m %s unit b p" % (wic_sector_size, images[0]), | ||
934 | stderr=subprocess.PIPE) | ||
935 | |||
936 | # parse parted output which looks like this: | ||
937 | # BYT;\n | ||
938 | # /var/tmp/wic/build/tmpgjzzefdd-202410281021-sda.direct:78569472B:file:4096:4096:gpt::;\n | ||
939 | # 1:139264B:39284735B:39145472B:ext4:rofs-a:;\n | ||
940 | # 2:39284736B:78430207B:39145472B:ext4:primary:;\n | ||
941 | disk_info = res.output.splitlines()[1] | ||
942 | # Check sector sizes | ||
943 | sector_size_logical = int(disk_info.split(":")[3]) | ||
944 | sector_size_physical = int(disk_info.split(":")[4]) | ||
945 | self.assertEqual(wic_sector_size, sector_size_logical, "Logical sector size is not %d." % wic_sector_size) | ||
946 | self.assertEqual(wic_sector_size, sector_size_physical, "Physical sector size is not %d." % wic_sector_size) | ||
947 | |||
948 | finally: | ||
949 | os.environ['PATH'] = oldpath | ||
842 | 950 | ||
843 | class Wic2(WicTestCase): | 951 | class Wic2(WicTestCase): |
844 | 952 | ||
@@ -913,6 +1021,18 @@ class Wic2(WicTestCase): | |||
913 | """Test building wic images by bitbake""" | 1021 | """Test building wic images by bitbake""" |
914 | config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\ | 1022 | config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\ |
915 | 'MACHINE_FEATURES:append = " efi"\n' | 1023 | 'MACHINE_FEATURES:append = " efi"\n' |
1024 | image_recipe_append = """ | ||
1025 | do_image_wic[postfuncs] += "run_wic_cmd" | ||
1026 | run_wic_cmd() { | ||
1027 | echo "test" >> ${WORKDIR}/test.wic-cp | ||
1028 | wic cp --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${WORKDIR}/test.wic-cp ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/ | ||
1029 | wic ls --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/ | ||
1030 | wic rm --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/test.wic-cp | ||
1031 | wic cp --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${WORKDIR}/test.wic-cp ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/ | ||
1032 | } | ||
1033 | """ | ||
1034 | self.write_recipeinc('images', image_recipe_append) | ||
1035 | |||
916 | self.append_config(config) | 1036 | self.append_config(config) |
917 | image = 'wic-image-minimal' | 1037 | image = 'wic-image-minimal' |
918 | bitbake(image) | 1038 | bitbake(image) |
@@ -921,6 +1041,11 @@ class Wic2(WicTestCase): | |||
921 | bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image) | 1041 | bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], image) |
922 | prefix = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.' % bb_vars['IMAGE_LINK_NAME']) | 1042 | prefix = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], '%s.' % bb_vars['IMAGE_LINK_NAME']) |
923 | 1043 | ||
1044 | sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') | ||
1045 | # check if file is there | ||
1046 | result = runCmd("wic ls %s:1/ -n %s" % (prefix+"wic", sysroot)) | ||
1047 | self.assertIn("test.wic-cp", result.output) | ||
1048 | |||
924 | # check if we have result image and manifests symlinks | 1049 | # check if we have result image and manifests symlinks |
925 | # pointing to existing files | 1050 | # pointing to existing files |
926 | for suffix in ('wic', 'manifest'): | 1051 | for suffix in ('wic', 'manifest'): |
@@ -936,10 +1061,29 @@ class Wic2(WicTestCase): | |||
936 | config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\ | 1061 | config = 'IMAGE_FSTYPES += "wic"\nWKS_FILE = "wic-image-minimal"\n'\ |
937 | 'MACHINE_FEATURES:append = " efi"\n' | 1062 | 'MACHINE_FEATURES:append = " efi"\n' |
938 | self.append_config(config) | 1063 | self.append_config(config) |
1064 | image_recipe_append = """ | ||
1065 | do_image_wic[postfuncs] += "run_wic_cmd" | ||
1066 | run_wic_cmd() { | ||
1067 | echo "test" >> ${WORKDIR}/test.wic-cp | ||
1068 | wic cp --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${WORKDIR}/test.wic-cp ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/ | ||
1069 | wic ls --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/ | ||
1070 | wic rm --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/test.wic-cp | ||
1071 | wic cp --vars "${STAGING_DIR}/${MACHINE}/imgdata/" -e "${IMAGE_BASENAME}" ${WORKDIR}/test.wic-cp ${IMGDEPLOYDIR}/${IMAGE_NAME}.wic:1/ | ||
1072 | } | ||
1073 | """ | ||
1074 | self.write_recipeinc('images', image_recipe_append) | ||
939 | bitbake('wic-image-minimal') | 1075 | bitbake('wic-image-minimal') |
1076 | |||
1077 | sysroot = get_bb_var('RECIPE_SYSROOT_NATIVE', 'wic-tools') | ||
1078 | bb_vars = get_bb_vars(['DEPLOY_DIR_IMAGE', 'IMAGE_LINK_NAME'], "wic-image-minimal") | ||
1079 | image_path = os.path.join(bb_vars['DEPLOY_DIR_IMAGE'], bb_vars['IMAGE_LINK_NAME']) | ||
1080 | # check if file is there | ||
1081 | result = runCmd("wic ls %s:1/ -n %s" % (image_path+".wic", sysroot)) | ||
1082 | self.assertIn("test.wic-cp", result.output) | ||
940 | self.remove_config(config) | 1083 | self.remove_config(config) |
941 | 1084 | ||
942 | with runqemu('wic-image-minimal', ssh=False, runqemuparams='nographic') as qemu: | 1085 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'wic-image-minimal') or "" |
1086 | with runqemu('wic-image-minimal', ssh=False, runqemuparams='%s nographic' % (runqemu_params)) as qemu: | ||
943 | cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' " \ | 1087 | cmd = "mount | grep '^/dev/' | cut -f1,3 -d ' ' | egrep -c -e '/dev/sda1 /boot' " \ |
944 | "-e '/dev/root /|/dev/sda2 /' -e '/dev/sda3 /media' -e '/dev/sda4 /mnt'" | 1088 | "-e '/dev/root /|/dev/sda2 /' -e '/dev/sda3 /media' -e '/dev/sda4 /mnt'" |
945 | status, output = qemu.run_serial(cmd) | 1089 | status, output = qemu.run_serial(cmd) |
@@ -959,8 +1103,9 @@ class Wic2(WicTestCase): | |||
959 | bitbake('core-image-minimal ovmf') | 1103 | bitbake('core-image-minimal ovmf') |
960 | self.remove_config(config) | 1104 | self.remove_config(config) |
961 | 1105 | ||
1106 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or "" | ||
962 | with runqemu('core-image-minimal', ssh=False, | 1107 | with runqemu('core-image-minimal', ssh=False, |
963 | runqemuparams='nographic ovmf', image_fstype='wic') as qemu: | 1108 | runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu: |
964 | cmd = "grep sda. /proc/partitions |wc -l" | 1109 | cmd = "grep sda. /proc/partitions |wc -l" |
965 | status, output = qemu.run_serial(cmd) | 1110 | status, output = qemu.run_serial(cmd) |
966 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | 1111 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) |
@@ -1000,8 +1145,8 @@ class Wic2(WicTestCase): | |||
1000 | native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools") | 1145 | native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "wic-tools") |
1001 | 1146 | ||
1002 | # verify partition size with wic | 1147 | # verify partition size with wic |
1003 | res = runCmd("parted -m %s unit kib p 2>/dev/null" % wicimg, | 1148 | res = runCmd("parted -m %s unit kib p" % wicimg, |
1004 | native_sysroot=native_sysroot) | 1149 | native_sysroot=native_sysroot, stderr=subprocess.PIPE) |
1005 | 1150 | ||
1006 | # parse parted output which looks like this: | 1151 | # parse parted output which looks like this: |
1007 | # BYT;\n | 1152 | # BYT;\n |
@@ -1040,71 +1185,71 @@ class Wic2(WicTestCase): | |||
1040 | with NamedTemporaryFile("w", suffix=".wks") as tempf: | 1185 | with NamedTemporaryFile("w", suffix=".wks") as tempf: |
1041 | # Test that partitions are placed at the correct offsets, default KB | 1186 | # Test that partitions are placed at the correct offsets, default KB |
1042 | tempf.write("bootloader --ptable gpt\n" \ | 1187 | tempf.write("bootloader --ptable gpt\n" \ |
1043 | "part / --source rootfs --ondisk hda --offset 32 --fixed-size 100M --fstype=ext4\n" \ | 1188 | "part / --source rootfs --ondisk hda --offset 32 --fixed-size 200M --fstype=ext4\n" \ |
1044 | "part /bar --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n") | 1189 | "part /bar --ondisk hda --offset 204832 --fixed-size 100M --fstype=ext4\n") |
1045 | tempf.flush() | 1190 | tempf.flush() |
1046 | 1191 | ||
1047 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) | 1192 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) |
1048 | self.assertEqual(partlns, [ | 1193 | self.assertEqual(partlns, [ |
1049 | "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;", | 1194 | "1:32.0kiB:204832kiB:204800kiB:ext4:primary:;", |
1050 | "2:102432kiB:204832kiB:102400kiB:ext4:primary:;", | 1195 | "2:204832kiB:307232kiB:102400kiB:ext4:primary:;", |
1051 | ]) | 1196 | ]) |
1052 | 1197 | ||
1053 | with NamedTemporaryFile("w", suffix=".wks") as tempf: | 1198 | with NamedTemporaryFile("w", suffix=".wks") as tempf: |
1054 | # Test that partitions are placed at the correct offsets, same with explicit KB | 1199 | # Test that partitions are placed at the correct offsets, same with explicit KB |
1055 | tempf.write("bootloader --ptable gpt\n" \ | 1200 | tempf.write("bootloader --ptable gpt\n" \ |
1056 | "part / --source rootfs --ondisk hda --offset 32K --fixed-size 100M --fstype=ext4\n" \ | 1201 | "part / --source rootfs --ondisk hda --offset 32K --fixed-size 200M --fstype=ext4\n" \ |
1057 | "part /bar --ondisk hda --offset 102432K --fixed-size 100M --fstype=ext4\n") | 1202 | "part /bar --ondisk hda --offset 204832K --fixed-size 100M --fstype=ext4\n") |
1058 | tempf.flush() | 1203 | tempf.flush() |
1059 | 1204 | ||
1060 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) | 1205 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) |
1061 | self.assertEqual(partlns, [ | 1206 | self.assertEqual(partlns, [ |
1062 | "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;", | 1207 | "1:32.0kiB:204832kiB:204800kiB:ext4:primary:;", |
1063 | "2:102432kiB:204832kiB:102400kiB:ext4:primary:;", | 1208 | "2:204832kiB:307232kiB:102400kiB:ext4:primary:;", |
1064 | ]) | 1209 | ]) |
1065 | 1210 | ||
1066 | with NamedTemporaryFile("w", suffix=".wks") as tempf: | 1211 | with NamedTemporaryFile("w", suffix=".wks") as tempf: |
1067 | # Test that partitions are placed at the correct offsets using MB | 1212 | # Test that partitions are placed at the correct offsets using MB |
1068 | tempf.write("bootloader --ptable gpt\n" \ | 1213 | tempf.write("bootloader --ptable gpt\n" \ |
1069 | "part / --source rootfs --ondisk hda --offset 32K --fixed-size 100M --fstype=ext4\n" \ | 1214 | "part / --source rootfs --ondisk hda --offset 32K --fixed-size 200M --fstype=ext4\n" \ |
1070 | "part /bar --ondisk hda --offset 101M --fixed-size 100M --fstype=ext4\n") | 1215 | "part /bar --ondisk hda --offset 201M --fixed-size 100M --fstype=ext4\n") |
1071 | tempf.flush() | 1216 | tempf.flush() |
1072 | 1217 | ||
1073 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) | 1218 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) |
1074 | self.assertEqual(partlns, [ | 1219 | self.assertEqual(partlns, [ |
1075 | "1:32.0kiB:102432kiB:102400kiB:ext4:primary:;", | 1220 | "1:32.0kiB:204832kiB:204800kiB:ext4:primary:;", |
1076 | "2:103424kiB:205824kiB:102400kiB:ext4:primary:;", | 1221 | "2:205824kiB:308224kiB:102400kiB:ext4:primary:;", |
1077 | ]) | 1222 | ]) |
1078 | 1223 | ||
1079 | with NamedTemporaryFile("w", suffix=".wks") as tempf: | 1224 | with NamedTemporaryFile("w", suffix=".wks") as tempf: |
1080 | # Test that partitions can be placed on a 512 byte sector boundary | 1225 | # Test that partitions can be placed on a 512 byte sector boundary |
1081 | tempf.write("bootloader --ptable gpt\n" \ | 1226 | tempf.write("bootloader --ptable gpt\n" \ |
1082 | "part / --source rootfs --ondisk hda --offset 65s --fixed-size 99M --fstype=ext4\n" \ | 1227 | "part / --source rootfs --ondisk hda --offset 65s --fixed-size 199M --fstype=ext4\n" \ |
1083 | "part /bar --ondisk hda --offset 102432 --fixed-size 100M --fstype=ext4\n") | 1228 | "part /bar --ondisk hda --offset 204832 --fixed-size 100M --fstype=ext4\n") |
1084 | tempf.flush() | 1229 | tempf.flush() |
1085 | 1230 | ||
1086 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) | 1231 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) |
1087 | self.assertEqual(partlns, [ | 1232 | self.assertEqual(partlns, [ |
1088 | "1:32.5kiB:101408kiB:101376kiB:ext4:primary:;", | 1233 | "1:32.5kiB:203808kiB:203776kiB:ext4:primary:;", |
1089 | "2:102432kiB:204832kiB:102400kiB:ext4:primary:;", | 1234 | "2:204832kiB:307232kiB:102400kiB:ext4:primary:;", |
1090 | ]) | 1235 | ]) |
1091 | 1236 | ||
1092 | with NamedTemporaryFile("w", suffix=".wks") as tempf: | 1237 | with NamedTemporaryFile("w", suffix=".wks") as tempf: |
1093 | # Test that a partition can be placed immediately after a MSDOS partition table | 1238 | # Test that a partition can be placed immediately after a MSDOS partition table |
1094 | tempf.write("bootloader --ptable msdos\n" \ | 1239 | tempf.write("bootloader --ptable msdos\n" \ |
1095 | "part / --source rootfs --ondisk hda --offset 1s --fixed-size 100M --fstype=ext4\n") | 1240 | "part / --source rootfs --ondisk hda --offset 1s --fixed-size 200M --fstype=ext4\n") |
1096 | tempf.flush() | 1241 | tempf.flush() |
1097 | 1242 | ||
1098 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) | 1243 | _, partlns = self._get_wic_partitions(tempf.name, native_sysroot) |
1099 | self.assertEqual(partlns, [ | 1244 | self.assertEqual(partlns, [ |
1100 | "1:0.50kiB:102400kiB:102400kiB:ext4::;", | 1245 | "1:0.50kiB:204800kiB:204800kiB:ext4::;", |
1101 | ]) | 1246 | ]) |
1102 | 1247 | ||
1103 | with NamedTemporaryFile("w", suffix=".wks") as tempf: | 1248 | with NamedTemporaryFile("w", suffix=".wks") as tempf: |
1104 | # Test that image creation fails if the partitions would overlap | 1249 | # Test that image creation fails if the partitions would overlap |
1105 | tempf.write("bootloader --ptable gpt\n" \ | 1250 | tempf.write("bootloader --ptable gpt\n" \ |
1106 | "part / --source rootfs --ondisk hda --offset 32 --fixed-size 100M --fstype=ext4\n" \ | 1251 | "part / --source rootfs --ondisk hda --offset 32 --fixed-size 200M --fstype=ext4\n" \ |
1107 | "part /bar --ondisk hda --offset 102431 --fixed-size 100M --fstype=ext4\n") | 1252 | "part /bar --ondisk hda --offset 204831 --fixed-size 100M --fstype=ext4\n") |
1108 | tempf.flush() | 1253 | tempf.flush() |
1109 | 1254 | ||
1110 | p, _ = self._get_wic_partitions(tempf.name, ignore_status=True) | 1255 | p, _ = self._get_wic_partitions(tempf.name, ignore_status=True) |
@@ -1113,7 +1258,7 @@ class Wic2(WicTestCase): | |||
1113 | with NamedTemporaryFile("w", suffix=".wks") as tempf: | 1258 | with NamedTemporaryFile("w", suffix=".wks") as tempf: |
1114 | # Test that partitions are not allowed to overlap with the booloader | 1259 | # Test that partitions are not allowed to overlap with the booloader |
1115 | tempf.write("bootloader --ptable gpt\n" \ | 1260 | tempf.write("bootloader --ptable gpt\n" \ |
1116 | "part / --source rootfs --ondisk hda --offset 8 --fixed-size 100M --fstype=ext4\n") | 1261 | "part / --source rootfs --ondisk hda --offset 8 --fixed-size 200M --fstype=ext4\n") |
1117 | tempf.flush() | 1262 | tempf.flush() |
1118 | 1263 | ||
1119 | p, _ = self._get_wic_partitions(tempf.name, ignore_status=True) | 1264 | p, _ = self._get_wic_partitions(tempf.name, ignore_status=True) |
@@ -1154,8 +1299,9 @@ class Wic2(WicTestCase): | |||
1154 | bitbake('core-image-minimal-mtdutils') | 1299 | bitbake('core-image-minimal-mtdutils') |
1155 | self.remove_config(config) | 1300 | self.remove_config(config) |
1156 | 1301 | ||
1302 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal-mtdutils') or "" | ||
1157 | with runqemu('core-image-minimal-mtdutils', ssh=False, | 1303 | with runqemu('core-image-minimal-mtdutils', ssh=False, |
1158 | runqemuparams='nographic', image_fstype='wic') as qemu: | 1304 | runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu: |
1159 | cmd = "grep sda. /proc/partitions |wc -l" | 1305 | cmd = "grep sda. /proc/partitions |wc -l" |
1160 | status, output = qemu.run_serial(cmd) | 1306 | status, output = qemu.run_serial(cmd) |
1161 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | 1307 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) |
@@ -1177,6 +1323,10 @@ class Wic2(WicTestCase): | |||
1177 | self.assertEqual(1, len(out)) | 1323 | self.assertEqual(1, len(out)) |
1178 | 1324 | ||
1179 | def test_rawcopy_plugin(self): | 1325 | def test_rawcopy_plugin(self): |
1326 | config = 'IMAGE_FSTYPES = "ext4"\n' | ||
1327 | self.append_config(config) | ||
1328 | self.assertEqual(0, bitbake('core-image-minimal').status) | ||
1329 | self.remove_config(config) | ||
1180 | self._rawcopy_plugin('ext4') | 1330 | self._rawcopy_plugin('ext4') |
1181 | 1331 | ||
1182 | def test_rawcopy_plugin_unpack(self): | 1332 | def test_rawcopy_plugin_unpack(self): |
@@ -1214,8 +1364,9 @@ class Wic2(WicTestCase): | |||
1214 | bitbake('core-image-minimal') | 1364 | bitbake('core-image-minimal') |
1215 | self.remove_config(config) | 1365 | self.remove_config(config) |
1216 | 1366 | ||
1367 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or "" | ||
1217 | with runqemu('core-image-minimal', ssh=False, | 1368 | with runqemu('core-image-minimal', ssh=False, |
1218 | runqemuparams='nographic', image_fstype='wic') as qemu: | 1369 | runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu: |
1219 | # Check that we have ONLY two /dev/sda* partitions (/boot and /) | 1370 | # Check that we have ONLY two /dev/sda* partitions (/boot and /) |
1220 | cmd = "grep sda. /proc/partitions | wc -l" | 1371 | cmd = "grep sda. /proc/partitions | wc -l" |
1221 | status, output = qemu.run_serial(cmd) | 1372 | status, output = qemu.run_serial(cmd) |
@@ -1242,7 +1393,7 @@ class Wic2(WicTestCase): | |||
1242 | def test_biosplusefi_plugin(self): | 1393 | def test_biosplusefi_plugin(self): |
1243 | """Test biosplusefi plugin""" | 1394 | """Test biosplusefi plugin""" |
1244 | # Wic generation below may fail depending on the order of the unittests | 1395 | # Wic generation below may fail depending on the order of the unittests |
1245 | # This is because bootimg-pcbios (that bootimg-biosplusefi uses) generate its MBR inside STAGING_DATADIR directory | 1396 | # This is because bootimg_pcbios (that bootimg_biosplusefi uses) generate its MBR inside STAGING_DATADIR directory |
1246 | # which may or may not exists depending on what was built already | 1397 | # which may or may not exists depending on what was built already |
1247 | # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir() | 1398 | # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir() |
1248 | # will raise with "Couldn't find correct bootimg_dir" | 1399 | # will raise with "Couldn't find correct bootimg_dir" |
@@ -1254,7 +1405,7 @@ class Wic2(WicTestCase): | |||
1254 | 1405 | ||
1255 | img = 'core-image-minimal' | 1406 | img = 'core-image-minimal' |
1256 | with NamedTemporaryFile("w", suffix=".wks") as wks: | 1407 | with NamedTemporaryFile("w", suffix=".wks") as wks: |
1257 | wks.writelines(['part /boot --active --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\n', | 1408 | wks.writelines(['part /boot --active --source bootimg_biosplusefi --sourceparams="loader=grub-efi"\n', |
1258 | 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\ | 1409 | 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\ |
1259 | 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n']) | 1410 | 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n']) |
1260 | wks.flush() | 1411 | wks.flush() |
@@ -1274,7 +1425,7 @@ class Wic2(WicTestCase): | |||
1274 | 1425 | ||
1275 | img = 'core-image-minimal' | 1426 | img = 'core-image-minimal' |
1276 | with NamedTemporaryFile("w", suffix=".wks") as wks: | 1427 | with NamedTemporaryFile("w", suffix=".wks") as wks: |
1277 | wks.writelines(['part /boot --source bootimg-efi --sourceparams="loader=uefi-kernel"\n' | 1428 | wks.writelines(['part /boot --source bootimg_efi --sourceparams="loader=uefi-kernel"\n' |
1278 | 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\ | 1429 | 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\ |
1279 | 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n']) | 1430 | 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n']) |
1280 | wks.flush() | 1431 | wks.flush() |
@@ -1288,24 +1439,45 @@ class Wic2(WicTestCase): | |||
1288 | @skipIfNotArch(['i586', 'i686', 'x86_64']) | 1439 | @skipIfNotArch(['i586', 'i686', 'x86_64']) |
1289 | @OETestTag("runqemu") | 1440 | @OETestTag("runqemu") |
1290 | def test_efi_plugin_unified_kernel_image_qemu(self): | 1441 | def test_efi_plugin_unified_kernel_image_qemu(self): |
1291 | """Test efi plugin's Unified Kernel Image feature in qemu""" | 1442 | """Test Unified Kernel Image feature in qemu without systemd in initramfs or rootfs""" |
1292 | config = 'IMAGE_FSTYPES = "wic"\n'\ | 1443 | config = """ |
1293 | 'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\ | 1444 | # efi firmware must load systemd-boot, not grub |
1294 | 'WKS_FILE = "test_efi_plugin.wks"\n'\ | 1445 | EFI_PROVIDER = "systemd-boot" |
1295 | 'MACHINE_FEATURES:append = " efi"\n' | 1446 | |
1447 | # image format must be wic, needs esp partition for firmware etc | ||
1448 | IMAGE_FSTYPES:pn-core-image-base:append = " wic" | ||
1449 | WKS_FILE = "test_efi_plugin.wks" | ||
1450 | |||
1451 | # efi, uki and systemd features must be enabled | ||
1452 | MACHINE_FEATURES:append = " efi" | ||
1453 | IMAGE_CLASSES:append:pn-core-image-base = " uki" | ||
1454 | |||
1455 | # uki embeds also an initrd, no systemd or udev | ||
1456 | INITRAMFS_IMAGE = "core-image-initramfs-boot" | ||
1457 | |||
1458 | # runqemu must not load kernel separately, it's in the uki | ||
1459 | QB_KERNEL_ROOT = "" | ||
1460 | QB_DEFAULT_KERNEL = "none" | ||
1461 | |||
1462 | # boot command line provided via uki, not via bootloader | ||
1463 | UKI_CMDLINE = "rootwait root=LABEL=root console=${KERNEL_CONSOLE}" | ||
1464 | |||
1465 | """ | ||
1296 | self.append_config(config) | 1466 | self.append_config(config) |
1297 | bitbake('core-image-minimal core-image-minimal-initramfs ovmf') | 1467 | bitbake('core-image-base ovmf') |
1468 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or "" | ||
1469 | uki_filename = get_bb_var('UKI_FILENAME', 'core-image-base') | ||
1298 | self.remove_config(config) | 1470 | self.remove_config(config) |
1299 | 1471 | ||
1300 | with runqemu('core-image-minimal', ssh=False, | 1472 | with runqemu('core-image-base', ssh=False, |
1301 | runqemuparams='nographic ovmf', image_fstype='wic') as qemu: | 1473 | runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu: |
1302 | # Check that /boot has EFI bootx64.efi (required for EFI) | 1474 | # Check that /boot has EFI boot*.efi (required for EFI) |
1303 | cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l" | 1475 | cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l" |
1304 | status, output = qemu.run_serial(cmd) | 1476 | status, output = qemu.run_serial(cmd) |
1305 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | 1477 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) |
1306 | self.assertEqual(output, '1') | 1478 | self.assertEqual(output, '1') |
1307 | # Check that /boot has EFI/Linux/linux.efi (required for Unified Kernel Images auto detection) | 1479 | # Check that /boot has EFI/Linux/${UKI_FILENAME} (required for Unified Kernel Images auto detection) |
1308 | cmd = "ls /boot/EFI/Linux/linux.efi | wc -l" | 1480 | cmd = "ls /boot/EFI/Linux/%s | wc -l" % (uki_filename) |
1309 | status, output = qemu.run_serial(cmd) | 1481 | status, output = qemu.run_serial(cmd) |
1310 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | 1482 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) |
1311 | self.assertEqual(output, '1') | 1483 | self.assertEqual(output, '1') |
@@ -1315,6 +1487,80 @@ class Wic2(WicTestCase): | |||
1315 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | 1487 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) |
1316 | self.assertEqual(output, '0') | 1488 | self.assertEqual(output, '0') |
1317 | 1489 | ||
1490 | @skipIfNotArch(['aarch64']) | ||
1491 | @OETestTag("runqemu") | ||
1492 | def test_efi_plugin_plain_systemd_boot_qemu_aarch64(self): | ||
1493 | """Test plain systemd-boot in qemu with systemd""" | ||
1494 | config = """ | ||
1495 | INIT_MANAGER = "systemd" | ||
1496 | EFI_PROVIDER = "systemd-boot" | ||
1497 | |||
1498 | # image format must be wic, needs esp partition for firmware etc | ||
1499 | IMAGE_FSTYPES:pn-core-image-base:append = " wic" | ||
1500 | WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks" | ||
1501 | |||
1502 | INITRAMFS_IMAGE = "core-image-initramfs-boot" | ||
1503 | |||
1504 | # to configure runqemu | ||
1505 | IMAGE_CLASSES += "qemuboot" | ||
1506 | # u-boot efi firmware | ||
1507 | QB_DEFAULT_BIOS = "u-boot.bin" | ||
1508 | # need to use virtio, scsi not supported by u-boot by default | ||
1509 | QB_DRIVE_TYPE = "/dev/vd" | ||
1510 | |||
1511 | # disable kvm, breaks boot | ||
1512 | QEMU_USE_KVM = "" | ||
1513 | |||
1514 | IMAGE_CLASSES:remove = 'testimage' | ||
1515 | """ | ||
1516 | self.append_config(config) | ||
1517 | bitbake('core-image-base u-boot') | ||
1518 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or "" | ||
1519 | |||
1520 | with runqemu('core-image-base', ssh=False, | ||
1521 | runqemuparams='%s nographic' % (runqemu_params), image_fstype='wic') as qemu: | ||
1522 | # Check that /boot has EFI boot*.efi (required for EFI) | ||
1523 | cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l" | ||
1524 | status, output = qemu.run_serial(cmd) | ||
1525 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
1526 | self.assertEqual(output, '1') | ||
1527 | # Check that boot.conf exists | ||
1528 | cmd = "cat /boot/loader/entries/boot.conf" | ||
1529 | status, output = qemu.run_serial(cmd) | ||
1530 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
1531 | self.remove_config(config) | ||
1532 | |||
1533 | @skipIfNotArch(['i586', 'i686', 'x86_64']) | ||
1534 | @OETestTag("runqemu") | ||
1535 | def test_efi_plugin_plain_systemd_boot_qemu_x86(self): | ||
1536 | """Test plain systemd-boot to systemd in qemu""" | ||
1537 | config = """ | ||
1538 | INIT_MANAGER = "systemd" | ||
1539 | EFI_PROVIDER = "systemd-boot" | ||
1540 | |||
1541 | # image format must be wic, needs esp partition for firmware etc | ||
1542 | IMAGE_FSTYPES:pn-core-image-base:append = " wic" | ||
1543 | WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks" | ||
1544 | |||
1545 | INITRAMFS_IMAGE = "core-image-initramfs-boot" | ||
1546 | """ | ||
1547 | self.append_config(config) | ||
1548 | bitbake('core-image-base ovmf') | ||
1549 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-base') or "" | ||
1550 | self.remove_config(config) | ||
1551 | |||
1552 | with runqemu('core-image-base', ssh=False, | ||
1553 | runqemuparams='%s nographic ovmf' % (runqemu_params), image_fstype='wic') as qemu: | ||
1554 | # Check that /boot has EFI boot*.efi (required for EFI) | ||
1555 | cmd = "ls /boot/EFI/BOOT/boot*.efi | wc -l" | ||
1556 | status, output = qemu.run_serial(cmd) | ||
1557 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
1558 | self.assertEqual(output, '1') | ||
1559 | # Check that boot.conf exists | ||
1560 | cmd = "cat /boot/loader/entries/boot.conf" | ||
1561 | status, output = qemu.run_serial(cmd) | ||
1562 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | ||
1563 | |||
1318 | def test_fs_types(self): | 1564 | def test_fs_types(self): |
1319 | """Test filesystem types for empty and not empty partitions""" | 1565 | """Test filesystem types for empty and not empty partitions""" |
1320 | img = 'core-image-minimal' | 1566 | img = 'core-image-minimal' |
@@ -1446,8 +1692,8 @@ class Wic2(WicTestCase): | |||
1446 | os.rename(image_path, image_path + '.bak') | 1692 | os.rename(image_path, image_path + '.bak') |
1447 | os.rename(new_image_path, image_path) | 1693 | os.rename(new_image_path, image_path) |
1448 | 1694 | ||
1449 | # Check if it boots in qemu | 1695 | runqemu_params = get_bb_var('TEST_RUNQEMUPARAMS', 'core-image-minimal') or "" |
1450 | with runqemu('core-image-minimal', ssh=False, runqemuparams='nographic') as qemu: | 1696 | with runqemu('core-image-minimal', ssh=False, runqemuparams='%s nographic' % (runqemu_params)) as qemu: |
1451 | cmd = "ls /etc/" | 1697 | cmd = "ls /etc/" |
1452 | status, output = qemu.run_serial('true') | 1698 | status, output = qemu.run_serial('true') |
1453 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) | 1699 | self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output)) |
diff --git a/meta/lib/oeqa/selftest/context.py b/meta/lib/oeqa/selftest/context.py index 57844b289a..16f82c6737 100644 --- a/meta/lib/oeqa/selftest/context.py +++ b/meta/lib/oeqa/selftest/context.py | |||
@@ -102,6 +102,13 @@ class OESelftestTestContext(OETestContext): | |||
102 | oe.path.copytree(builddir + "/cache", newbuilddir + "/cache") | 102 | oe.path.copytree(builddir + "/cache", newbuilddir + "/cache") |
103 | oe.path.copytree(selftestdir, newselftestdir) | 103 | oe.path.copytree(selftestdir, newselftestdir) |
104 | 104 | ||
105 | # if the last line of local.conf in newbuilddir is not empty and does not end with newline then add one | ||
106 | localconf_path = newbuilddir + "/conf/local.conf" | ||
107 | with open(localconf_path, "r+", encoding="utf-8") as f: | ||
108 | last_line = f.readlines()[-1] | ||
109 | if last_line and not last_line.endswith("\n"): | ||
110 | f.write("\n") | ||
111 | |||
105 | subprocess.check_output("git init && git add * && git commit -a -m 'initial'", cwd=newselftestdir, shell=True) | 112 | subprocess.check_output("git init && git add * && git commit -a -m 'initial'", cwd=newselftestdir, shell=True) |
106 | 113 | ||
107 | # Tried to used bitbake-layers add/remove but it requires recipe parsing and hence is too slow | 114 | # Tried to used bitbake-layers add/remove but it requires recipe parsing and hence is too slow |
@@ -114,11 +121,15 @@ class OESelftestTestContext(OETestContext): | |||
114 | bblayers_abspath = [os.path.abspath(path) for path in bblayers.split()] | 121 | bblayers_abspath = [os.path.abspath(path) for path in bblayers.split()] |
115 | with open("%s/conf/bblayers.conf" % newbuilddir, "a") as f: | 122 | with open("%s/conf/bblayers.conf" % newbuilddir, "a") as f: |
116 | newbblayers = "# new bblayers to be used by selftest in the new build dir '%s'\n" % newbuilddir | 123 | newbblayers = "# new bblayers to be used by selftest in the new build dir '%s'\n" % newbuilddir |
124 | newbblayers += 'unset BBLAYERS\n' | ||
117 | newbblayers += 'BBLAYERS = "%s"\n' % ' '.join(bblayers_abspath) | 125 | newbblayers += 'BBLAYERS = "%s"\n' % ' '.join(bblayers_abspath) |
118 | f.write(newbblayers) | 126 | f.write(newbblayers) |
119 | 127 | ||
128 | # Rewrite builddir paths seen in environment variables | ||
120 | for e in os.environ: | 129 | for e in os.environ: |
121 | if builddir + "/" in os.environ[e]: | 130 | # Rewrite paths that absolutely point inside builddir |
131 | # (e.g $builddir/conf/ would be rewritten but not $builddir/../bitbake/) | ||
132 | if builddir + "/" in os.environ[e] and builddir + "/" in os.path.abspath(os.environ[e]): | ||
122 | os.environ[e] = os.environ[e].replace(builddir + "/", newbuilddir + "/") | 133 | os.environ[e] = os.environ[e].replace(builddir + "/", newbuilddir + "/") |
123 | if os.environ[e].endswith(builddir): | 134 | if os.environ[e].endswith(builddir): |
124 | os.environ[e] = os.environ[e].replace(builddir, newbuilddir) | 135 | os.environ[e] = os.environ[e].replace(builddir, newbuilddir) |
@@ -194,8 +205,23 @@ class OESelftestTestContextExecutor(OETestContextExecutor): | |||
194 | parser.add_argument('-R', '--skip-tests', required=False, action='store', | 205 | parser.add_argument('-R', '--skip-tests', required=False, action='store', |
195 | nargs='+', dest="skips", default=None, | 206 | nargs='+', dest="skips", default=None, |
196 | help='Skip the tests specified. Format should be <module>[.<class>[.<test_method>]]') | 207 | help='Skip the tests specified. Format should be <module>[.<class>[.<test_method>]]') |
208 | |||
209 | def check_parallel_support(parameter): | ||
210 | if not parameter.isdigit(): | ||
211 | import argparse | ||
212 | raise argparse.ArgumentTypeError("argument -j/--num-processes: invalid int value: '%s' " % str(parameter)) | ||
213 | |||
214 | processes = int(parameter) | ||
215 | if processes: | ||
216 | try: | ||
217 | import testtools, subunit | ||
218 | except ImportError: | ||
219 | print("Failed to import testtools or subunit, the testcases will run serially") | ||
220 | processes = None | ||
221 | return processes | ||
222 | |||
197 | parser.add_argument('-j', '--num-processes', dest='processes', action='store', | 223 | parser.add_argument('-j', '--num-processes', dest='processes', action='store', |
198 | type=int, help="number of processes to execute in parallel with") | 224 | type=check_parallel_support, help="number of processes to execute in parallel with") |
199 | 225 | ||
200 | parser.add_argument('-t', '--select-tag', dest="select_tags", | 226 | parser.add_argument('-t', '--select-tag', dest="select_tags", |
201 | action='append', default=None, | 227 | action='append', default=None, |