summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/selftest
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oeqa/selftest')
-rw-r--r--meta/lib/oeqa/selftest/cases/archiver.py18
-rw-r--r--meta/lib/oeqa/selftest/cases/barebox.py44
-rw-r--r--meta/lib/oeqa/selftest/cases/bbclasses.py106
-rw-r--r--meta/lib/oeqa/selftest/cases/bblayers.py31
-rw-r--r--meta/lib/oeqa/selftest/cases/bbtests.py21
-rw-r--r--meta/lib/oeqa/selftest/cases/binutils.py2
-rw-r--r--meta/lib/oeqa/selftest/cases/buildhistory.py61
-rw-r--r--meta/lib/oeqa/selftest/cases/buildoptions.py51
-rw-r--r--meta/lib/oeqa/selftest/cases/containerimage.py3
-rw-r--r--meta/lib/oeqa/selftest/cases/cve_check.py256
-rw-r--r--meta/lib/oeqa/selftest/cases/debuginfod.py14
-rw-r--r--meta/lib/oeqa/selftest/cases/devtool.py216
-rw-r--r--meta/lib/oeqa/selftest/cases/distrodata.py22
-rw-r--r--meta/lib/oeqa/selftest/cases/efibootpartition.py11
-rw-r--r--meta/lib/oeqa/selftest/cases/esdk.py6
-rw-r--r--meta/lib/oeqa/selftest/cases/fetch.py16
-rw-r--r--meta/lib/oeqa/selftest/cases/fitimage.py2117
-rw-r--r--meta/lib/oeqa/selftest/cases/gcc.py4
-rw-r--r--meta/lib/oeqa/selftest/cases/gdbserver.py2
-rw-r--r--meta/lib/oeqa/selftest/cases/imagefeatures.py15
-rw-r--r--meta/lib/oeqa/selftest/cases/incompatible_lic.py9
-rw-r--r--meta/lib/oeqa/selftest/cases/layerappend.py2
-rw-r--r--meta/lib/oeqa/selftest/cases/liboe.py37
-rw-r--r--meta/lib/oeqa/selftest/cases/locales.py2
-rw-r--r--meta/lib/oeqa/selftest/cases/meta_ide.py8
-rw-r--r--meta/lib/oeqa/selftest/cases/minidebuginfo.py16
-rw-r--r--meta/lib/oeqa/selftest/cases/oescripts.py26
-rw-r--r--meta/lib/oeqa/selftest/cases/overlayfs.py41
-rw-r--r--meta/lib/oeqa/selftest/cases/package.py26
-rw-r--r--meta/lib/oeqa/selftest/cases/picolibc.py18
-rw-r--r--meta/lib/oeqa/selftest/cases/recipetool.py280
-rw-r--r--meta/lib/oeqa/selftest/cases/recipeutils.py4
-rw-r--r--meta/lib/oeqa/selftest/cases/reproducible.py111
-rw-r--r--meta/lib/oeqa/selftest/cases/retain.py241
-rw-r--r--meta/lib/oeqa/selftest/cases/runtime_test.py11
-rw-r--r--meta/lib/oeqa/selftest/cases/rust.py138
-rw-r--r--meta/lib/oeqa/selftest/cases/sdk.py39
-rw-r--r--meta/lib/oeqa/selftest/cases/signing.py2
-rw-r--r--meta/lib/oeqa/selftest/cases/spdx.py256
-rw-r--r--meta/lib/oeqa/selftest/cases/sstatetests.py174
-rw-r--r--meta/lib/oeqa/selftest/cases/toolchain.py71
-rw-r--r--meta/lib/oeqa/selftest/cases/uboot.py98
-rw-r--r--meta/lib/oeqa/selftest/cases/uki.py141
-rw-r--r--meta/lib/oeqa/selftest/cases/wic.py384
-rw-r--r--meta/lib/oeqa/selftest/context.py30
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
8from oeqa.selftest.case import OESelftestTestCase
9from oeqa.utils.commands import bitbake, runqemu
10from oeqa.core.decorator.data import skipIfNotArch
11from oeqa.core.decorator import OETestTag
12
13barebox_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
20class 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("""
30QB_DEFAULT_KERNEL = "barebox-dt-2nd.img"
31PREFERRED_PROVIDER_virtual/bootloader = "barebox"
32QEMU_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
7from oeqa.selftest.case import OESelftestTestCase
8from oeqa.utils.commands import get_bb_vars, bitbake
9
10class 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("""
44DISTRO_FEATURES:append = " systemd usrmerge"
45DISTRO_FEATURES:remove = "sysvinit"
46VIRTUAL-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("""
68DISTRO_FEATURES:append = " systemd sysvinit usrmerge"
69VIRTUAL-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("""
91DISTRO_FEATURES:remove = "systemd"
92DISTRO_FEATURES:append = " sysvinit usrmerge"
93VIRTUAL-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
244class 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"
236OVERRIDES .= ":gplv3test"
236require conf/distro/include/no-gplv3.inc 237require 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"
384TEST_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)
395bitbake -e: "%s"
396bitbake-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
9import datetime 9import datetime
10 10
11from oeqa.selftest.case import OESelftestTestCase 11from oeqa.selftest.case import OESelftestTestCase
12from oeqa.utils.commands import bitbake, get_bb_vars 12from oeqa.utils.commands import bitbake, get_bb_vars, get_bb_var, runCmd
13 13
14 14
15class BuildhistoryBase(OESelftestTestCase): 15class 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
10import shutil 10import shutil
11import tempfile 11import tempfile
12from oeqa.selftest.case import OESelftestTestCase 12from oeqa.selftest.case import OESelftestTestCase
13from oeqa.selftest.cases.buildhistory import BuildhistoryBase
14from oeqa.core.decorator.data import skipIfMachine 13from oeqa.core.decorator.data import skipIfMachine
15from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars 14from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars
16import oeqa.utils.ftools as ftools 15import 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
143class 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
179class ArchiverTest(OESelftestTestCase): 141class 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
235class Poisoning(OESelftestTestCase): 197class 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"
43IMAGE_FSTYPES = "container" 43IMAGE_FSTYPES = "container"
44PACKAGE_CLASSES = "package_ipk" 44PACKAGE_CLASSES = "package_ipk"
45IMAGE_FEATURES = ""
46IMAGE_BUILDINFO_FILE = "" 45IMAGE_BUILDINFO_FILE = ""
47INIT_MANAGER = "sysvinit" 46INIT_MANAGER = "sysvinit"
48IMAGE_INSTALL:remove = "ssh-pregen-hostkeys" 47IMAGE_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("""
115TMPDIR = "${TOPDIR}/tmp-debuginfod" 115TMPDIR = "${TOPDIR}/tmp-debuginfod"
116DISTRO_FEATURES:append = " debuginfod" 116DISTRO_FEATURES:append = " debuginfod"
117INHERIT += "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("""
142TMPDIR = "${TOPDIR}/tmp-debuginfod" 143TMPDIR = "${TOPDIR}/tmp-debuginfod"
143DISTRO_FEATURES:append = " debuginfod" 144DISTRO_FEATURES:append = " debuginfod"
145INHERIT += "localpkgfeed"
144CORE_IMAGE_EXTRA_INSTALL += "elfutils xz" 146CORE_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
1763class 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
1804class 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
1873class 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("""
116Unable to find recipes for the following entries in maintainers.inc: 116Unable 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
8from oeqa.selftest.case import OESelftestTestCase 8from oeqa.selftest.case import OESelftestTestCase
9from oeqa.utils.commands import bitbake, runqemu 9from oeqa.utils.commands import bitbake, runqemu, get_bb_var
10from oeqa.core.decorator.data import skipIfNotMachine 10from oeqa.core.decorator.data import skipIfNotMachine
11import oe.types 11import 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("""
23EFI_PROVIDER = "systemd-boot" 24EFI_PROVIDER = "grub-efi"
24IMAGE_FSTYPES:pn-%s:append = " wic" 25IMAGE_FSTYPES:pn-%s:append = " wic"
25MACHINE_FEATURES:append = " efi" 26MACHINE_FEATURES:append = " efi"
26WKS_FILE = "efi-bootdisk.wks.in" 27WKS_FILE = "efi-bootdisk.wks.in"
27IMAGE_INSTALL:append = " grub-efi systemd-boot kernel-image-bzimage" 28IMAGE_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
7from oeqa.selftest.case import OESelftestTestCase
8from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars
9import os 7import os
10import re 8import re
9import shlex
10import logging
11import pprint
12import tempfile
13
14import oe.fitimage
15
16from oeqa.selftest.case import OESelftestTestCase
17from oeqa.utils.commands import runCmd, bitbake, get_bb_vars, get_bb_var
18
19
20class 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
38class 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
395class 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
12class 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
739class 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
29KERNEL_IMAGETYPE = "Image" 756KERNEL_IMAGETYPE = "Image"
30KERNEL_IMAGETYPES += " fitImage "
31KERNEL_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
34IMAGE_FSTYPES += "cpio.gz" 759IMAGE_FSTYPES += "cpio.gz"
@@ -40,79 +765,145 @@ UBOOT_RD_ENTRYPOINT = "0x88000000"
40UBOOT_LOADADDRESS = "0x80080000" 765UBOOT_LOADADDRESS = "0x80080000"
41UBOOT_ENTRYPOINT = "0x80080000" 766UBOOT_ENTRYPOINT = "0x80080000"
42FIT_DESC = "A model description" 767FIT_DESC = "A model description"
768FIT_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), 789DISTRO = "poky"
89 "Fields in Image Tree Source File %s did not match, error in finding %s" 790MACHINE = "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
831MACHINE = "beaglebone-yocto"
832# Add a devicetree overlay which does not need kernel sources
833PREFERRED_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
858MACHINE = "beaglebone-yocto"
859UBOOT_SIGN_ENABLE = "1"
860UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
861UBOOT_SIGN_KEYNAME = "dev"
862UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
863FIT_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
113MACHINE = "beaglebone-yocto" 906MACHINE = "beaglebone-yocto"
114KERNEL_IMAGETYPES += " fitImage "
115KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
116UBOOT_SIGN_ENABLE = "1" 907UBOOT_SIGN_ENABLE = "1"
117FIT_GENERATE_KEYS = "1" 908FIT_GENERATE_KEYS = "1"
118UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 909UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
@@ -121,211 +912,494 @@ UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
121FIT_SIGN_INDIVIDUAL = "1" 912FIT_SIGN_INDIVIDUAL = "1"
122UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'" 913UBOOT_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 941DISTRO = "poky"
256MACHINE = "qemuarm" 942MACHINE = "beaglebone-yocto"
257UBOOT_MACHINE = "am57xx_evm_defconfig" 943INITRAMFS_IMAGE = "core-image-minimal-initramfs"
258SPL_BINARY = "MLO" 944INITRAMFS_SCRIPTS = ""
945UBOOT_MACHINE = "am335x_evm_defconfig"
946UBOOT_SIGN_ENABLE = "1"
947UBOOT_SIGN_KEYNAME = "beaglebonekey"
948UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}"
949UBOOT_DTB_BINARY = "u-boot.dtb"
950UBOOT_ENTRYPOINT = "0x80000000"
951UBOOT_LOADADDRESS = "0x80000000"
952UBOOT_RD_LOADADDRESS = "0x88000000"
953UBOOT_RD_ENTRYPOINT = "0x88000000"
954UBOOT_DTB_LOADADDRESS = "0x82000000"
955UBOOT_ARCH = "arm"
956UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
957UBOOT_MKIMAGE_KERNEL_TYPE = "kernel"
958UBOOT_EXTLINUX = "0"
959KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
960FIT_KERNEL_COMP_ALG = "none"
961FIT_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
261UBOOT_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)
264UBOOT_LOADADDRESS = "0x80080000" 977 self._test_fitimage(bb_vars)
265UBOOT_ENTRYPOINT = "0x80080000" 978
266UBOOT_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 = """
269KERNEL_IMAGETYPES += " fitImage " 995DISTRO = "poky"
270KERNEL_CLASSES = " kernel-fitimage" 996MACHINE = "beaglebone-yocto"
997INITRAMFS_IMAGE_BUNDLE = "1"
998INITRAMFS_IMAGE = "core-image-minimal-initramfs"
999INITRAMFS_SCRIPTS = ""
1000UBOOT_MACHINE = "am335x_evm_defconfig"
271UBOOT_SIGN_ENABLE = "1" 1001UBOOT_SIGN_ENABLE = "1"
272FIT_GENERATE_KEYS = "1" 1002UBOOT_SIGN_KEYNAME = "beaglebonekey"
273UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 1003UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}"
274UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest" 1004UBOOT_DTB_BINARY = "u-boot.dtb"
275UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" 1005UBOOT_ENTRYPOINT = "0x80000000"
276FIT_SIGN_INDIVIDUAL = "1" 1006UBOOT_LOADADDRESS = "0x80000000"
1007UBOOT_DTB_LOADADDRESS = "0x82000000"
1008UBOOT_ARCH = "arm"
1009UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
1010UBOOT_MKIMAGE_KERNEL_TYPE = "kernel"
1011UBOOT_EXTLINUX = "0"
1012KERNEL_IMAGETYPE_REPLACEMENT = "zImage"
1013FIT_KERNEL_COMP_ALG = "none"
1014FIT_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
1023class 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
1125class 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"
349UBOOT_LOADADDRESS = "0x80080000" 1423UBOOT_LOADADDRESS = "0x80080000"
350UBOOT_ENTRYPOINT = "0x80080000" 1424UBOOT_ENTRYPOINT = "0x80080000"
351UBOOT_FIT_DESC = "A model description" 1425UBOOT_FIT_DESC = "A model description"
352KERNEL_IMAGETYPES += " fitImage "
353KERNEL_CLASSES = " kernel-fitimage "
354INHERIT += "test-mkimage-wrapper"
355UBOOT_SIGN_ENABLE = "1"
356FIT_GENERATE_KEYS = "1"
357UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
358UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
359UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
360FIT_SIGN_INDIVIDUAL = "1"
361UBOOT_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
431MACHINE = "qemuarm" 1454MACHINE = "qemuarm"
432UBOOT_MACHINE = "am57xx_evm_defconfig" 1455UBOOT_MACHINE = "am57xx_evm_defconfig"
433SPL_BINARY = "MLO" 1456SPL_BINARY = "MLO"
434# The kernel-fitimage class is a dependency even if we're only
435# creating/signing the U-Boot fitImage
436KERNEL_CLASSES = " kernel-fitimage"
437INHERIT += "test-mkimage-wrapper"
438# Enable creation and signing of the U-Boot fitImage 1457# Enable creation and signing of the U-Boot fitImage
439UBOOT_FITIMAGE_ENABLE = "1" 1458UBOOT_FITIMAGE_ENABLE = "1"
440SPL_SIGN_ENABLE = "1" 1459SPL_SIGN_ENABLE = "1"
@@ -452,106 +1471,9 @@ UBOOT_FIT_GENERATE_KEYS = "1"
452UBOOT_FIT_HASH_ALG = "sha256" 1471UBOOT_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"
588UBOOT_ENTRYPOINT = "0x80000000" 1510UBOOT_ENTRYPOINT = "0x80000000"
589UBOOT_LOADADDRESS = "0x80000000" 1511UBOOT_LOADADDRESS = "0x80000000"
590UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 1512UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
591UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded Kernel comment'" 1513UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'"
592UBOOT_DTB_LOADADDRESS = "0x82000000" 1514UBOOT_DTB_LOADADDRESS = "0x82000000"
593UBOOT_ARCH = "arm" 1515UBOOT_ARCH = "arm"
594SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 1516SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
@@ -596,251 +1518,214 @@ SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart cascaded U-Boot comment'"
596UBOOT_EXTLINUX = "0" 1518UBOOT_EXTLINUX = "0"
597UBOOT_FIT_GENERATE_KEYS = "1" 1519UBOOT_FIT_GENERATE_KEYS = "1"
598UBOOT_FIT_HASH_ALG = "sha256" 1520UBOOT_FIT_HASH_ALG = "sha256"
599KERNEL_IMAGETYPES += " fitImage "
600KERNEL_CLASSES = " kernel-fitimage "
601INHERIT += "test-mkimage-wrapper"
602UBOOT_SIGN_ENABLE = "1" 1521UBOOT_SIGN_ENABLE = "1"
603FIT_GENERATE_KEYS = "1"
604UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys" 1522UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
605UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
606UBOOT_SIGN_KEYNAME = "cfg-oe-selftest" 1523UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
607FIT_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(';'): 1549MACHINE = "qemuarm"
652 self.fail('Unexpected formatting in %s sigs section line %d:%s' % (fitimage_its_path, linect, line)) 1550UBOOT_MACHINE = "am57xx_evm_defconfig"
653 key, value = line.split('=', 1) 1551SPL_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: 1554UBOOT_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"', 1557UBOOT_LOADADDRESS = "0x80080000"
662 'key-name-hint': '"spl-cascaded-oe-selftest"', 1558UBOOT_ENTRYPOINT = "0x80080000"
663 } 1559UBOOT_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 1562UBOOT_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> 1565UBOOT_FIT_TEE_IMAGE = "${TOPDIR}/tee-dummy.bin"
726 """ 1566UBOOT_FIT_TEE_LOADADDRESS = "0x80180000"
1567UBOOT_FIT_TEE_ENTRYPOINT = "0x80180000"
1568
1569# Enable creation of the ATF fitImage
1570UBOOT_FIT_ARM_TRUSTED_FIRMWARE = "1"
1571
1572# ATF fitImage properties
1573UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE = "${TOPDIR}/atf-dummy.bin"
1574UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS = "0x80280000"
1575UBOOT_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 = """
729DISTRO="poky" 1613# There's no U-boot deconfig with CONFIG_FIT_SIGNATURE yet, so we need at
730MACHINE = "beaglebone-yocto" 1614# least CONFIG_SPL_LOAD_FIT and CONFIG_SPL_OF_CONTROL set
731INITRAMFS_IMAGE_BUNDLE = "1" 1615MACHINE = "qemuarm"
732INITRAMFS_IMAGE = "core-image-minimal-initramfs" 1616UBOOT_MACHINE = "am57xx_evm_defconfig"
733INITRAMFS_SCRIPTS = "" 1617SPL_BINARY = "MLO"
734UBOOT_MACHINE = "am335x_evm_defconfig" 1618# Enable creation and signing of the U-Boot fitImage
735KERNEL_CLASSES = " kernel-fitimage " 1619UBOOT_FITIMAGE_ENABLE = "1"
736KERNEL_IMAGETYPES = "fitImage" 1620SPL_SIGN_ENABLE = "1"
737UBOOT_SIGN_ENABLE = "1" 1621SPL_SIGN_KEYNAME = "spl-oe-selftest"
738UBOOT_SIGN_KEYNAME = "beaglebonekey" 1622SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
739UBOOT_SIGN_KEYDIR ?= "${DEPLOY_DIR_IMAGE}"
740UBOOT_DTB_BINARY = "u-boot.dtb" 1623UBOOT_DTB_BINARY = "u-boot.dtb"
741UBOOT_ENTRYPOINT = "0x80000000" 1624UBOOT_ENTRYPOINT = "0x80000000"
742UBOOT_LOADADDRESS = "0x80000000" 1625UBOOT_LOADADDRESS = "0x80000000"
743UBOOT_DTB_LOADADDRESS = "0x82000000"
744UBOOT_ARCH = "arm" 1626UBOOT_ARCH = "arm"
745UBOOT_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000" 1627SPL_MKIMAGE_DTCOPTS = "-I dts -O dtb -p 2000"
746UBOOT_MKIMAGE_KERNEL_TYPE = "kernel" 1628SPL_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot ATF TEE comment'"
747UBOOT_EXTLINUX = "0" 1629UBOOT_EXTLINUX = "0"
748FIT_GENERATE_KEYS = "1" 1630UBOOT_FIT_GENERATE_KEYS = "1"
749KERNEL_IMAGETYPE_REPLACEMENT = "zImage" 1631UBOOT_FIT_HASH_ALG = "sha256"
750FIT_KERNEL_COMP_ALG = "none"
751FIT_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") 1634UBOOT_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') 1637UBOOT_FIT_TEE_IMAGE = "${TOPDIR}/tee-dummy.bin"
760 machine = get_bb_var('MACHINE') 1638UBOOT_FIT_TEE_LOADADDRESS = "0x80180000"
761 fitimage_its_path = os.path.join(deploy_dir_image, 1639UBOOT_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)) 1642UBOOT_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')) 1645UBOOT_FIT_ARM_TRUSTED_FIRMWARE_IMAGE = "${TOPDIR}/atf-dummy.bin"
772 kernel_type = str(get_bb_var('UBOOT_MKIMAGE_KERNEL_TYPE')) 1646UBOOT_FIT_ARM_TRUSTED_FIRMWARE_LOADADDRESS = "0x80280000"
773 kernel_compression = str(get_bb_var('FIT_KERNEL_COMP_ALG')) 1647UBOOT_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
1679MACHINE = "beaglebone-yocto"
1680UBOOT_SIGN_ENABLE = "1"
1681UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
1682UBOOT_SIGN_KEYNAME = "the-kernel-config-key"
1683UBOOT_SIGN_IMG_KEYNAME = "the-kernel-image-key"
1684UBOOT_MKIMAGE_DTCOPTS="-I dts -O dtb -p 2000"
1685FIT_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
1718MACHINE = "qemuarm"
1719UBOOT_MACHINE = "am57xx_evm_defconfig"
1720# Enable creation and signing of the U-Boot fitImage (no SPL)
1721UBOOT_FITIMAGE_ENABLE = "1"
1722SPL_DTB_BINARY = ""
1723SPL_SIGN_ENABLE = "1"
1724SPL_SIGN_KEYNAME = "spl-oe-selftest"
1725SPL_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
1726UBOOT_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"
250DISTRO_FEATURES:append = " pam opengl wayland" 250DISTRO_FEATURES:append = " pam opengl wayland"
251 251
252# Switch to systemd 252# Switch to systemd
253DISTRO_FEATURES:append = " systemd usrmerge" 253INIT_MANAGER = "systemd"
254VIRTUAL-RUNTIME_init_manager = "systemd"
255VIRTUAL-RUNTIME_initscripts = ""
256VIRTUAL-RUNTIME_syslog = ""
257VIRTUAL-RUNTIME_login_manager = "shadow-base"
258DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
259 254
260# Replace busybox 255# Replace busybox
261PREFERRED_PROVIDER_virtual/base-utils = "packagegroup-core-base-utils" 256PREFERRED_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 = """
321DISTRO_FEATURES:append = " api-documentation" 316DISTRO_FEATURES:append = " api-documentation"
322CORE_IMAGE_EXTRA_INSTALL = "man-pages kmod-doc" 317CORE_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 """
103IMAGE_INSTALL:append = " bash" 103IMAGE_INSTALL:append = " bash"
104INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*" 104INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*"
105MACHINE_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("""
138INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*" 139INCOMPATIBLE_LICENSE:pn-core-image-minimal = "GPL-3.0* LGPL-3.0*"
140
141require 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}:"
37SRC_URI:append = " file://appendtest.txt" 37SRC_URI:append = " file://appendtest.txt"
38 38
39sysroot_stage_all:append() { 39sysroot_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
9import oe.path 9import oe.path
10import os 10import os
11 11
12class LibOE(OESelftestTestCase): 12class 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
106class 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
116echo Ivn fgqbhg | tr '[a-zA-Z]' '[n-za-mN-ZA-M]'
117echo Ivn fgqree | tr '[a-zA-Z]' '[n-za-mN-ZA-M]' >&2
118exit 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
8import tempfile 8import tempfile
9import shutil 9import shutil
10 10
11from oeqa.core.decorator import OETestTag
11from oeqa.selftest.case import OESelftestTestCase 12from oeqa.selftest.case import OESelftestTestCase
12from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runCmd 13from 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("""
54DISTRO_FEATURES:append = " minidebuginfo"
55INIT_MANAGER = "systemd"
56IMAGE_CLASSES += "testimage"
57TEST_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
9import importlib 9import importlib
10import unittest 10import unittest
11from oeqa.selftest.case import OESelftestTestCase 11from oeqa.selftest.case import OESelftestTestCase
12from oeqa.selftest.cases.buildhistory import BuildhistoryBase
13from oeqa.utils.commands import runCmd, bitbake, get_bb_var 12from oeqa.utils.commands import runCmd, bitbake, get_bb_var
14from oeqa.utils import CommandError 13from oeqa.utils import CommandError
15 14
16class 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")
40class OEPybootchartguyTests(OESelftestTestCase): 16class 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
7from oeqa.selftest.case import OESelftestTestCase 7from oeqa.selftest.case import OESelftestTestCase
8from oeqa.utils.commands import bitbake, runqemu 8from oeqa.utils.commands import bitbake, runqemu, get_bb_vars
9from oeqa.core.decorator import OETestTag 9from oeqa.core.decorator import OETestTag
10from oeqa.core.decorator.data import skipIfNotMachine 10from 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
7from oeqa.selftest.case import OESelftestTestCase
8from oeqa.utils.commands import bitbake, get_bb_var
9
10class 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
985class RecipetoolTests(RecipetoolBase): 798class 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
100def run_diffoscope(a_dir, b_dir, html_dir, max_report_size=0, **kwargs): 100def 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
8import os
9import glob
10import fnmatch
11import oe.path
12import shutil
13import tarfile
14from oeqa.utils.commands import bitbake, get_bb_vars
15from oeqa.selftest.case import OESelftestTestCase
16
17class 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
2import os
3import subprocess 2import subprocess
4import time 3import time
5from oeqa.core.decorator import OETestTag 4from oeqa.core.decorator import OETestTag
5from oeqa.core.decorator.data import skipIfArch
6from oeqa.core.case import OEPTestResultTestCase 6from oeqa.core.case import OEPTestResultTestCase
7from oeqa.selftest.case import OESelftestTestCase 7from oeqa.selftest.case import OESelftestTestCase
8from oeqa.utils.commands import runCmd, bitbake, get_bb_var, get_bb_vars, runqemu, Command 8from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu
9from oeqa.utils.sshcontrol import SSHControl 9from oeqa.utils.sshcontrol import SSHControl
10 10
11def parse_results(filename): 11def 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")
40class RustSelfTestSystemEmulated(OESelftestTestCase, OEPTestResultTestCase): 40class 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
7import os.path
8
9from oeqa.selftest.case import OESelftestTestCase
10from oeqa.utils.commands import bitbake, get_bb_vars
11
12class 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("""
26TOOLCHAIN_HOST_TASK:append = " nativesdk-selftest-hello"
27IMAGE_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
7import json 7import json
8import os 8import os
9import textwrap
10import hashlib
11from pathlib import Path
9from oeqa.selftest.case import OESelftestTestCase 12from oeqa.selftest.case import OESelftestTestCase
10from oeqa.utils.commands import bitbake, get_bb_var, runCmd 13from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runCmd
14import oe.spdx30
11 15
12class SPDXCheck(OESelftestTestCase):
13 16
17class 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(
22INHERIT += "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
75class 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
143class 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
253class SStateCreation(SStateBase): 246class 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
266class SStateCleanup(SStateBase): 253class 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
369class SStateHashSameSigs(SStateBase): 356class 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("""
379MACHINE = "qemux86" 360MACHINE = "qemux86"
380TMPDIR = "${TOPDIR}/tmp-sstatesamehash" 361TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
381TCLIBCAPPEND = ""
382BUILD_ARCH = "x86_64" 362BUILD_ARCH = "x86_64"
383BUILD_OS = "linux" 363BUILD_OS = "linux"
384SDKMACHINE = "x86_64" 364SDKMACHINE = "x86_64"
@@ -390,13 +370,12 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
390 self.write_config(""" 370 self.write_config("""
391MACHINE = "qemux86" 371MACHINE = "qemux86"
392TMPDIR = "${TOPDIR}/tmp-sstatesamehash2" 372TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
393TCLIBCAPPEND = ""
394BUILD_ARCH = "i686" 373BUILD_ARCH = "i686"
395BUILD_OS = "linux" 374BUILD_OS = "linux"
396SDKMACHINE = "i686" 375SDKMACHINE = "%s"
397PACKAGE_CLASSES = "package_rpm package_ipk package_deb" 376PACKAGE_CLASSES = "package_rpm package_ipk package_deb"
398BB_SIGNATURE_HANDLER = "OEBasicHash" 377BB_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("""
428TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" 421TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
429TCLIBCAPPEND = \"\"
430NATIVELSBSTRING = \"DistroA\" 422NATIVELSBSTRING = \"DistroA\"
431BB_SIGNATURE_HANDLER = "OEBasicHash" 423BB_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("""
436TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" 428TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
437TCLIBCAPPEND = \"\"
438NATIVELSBSTRING = \"DistroB\" 429NATIVELSBSTRING = \"DistroB\"
439BB_SIGNATURE_HANDLER = "OEBasicHash" 430BB_SIGNATURE_HANDLER = "OEBasicHash"
440""") 431""")
@@ -463,17 +454,17 @@ class SStateHashSameSigs2(SStateBase):
463 454
464 configA = """ 455 configA = """
465TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" 456TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
466TCLIBCAPPEND = \"\"
467MACHINE = \"qemux86-64\" 457MACHINE = \"qemux86-64\"
468BB_SIGNATURE_HANDLER = "OEBasicHash" 458BB_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 = """
472TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" 462TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
473TCLIBCAPPEND = \"\"
474MACHINE = \"qemuarm\" 463MACHINE = \"qemuarm\"
475OLDEST_KERNEL = \"3.3.0\" 464OLDEST_KERNEL = \"3.3.0\"
476BB_SIGNATURE_HANDLER = "OEBasicHash" 465BB_SIGNATURE_HANDLER = "OEBasicHash"
466ERROR_QA:append = " somenewoption"
467WARN_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 = """
486TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" 477TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
487TCLIBCAPPEND = \"\"
488MACHINE = \"qemux86-64\" 478MACHINE = \"qemux86-64\"
489require conf/multilib.conf 479require conf/multilib.conf
490MULTILIBS = \"multilib:lib32\" 480MULTILIBS = \"multilib:lib32\"
@@ -493,7 +483,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
493""" 483"""
494 configB = """ 484 configB = """
495TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" 485TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
496TCLIBCAPPEND = \"\"
497MACHINE = \"qemuarm\" 486MACHINE = \"qemuarm\"
498require conf/multilib.conf 487require conf/multilib.conf
499MULTILIBS = \"\" 488MULTILIBS = \"\"
@@ -511,7 +500,6 @@ class SStateHashSameSigs3(SStateBase):
511 500
512 self.write_config(""" 501 self.write_config("""
513TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" 502TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
514TCLIBCAPPEND = \"\"
515MACHINE = \"qemux86\" 503MACHINE = \"qemux86\"
516require conf/multilib.conf 504require conf/multilib.conf
517MULTILIBS = "multilib:lib32" 505MULTILIBS = "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("""
524TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" 512TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
525TCLIBCAPPEND = \"\"
526MACHINE = \"qemux86copy\" 513MACHINE = \"qemux86copy\"
527require conf/multilib.conf 514require conf/multilib.conf
528MULTILIBS = "multilib:lib32" 515MULTILIBS = "multilib:lib32"
@@ -559,7 +546,6 @@ BB_SIGNATURE_HANDLER = "OEBasicHash"
559 546
560 self.write_config(""" 547 self.write_config("""
561TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\" 548TMPDIR = \"${TOPDIR}/tmp-sstatesamehash\"
562TCLIBCAPPEND = \"\"
563MACHINE = \"qemux86\" 549MACHINE = \"qemux86\"
564require conf/multilib.conf 550require conf/multilib.conf
565MULTILIBS = "multilib:lib32" 551MULTILIBS = "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("""
572TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\" 558TMPDIR = \"${TOPDIR}/tmp-sstatesamehash2\"
573TCLIBCAPPEND = \"\"
574MACHINE = \"qemux86copy\" 559MACHINE = \"qemux86copy\"
575BB_SIGNATURE_HANDLER = "OEBasicHash" 560BB_SIGNATURE_HANDLER = "OEBasicHash"
576""") 561""")
@@ -598,7 +583,6 @@ class SStateHashSameSigs4(SStateBase):
598 583
599 self.write_config(""" 584 self.write_config("""
600TMPDIR = "${TOPDIR}/tmp-sstatesamehash" 585TMPDIR = "${TOPDIR}/tmp-sstatesamehash"
601TCLIBCAPPEND = ""
602BB_NUMBER_THREADS = "${@oe.utils.cpu_count()}" 586BB_NUMBER_THREADS = "${@oe.utils.cpu_count()}"
603PARALLEL_MAKE = "-j 1" 587PARALLEL_MAKE = "-j 1"
604DL_DIR = "${TOPDIR}/download1" 588DL_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("""
615TMPDIR = "${TOPDIR}/tmp-sstatesamehash2" 599TMPDIR = "${TOPDIR}/tmp-sstatesamehash2"
616TCLIBCAPPEND = ""
617BB_NUMBER_THREADS = "${@oe.utils.cpu_count()+1}" 600BB_NUMBER_THREADS = "${@oe.utils.cpu_count()+1}"
618PARALLEL_MAKE = "-j 2" 601PARALLEL_MAKE = "-j 2"
619DL_DIR = "${TOPDIR}/download2" 602DL_DIR = "${TOPDIR}/download2"
@@ -724,7 +707,6 @@ class SStateFindSiginfo(SStateBase):
724 """ 707 """
725 self.write_config(""" 708 self.write_config("""
726TMPDIR = \"${TOPDIR}/tmp-sstates-findsiginfo\" 709TMPDIR = \"${TOPDIR}/tmp-sstates-findsiginfo\"
727TCLIBCAPPEND = \"\"
728MACHINE = \"qemux86-64\" 710MACHINE = \"qemux86-64\"
729require conf/multilib.conf 711require conf/multilib.conf
730MULTILIBS = "multilib:lib32" 712MULTILIBS = "multilib:lib32"
@@ -917,15 +899,24 @@ INHERIT += "base-do-configure-modified"
917""", 899""",
918expected_sametmp_output, expected_difftmp_output) 900expected_sametmp_output, expected_difftmp_output)
919 901
920@OETestTag("yocto-mirrors") 902class SStateCheckObjectPresence(SStateBase):
921class 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")
955class 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("""
978MACHINE = "{}" 960MACHINE = "{}"
979BB_HASHSERVE_UPSTREAM = "hashserv.yocto.io:8687" 961BB_HASHSERVE_UPSTREAM = "hashserv.yoctoproject.org:8686"
980SSTATE_MIRRORS ?= "file://.* http://cdn.jsdelivr.net/yocto/sstate/all/PATH;downloadfilename=PATH" 962SSTATE_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
7import shutil
8import subprocess
9import tempfile
10from types import SimpleNamespace
11
12import oe.path
13from oeqa.selftest.case import OESelftestTestCase
14from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars
15
16class 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("""
58TOOLCHAIN = "gcc"
59TOOLCHAIN: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
8from oeqa.selftest.case import OESelftestTestCase
9from oeqa.utils.commands import bitbake, runqemu, get_bb_var, get_bb_vars, runCmd
10from oeqa.core.decorator.data import skipIfNotArch, skipIfNotBuildArch
11from oeqa.core.decorator import OETestTag
12
13uboot_boot_patterns = {
14 'search_reached_prompt': "stop autoboot",
15 'search_login_succeeded': "=>",
16 'search_cmd_finished': "=>"
17 }
18
19
20class 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("""
30QB_DEFAULT_BIOS = "u-boot.bin"
31PREFERRED_PROVIDER_virtual/bootloader = "u-boot"
32QEMU_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("""
64QEMU_USE_KVM = "1"
65
66# Using u-boot in EFI mode, need ESP partition for grub/systemd-boot/kernel etc
67IMAGE_FSTYPES:pn-core-image-minimal:append = " wic"
68
69# easiest to follow genericarm64 setup with wks file, initrd and EFI loader
70INITRAMFS_IMAGE = "core-image-initramfs-boot"
71EFI_PROVIDER = "${@bb.utils.contains("DISTRO_FEATURES", "systemd", "systemd-boot", "grub-efi", d)}"
72WKS_FILE = "genericarm64.wks.in"
73
74# use wic image with ESP for u-boot, not ext4
75QB_DEFAULT_FSTYPE = "wic"
76
77PREFERRED_PROVIDER_virtual/bootloader = "u-boot"
78QB_DEFAULT_BIOS = "u-boot.bin"
79
80# let u-boot or EFI loader load kernel from ESP
81QB_DEFAULT_KERNEL = "none"
82
83# virt pci, not scsi because support not in u-boot to find ESP
84QB_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
8from oeqa.selftest.case import OESelftestTestCase
9from oeqa.utils.commands import bitbake, runqemu, get_bb_var
10from oeqa.core.decorator.data import skipIfNotArch
11from oeqa.core.decorator import OETestTag
12import oe.types
13
14class 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
29EFI_PROVIDER = "systemd-boot"
30
31# image format must be wic, needs esp partition for firmware etc
32IMAGE_FSTYPES:pn-%s:append = " wic"
33WKS_FILE = "efi-uki-bootdisk.wks.in"
34
35# efi, uki and systemd features must be enabled
36INIT_MANAGER = "systemd"
37MACHINE_FEATURES:append = " efi"
38IMAGE_CLASSES:append:pn-core-image-minimal = " uki"
39
40# uki embeds also an initrd
41INITRAMFS_IMAGE = "core-image-minimal-initramfs"
42
43# runqemu must not load kernel separately, it's in the uki
44QB_KERNEL_ROOT = ""
45QB_DEFAULT_KERNEL = "none"
46
47# boot command line provided via uki, not via bootloader
48UKI_CMDLINE = "rootwait root=LABEL=root console=${KERNEL_CONSOLE}"
49
50# disable kvm, breaks boot
51QEMU_USE_KVM = ""
52
53IMAGE_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
89EFI_PROVIDER = "systemd-boot"
90
91# image format must be wic, needs esp partition for firmware etc
92IMAGE_FSTYPES:pn-core-image-base:append = " wic"
93WKS_FILE = "efi-uki-bootdisk.wks.in"
94
95# efi, uki and systemd features must be enabled
96MACHINE_FEATURES:append = " efi"
97IMAGE_CLASSES:append:pn-core-image-base = " uki"
98
99# uki embeds also an initrd, no systemd or udev
100INITRAMFS_IMAGE = "core-image-initramfs-boot"
101
102# runqemu must not load kernel separately, it's in the uki
103QB_KERNEL_ROOT = ""
104QB_DEFAULT_KERNEL = "none"
105
106# boot command line provided via uki, not via bootloader
107UKI_CMDLINE = "rootwait root=LABEL=root console=${KERNEL_CONSOLE}"
108
109# disable kvm, breaks boot
110QEMU_USE_KVM = ""
111
112IMAGE_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
12import sys 12import sys
13import unittest 13import unittest
14import hashlib 14import hashlib
15import subprocess
15 16
16from glob import glob 17from glob import glob
17from shutil import rmtree, copy 18from 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("""
446part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr 447part / --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path usr
447part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr 448part /usr --source rootfs --ondisk mmcblk0 --fstype=ext4 --rootfs-dir %s/usr
448part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr""" 449part /etc --source rootfs --ondisk mmcblk0 --fstype=ext4 --exclude-path bin/ --rootfs-dir %s/usr
449 % (rootfs_dir, rootfs_dir)) 450part /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
843class Wic2(WicTestCase): 951class 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 = """
1025do_image_wic[postfuncs] += "run_wic_cmd"
1026run_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 = """
1065do_image_wic[postfuncs] += "run_wic_cmd"
1066run_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'\ 1445EFI_PROVIDER = "systemd-boot"
1295 'MACHINE_FEATURES:append = " efi"\n' 1446
1447# image format must be wic, needs esp partition for firmware etc
1448IMAGE_FSTYPES:pn-core-image-base:append = " wic"
1449WKS_FILE = "test_efi_plugin.wks"
1450
1451# efi, uki and systemd features must be enabled
1452MACHINE_FEATURES:append = " efi"
1453IMAGE_CLASSES:append:pn-core-image-base = " uki"
1454
1455# uki embeds also an initrd, no systemd or udev
1456INITRAMFS_IMAGE = "core-image-initramfs-boot"
1457
1458# runqemu must not load kernel separately, it's in the uki
1459QB_KERNEL_ROOT = ""
1460QB_DEFAULT_KERNEL = "none"
1461
1462# boot command line provided via uki, not via bootloader
1463UKI_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 = """
1495INIT_MANAGER = "systemd"
1496EFI_PROVIDER = "systemd-boot"
1497
1498# image format must be wic, needs esp partition for firmware etc
1499IMAGE_FSTYPES:pn-core-image-base:append = " wic"
1500WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks"
1501
1502INITRAMFS_IMAGE = "core-image-initramfs-boot"
1503
1504# to configure runqemu
1505IMAGE_CLASSES += "qemuboot"
1506# u-boot efi firmware
1507QB_DEFAULT_BIOS = "u-boot.bin"
1508# need to use virtio, scsi not supported by u-boot by default
1509QB_DRIVE_TYPE = "/dev/vd"
1510
1511# disable kvm, breaks boot
1512QEMU_USE_KVM = ""
1513
1514IMAGE_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 = """
1538INIT_MANAGER = "systemd"
1539EFI_PROVIDER = "systemd-boot"
1540
1541# image format must be wic, needs esp partition for firmware etc
1542IMAGE_FSTYPES:pn-core-image-base:append = " wic"
1543WKS_FILE = "test_efi_plugin_plain_systemd-boot.wks"
1544
1545INITRAMFS_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,