diff mbox series

[5/5] oe-selftest: devtool: Add multiconfig workflow tests

Message ID 20261023102215.4043005-6-anusmita.duttamazumder@arm.com
State Under Review
Headers show
Series devtool: Support multiconfig workflows and fix kernel extraction | expand

Commit Message

Anusmita Dutta Mazumder Oct. 23, 2026, 10:22 a.m. UTC
Cover modify, build, reset, update-recipe and finish with a
recipe available only in the selected multiconfig. Check
workspace isolation between configurations.

[YOCTO #13933]

Signed-off-by: Anusmita Dutta Mazumder <anusmita.duttamazumder@arm.com>
---
 meta/lib/oeqa/selftest/cases/devtool.py | 133 ++++++++++++++++++++++++
 1 file changed, 133 insertions(+)
diff mbox series

Patch

diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index b848623a6e..1b83ee16d4 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -1327,6 +1327,139 @@  class DevtoolModifyTests(DevtoolBase):
         self.assertExists(os.path.join(source_repo_gitsm_gitmodules, 'bitbake'), 'Submodule not found')
         self.assertExists(os.path.join(source_repo_gitsm_gitmodules, 'bitbake-gitsm-test1'), 'Submodule not found')
 
+class DevtoolMulticonfigTests(DevtoolBase):
+
+    def setUp(self):
+        super(DevtoolMulticonfigTests, self).setUp()
+        self.recipe = 'devtool-multiconfig-test'
+        self.target = 'mc:devtooltest:%s' % self.recipe
+        self.tempdir = tempfile.mkdtemp(prefix='devtoolqa')
+        self.track_for_cleanup(self.tempdir)
+        self.track_for_cleanup(self.workspacedir)
+        self.srctree = os.path.join(self.tempdir, 'source')
+        self.layerdir = os.path.join(self.tempdir, 'meta-devtool-multiconfig')
+
+        self.append_config('BBMULTICONFIG = "devtooltest"\n')
+        # Only this configuration satisfies the test recipe's compatibility
+        # restriction, regardless of the machine used by the selftest runner.
+        self.write_config('MACHINEOVERRIDES .= ":devtool-mc"\n'
+                          'TMPDIR = "${TOPDIR}/tmp-devtool-mc"\n', 'devtooltest')
+        self.add_command_to_tearDown('bitbake -c clean %s' % self.target)
+        self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+        self._create_temp_layer(self.layerdir, True, 'devtoolmulticonfig')
+
+        self.recipedir = os.path.join(self.layerdir, 'recipes-test', self.recipe)
+        filesdir = os.path.join(self.recipedir, self.recipe)
+        os.makedirs(filesdir)
+        archive = 'devtool-test-subdir.tar.gz'
+        shutil.copyfile(os.path.join(get_test_layer(), 'recipes-test', 'devtool',
+                                     'devtool-test-subdir', archive),
+                        os.path.join(filesdir, archive))
+        with open(os.path.join(self.recipedir, self.recipe + '_1.0.bb'), 'w') as f:
+            f.write('LICENSE = "MIT"\n'
+                    'LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"\n'
+                    'inherit allarch\n'
+                    'INHIBIT_DEFAULT_DEPS = "1"\n'
+                    'COMPATIBLE_MACHINE = "devtool-mc"\n'
+                    'SRC_URI = "file://devtool-test-subdir.tar.gz"\n'
+                    'S = "${UNPACKDIR}/devtool-test-subdir"\n'
+                    'B = "${WORKDIR}/build"\n'
+                    'do_compile() {\n'
+                    '    cp ${S}/testfile ${B}/testfile\n'
+                    '}\n'
+                    'do_install() {\n'
+                    '    install -Dm0644 ${B}/testfile ${D}${datadir}/${BPN}/testfile\n'
+                    '}\n'
+                    'FILES:${PN} = "${datadir}/${BPN}"\n')
+
+    def _modify(self, wildcard=False):
+        runCmd('devtool modify %s %s %s' %
+               ('--wildcard' if wildcard else '', self.target, self.srctree))
+        self._check_src_repo(self.srctree)
+        append = os.path.join(self.workspacedir, 'appends', 'multiconfig',
+                              'devtooltest', self.recipe + ('_%.bbappend' if wildcard else '_1.0.bbappend'))
+        self.assertExists(append)
+        self.assertEqual(get_bb_var('EXTERNALSRC', self.target), self.srctree)
+        self.assertIn(self.recipe, runCmd('devtool status').output)
+        return append
+
+    def test_devtool_multiconfig_modify_build_reset(self):
+        """Extract, build and clean a recipe unavailable in the default config."""
+        result = bitbake('%s -n' % self.recipe, ignore_status=True)
+        self.assertNotEqual(result.status, 0)
+        self.assertIn('incompatible with machine', result.output)
+
+        append = self._modify()
+        with open(os.path.join(self.srctree, 'testfile'), 'a') as f:
+            f.write('\nmulticonfig workspace change\n')
+        runCmd('devtool build %s' % self.target)
+        builddir = get_bb_var('B', self.target)
+        with open(os.path.join(builddir, 'testfile')) as f:
+            self.assertIn('multiconfig workspace change', f.read())
+
+        # Do not use --no-clean: cleaning must retain the multiconfig prefix.
+        runCmd('devtool reset %s' % self.target)
+        self.assertNotExists(append)
+        self.assertNotIn(self.recipe, runCmd('devtool status').output)
+        self.assertFalse(get_bb_var('EXTERNALSRC', self.target))
+
+    def test_devtool_multiconfig_update_finish(self):
+        """Update and finish using qualified targets and bare workspace keys."""
+        append = self._modify()
+        for command, marker in [('update-recipe', 'update'), ('finish', 'finish')]:
+            with open(os.path.join(self.srctree, 'testfile'), 'a') as f:
+                f.write('\nmulticonfig %s change\n' % marker)
+            runCmd('git commit -am "Multiconfig %s change" '
+                   '-m "Upstream-Status: Inappropriate [selftest]"' % marker,
+                   cwd=self.srctree)
+            destination = self.layerdir if command == 'finish' else ''
+            runCmd('devtool %s %s %s' % (command, self.target, destination))
+            patches = glob.glob(os.path.join(self.recipedir, self.recipe, '*.patch'))
+            self.assertTrue(patches, 'No patches written by %s' % command)
+            contents = ''
+            for patch in patches:
+                with open(patch) as f:
+                    contents += f.read()
+            self.assertIn('multiconfig %s change' % marker, contents)
+            if command == 'update-recipe':
+                self.assertExists(append)
+                self.assertIn(self.recipe, runCmd('devtool status').output)
+
+        self.assertNotExists(append)
+        self.assertNotIn(self.recipe, runCmd('devtool status').output)
+        self.assertFalse(get_bb_var('EXTERNALSRC', self.target))
+        bitbake('%s -c patch' % self.target)
+        with open(os.path.join(get_bb_var('S', self.target), 'testfile')) as f:
+            contents = f.read()
+        self.assertIn('multiconfig update change', contents)
+        self.assertIn('multiconfig finish change', contents)
+
+    def test_devtool_multiconfig_workspace_isolation(self):
+        """A wildcard workspace append must affect only its configuration."""
+        self.append_config('COMPATIBLE_MACHINE:pn-%s = ".*"\n'
+                           'BBMULTICONFIG += "devtoolother"\n' % self.recipe)
+        self.write_config('TMPDIR = "${TOPDIR}/tmp-devtool-other"\n', 'devtoolother')
+        other_target = 'mc:devtoolother:%s' % self.recipe
+        append = self._modify(wildcard=True)
+        self.assertFalse(get_bb_var('EXTERNALSRC', self.recipe))
+        self.assertFalse(get_bb_var('EXTERNALSRC', other_target))
+
+        result = runCmd('devtool modify -n %s %s' % (self.recipe, self.srctree),
+                        ignore_status=True)
+        self.assertNotEqual(result.status, 0)
+        self.assertIn('already in your workspace', result.output)
+        self.assertExists(append)
+        runCmd('devtool reset %s' % self.target)
+        self.assertNotExists(append)
+
+        # The default configuration's append must likewise stay out of MCs.
+        runCmd('devtool modify -n %s %s' % (self.recipe, self.srctree))
+        self.assertEqual(get_bb_var('EXTERNALSRC', self.recipe), self.srctree)
+        self.assertFalse(get_bb_var('EXTERNALSRC', self.target))
+        self.assertFalse(get_bb_var('EXTERNALSRC', other_target))
+        runCmd('devtool reset %s' % self.recipe)
+
+
 class DevtoolUpdateTests(DevtoolBase):
 
     def test_devtool_update_recipe(self):