diff mbox series

[scarthgap,v2,09/11] lib: oeqa: spdx: Add tests for extra options

Message ID 20251020070952.942165-10-kamel.bouhara@bootlin.com
State New
Headers show
Series backport: SPDX 3.0 support to Scarthgap | expand

Commit Message

Kamel Bouhara Oct. 20, 2025, 7:09 a.m. UTC
From: Joshua Watt <jpewhacker@gmail.com>

Adds a test for several of the extra options provided by the SPDX
classes. In particular, these are the options that can produce
non-reproducible results, so are not enabled by default in OE core. This
test takes care to configure the build so that the tests do run in a
reproducible manner so that pre-built test objects can be pulled from
sstate

Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit 14f534f15f7fe6362723d7f064d39783c5bd758f)
Signed-off-by: Kamel Bouhara (Schneider Electric) <kamel.bouhara@bootlin.com>
---
 meta/lib/oeqa/selftest/cases/spdx.py | 118 +++++++++++++++++++++++----
 1 file changed, 104 insertions(+), 14 deletions(-)
diff mbox series

Patch

diff --git a/meta/lib/oeqa/selftest/cases/spdx.py b/meta/lib/oeqa/selftest/cases/spdx.py
index db124f4b93..c279ea162b 100644
--- a/meta/lib/oeqa/selftest/cases/spdx.py
+++ b/meta/lib/oeqa/selftest/cases/spdx.py
@@ -7,9 +7,11 @@ 
 import json
 import os
 import textwrap
+import hashlib
 from pathlib import Path
 from oeqa.selftest.case import OESelftestTestCase
 from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runCmd
+import oe.spdx30
 
 
 class SPDX22Check(OESelftestTestCase):
@@ -72,8 +74,6 @@  class SPDX3CheckBase(object):
     """
 
     def check_spdx_file(self, filename):
-        import oe.spdx30
-
         self.assertExists(filename)
 
         # Read the file
@@ -85,13 +85,16 @@  class SPDX3CheckBase(object):
         return objset
 
     def check_recipe_spdx(self, target_name, spdx_path, *, task=None, extraconf=""):
-        config = textwrap.dedent(
-            f"""\
-            INHERIT:remove = "create-spdx"
-            INHERIT += "{self.SPDX_CLASS}"
-            {extraconf}
-            """
+        config = (
+            textwrap.dedent(
+                f"""\
+                INHERIT:remove = "create-spdx"
+                INHERIT += "{self.SPDX_CLASS}"
+                """
+            )
+            + textwrap.dedent(extraconf)
         )
+
         self.write_config(config)
 
         if task:
@@ -119,11 +122,17 @@  class SPDX3CheckBase(object):
         return self.check_spdx_file(filename)
 
     def check_objset_missing_ids(self, objset):
-        if objset.missing_ids:
+        for o in objset.foreach_type(oe.spdx30.SpdxDocument):
+            doc = o
+            break
+        else:
+            self.assertTrue(False, "Unable to find SpdxDocument")
+
+        missing_ids = objset.missing_ids - set(i.externalSpdxId for i in doc.import_)
+        if missing_ids:
             self.assertTrue(
                 False,
-                "The following SPDXIDs are unresolved:\n  "
-                + "\n  ".join(objset.missing_ids),
+                "The following SPDXIDs are unresolved:\n  " + "\n  ".join(missing_ids),
             )
 
 
@@ -187,12 +196,93 @@  class SPDX30Check(SPDX3CheckBase, OESelftestTestCase):
         objset = self.check_recipe_spdx(
             "baremetal-helloworld",
             "{DEPLOY_DIR_IMAGE}/baremetal-helloworld-image-{MACHINE}.spdx.json",
-            extraconf=textwrap.dedent(
-                """\
+            extraconf="""\
                 TCLIBC = "baremetal"
+                """,
+        )
+
+        # Document should be fully linked
+        self.check_objset_missing_ids(objset)
+
+    def test_extra_opts(self):
+        HOST_SPDXID = "http://foo.bar/spdx/bar2"
+
+        EXTRACONF = textwrap.dedent(
+            f"""\
+            SPDX_INVOKED_BY_name = "CI Tool"
+            SPDX_INVOKED_BY_type = "software"
+
+            SPDX_ON_BEHALF_OF_name = "John Doe"
+            SPDX_ON_BEHALF_OF_type = "person"
+            SPDX_ON_BEHALF_OF_id_email = "John.Doe@noreply.com"
+
+            SPDX_PACKAGE_SUPPLIER_name = "ACME Embedded Widgets"
+            SPDX_PACKAGE_SUPPLIER_type = "organization"
+
+            SPDX_AUTHORS += "authorA"
+            SPDX_AUTHORS_authorA_ref = "SPDX_ON_BEHALF_OF"
+
+            SPDX_BUILD_HOST = "host"
+
+            SPDX_IMPORTS += "host"
+            SPDX_IMPORTS_host_spdxid = "{HOST_SPDXID}"
+
+            SPDX_INCLUDE_BUILD_VARIABLES = "1"
+            SPDX_INCLUDE_BITBAKE_PARENT_BUILD = "1"
+            SPDX_INCLUDE_TIMESTAMPS = "1"
+
+            SPDX_PRETTY = "1"
+            """
+        )
+        extraconf_hash = hashlib.sha1(EXTRACONF.encode("utf-8")).hexdigest()
+
+        objset = self.check_recipe_spdx(
+            "core-image-minimal",
+            "{DEPLOY_DIR_IMAGE}/core-image-minimal-{MACHINE}.rootfs.spdx.json",
+            # Many SPDX variables do not trigger a rebuild, since they are
+            # intended to record information at the time of the build. As such,
+            # the extra configuration alone may not trigger a rebuild, and even
+            # if it does, the task hash won't necessarily be unique. In order
+            # to make sure rebuilds happen, but still allow these test objects
+            # to be pulled from sstate (e.g. remain reproducible), change the
+            # namespace prefix to include the hash of the extra configuration
+            extraconf=textwrap.dedent(
+                f"""\
+                SPDX_NAMESPACE_PREFIX = "http://spdx.org/spdxdocs/{extraconf_hash}"
                 """
-            ),
+            )
+            + EXTRACONF,
         )
 
         # Document should be fully linked
         self.check_objset_missing_ids(objset)
+
+        for o in objset.foreach_type(oe.spdx30.SoftwareAgent):
+            if o.name == "CI Tool":
+                break
+        else:
+            self.assertTrue(False, "Unable to find software tool")
+
+        for o in objset.foreach_type(oe.spdx30.Person):
+            if o.name == "John Doe":
+                break
+        else:
+            self.assertTrue(False, "Unable to find person")
+
+        for o in objset.foreach_type(oe.spdx30.Organization):
+            if o.name == "ACME Embedded Widgets":
+                break
+        else:
+            self.assertTrue(False, "Unable to find organization")
+
+        for o in objset.foreach_type(oe.spdx30.SpdxDocument):
+            doc = o
+            break
+        else:
+            self.assertTrue(False, "Unable to find SpdxDocument")
+
+        for i in doc.import_:
+            if i.externalSpdxId == HOST_SPDXID:
+                break
+        else:
+            self.assertTrue(False, "Unable to find imported Host SpdxID")