diff mbox series

[3/3] oeqa/selftest/devtool: add test for YOCTO# 14723

Message ID 20231012202535.2902235-3-chris.laplante@agilent.com
State New
Headers show
Series [1/3] kernel-yocto, devtool-source.bbclass: fix 'devtool modify' for kernels | expand

Commit Message

chris.laplante@agilent.com Oct. 12, 2023, 8:25 p.m. UTC
From: Chris Laplante <chris.laplante@agilent.com>

This relatively exhaustive test is designed to exercise the 'devtool
modify' workflow for kernel recipes, with a focus on SRC_URI override
branches.

Signed-off-by: Chris Laplante <chris.laplante@agilent.com>
---
 meta/lib/oeqa/selftest/cases/devtool.py | 132 ++++++++++++++++++++++++
 1 file changed, 132 insertions(+)
diff mbox series

Patch

diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index a5ef0329df..1072e23c21 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -2197,3 +2197,135 @@  class DevtoolUpgradeTests(DevtoolBase):
 
         #Step 4.5
         runCmd("grep %s %s" % (modconfopt, codeconfigfile))
+
+    def test_devtool_modify_kernel_overrides(self):
+        """
+        [YOCTO #14723]
+
+        Test that patches in SRC_URI overrides round-trip correctly through devtool modify, especially when
+        appends/prepends are present.
+        """
+        import typing
+
+        # Perform some initial setup
+        kernel_provider = self.td['PREFERRED_PROVIDER_virtual/kernel']
+        self.track_for_cleanup(self.workspacedir)
+        self.add_command_to_tearDown('bitbake -c clean %s' % kernel_provider)
+        self.add_command_to_tearDown('bitbake-layers remove-layer */workspace')
+
+        # Collect some information for later
+        machine = get_bb_var("MACHINE")
+        recipefile = get_bb_var('FILE', kernel_provider)
+        recipedir = os.path.dirname(recipefile)
+        res = re.search('recipes-.*', recipedir)
+        self.assertTrue(res, 'Unable to find recipe subdirectory')
+        recipesubdir = res[0]
+
+        new_file_contents = "A new file"
+
+        bitbake('%s -c clean' % kernel_provider)
+
+        # We are going to call 'devtool modify' multiple times in this test, so we use a context manager for the temp
+        # dir rather than call 'track_for_cleanup', since we want the tempdirs destroyed between sub-tests. Also it's
+        # easier and clearer this way.
+        with tempfile.TemporaryDirectory(prefix='devtoolqa') as tempdir:
+            runCmd('devtool modify %s -x %s' % (kernel_provider, tempdir))
+            self._check_src_repo(tempdir)
+
+            tags = runCmd('git tag --points-at HEAD', cwd=tempdir).output.strip().splitlines()
+            self.assertSetEqual(set(tags), {"devtool-base", "devtool-patched"})
+
+            # Construct a patch to add a file
+            runCmd(f'echo "{new_file_contents}" > devtool-new-file', cwd=tempdir)
+            runCmd('git add devtool-new-file', cwd=tempdir)
+            # Need to add the Upstream-Status otherwise the patch will be rejected next time we 'modify'.
+            runCmd('git commit -m "Add a new file\n\nUpstream-Status: Inappropriate [OE self-test specific]"', cwd=tempdir)
+            self.add_command_to_tearDown('rm -rf %s' % os.path.join(self.testlayer_path, recipesubdir))
+            runCmd('devtool finish %s meta-selftest' % kernel_provider)
+
+        # Check patch was created
+        patchfile = os.path.join(self.testlayer_path, recipesubdir, kernel_provider, "0001-Add-a-new-file.patch")
+        self.assertExists(patchfile, "Patch file doesn't exist")
+
+        # Check bbappend was created
+        appendfn = os.path.join(self.testlayer_path, recipesubdir, '%s_%%.bbappend' % kernel_provider)
+        self.assertExists(appendfn, "bbappend doesn't exist")
+
+        def _assert_new_file_presence(exists: bool):
+            file_path = os.path.join(tempdir, "devtool-new-file")
+            if exists:
+                with open(file_path, "r") as f:
+                    contents = f.read()
+                self.assertEqual(contents, f"{new_file_contents}\n")
+            else:
+                self.assertNotExists(file_path)
+
+        # Check that the patch round-trips
+        with tempfile.TemporaryDirectory(prefix='devtoolqa') as tempdir:
+            result = runCmd(f'devtool modify {kernel_provider} -x {tempdir}')
+            self._check_src_repo(tempdir)
+            _assert_new_file_presence(True)
+            runCmd(f'devtool reset {kernel_provider}')
+
+        def _modify_append_file(fn: typing.Callable[[str], str]):
+            with open(appendfn, "r+") as f:
+                contents = f.read()
+                contents = fn(contents)
+                f.seek(0)
+                f.write(contents)
+                f.truncate()
+
+        # Modify bbappend to conditionally apply the patch
+        _modify_append_file(lambda contents: contents.replace('SRC_URI += "', f'SRC_URI:append:{machine} = " '))
+
+        # Check that the patch still applies and that all branches/tags are created as expected
+        with tempfile.TemporaryDirectory(prefix='devtoolqa') as tempdir:
+            result = runCmd(f'devtool modify {kernel_provider} -x {tempdir}')
+            self._check_src_repo(tempdir)
+            _assert_new_file_presence(True)
+            runCmd(f'devtool reset {kernel_provider}')
+
+        # Change the bbappend such that the patch is applied for a non-active OVERRIDE
+        _modify_append_file(lambda contents: contents.replace(f'SRC_URI:append:{machine} = " ', f'SRC_URI:append:{machine}-fake = " '))
+
+        with tempfile.TemporaryDirectory(prefix='devtoolqa') as tempdir:
+            result = runCmd(f'devtool modify {kernel_provider} -x {tempdir}')
+            self._check_src_repo(tempdir)
+
+            # File shouldn't exist on this branch...
+            _assert_new_file_presence(False)
+            current_branch = runCmd('git symbolic-ref --short HEAD', cwd=tempdir).output.strip()
+            self.assertEqual(current_branch, "devtool")
+
+            tags = runCmd('git tag --points-at HEAD', cwd=tempdir).output.strip().splitlines()
+            self.assertSetEqual(set(tags), {"devtool-base", "devtool-patched"})
+
+            # It should exist on the 'devtool-override-${MACHINE}-fake' branch
+            runCmd(f'git checkout devtool-override-{machine}-fake', cwd=tempdir)
+            _assert_new_file_presence(True)
+            runCmd(f'devtool reset {kernel_provider}')
+
+        # Finally, change the bbappend such that the patch is applied to two different OVERRIDES.
+        # Would get two branches created, devtool-override-${MACHINE}-fake and devtool-override-${MACHINE}-fake2, with
+        # the patch applied. This tests that patch series generation is working for multiple overrides branches.
+        #
+        # The 'devtool' branch will not have the patch applied because there are no active OVERRIDES.
+        _modify_append_file(lambda contents: contents.replace(f'SRC_URI:append:{machine}-fake = " ', f'SRC_URI:append:{machine}-fake2 = " file://0001-Add-a-new-file.patch"\nSRC_URI:append:{machine}-fake = " '))
+        with tempfile.TemporaryDirectory(prefix='devtoolqa') as tempdir:
+            result = runCmd(f'devtool modify {kernel_provider} -x {tempdir}')
+            self._check_src_repo(tempdir)
+
+            # File shouldn't exist on this branch...
+            _assert_new_file_presence(False)
+            current_branch = runCmd('git symbolic-ref --short HEAD', cwd=tempdir).output.strip()
+            self.assertEqual(current_branch, "devtool")
+
+            tags = runCmd('git tag --points-at HEAD', cwd=tempdir).output.strip().splitlines()
+            self.assertSetEqual(set(tags), {"devtool-base", "devtool-patched"})
+
+            # It should exist on the 'devtool-override-${MACHINE}-fake' branch
+            for branch in [f"devtool-override-{machine}-fake", f"devtool-override-{machine}-fake2"]:
+                runCmd(f'git checkout {branch}', cwd=tempdir)
+                _assert_new_file_presence(True)
+
+            runCmd(f'devtool reset {kernel_provider}')