]> git.ipfire.org Git - thirdparty/openembedded/openembedded-core-contrib.git/commitdiff
lib: oeqa: spdx: Add tests for extra options
authorJoshua Watt <jpewhacker@gmail.com>
Tue, 3 Dec 2024 22:37:10 +0000 (15:37 -0700)
committerRichard Purdie <richard.purdie@linuxfoundation.org>
Thu, 5 Dec 2024 11:55:08 +0000 (11:55 +0000)
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>
meta/lib/oe/sbom30.py
meta/lib/oeqa/selftest/cases/spdx.py

index 7d01715ce242d0b2509bb9f92075ab83ae1ff643..9a3b188dbbba4f71ae9b0503c1e9013160d80c72 100644 (file)
@@ -875,16 +875,11 @@ class ObjectSet(oe.spdx30.SHACLObjectSet):
             else:
                 missing_spdxids.add(spdxid)
 
-        bb.debug(1, "Linking...")
-        missing = self.link()
-        if missing != missing_spdxids:
-            bb.fatal(
-                f"Linked document doesn't match missing SPDX ID list. Got: {missing}\nExpected: {missing_spdxids}"
-            )
-
         self.doc.import_ = sorted(imports.values(), key=lambda e: e.externalSpdxId)
-
-        return missing_spdxids
+        bb.debug(1, "Linking...")
+        self.link()
+        self.missing_ids -= set(imports.keys())
+        return self.missing_ids
 
 
 def load_jsonld(d, path, required=False):
index 83840702199db8741bdcc7e2c7f30508682f9f44..9b35793d139330e2ae54d7b37ed9e4ec968c568f 100644 (file)
@@ -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):
@@ -73,8 +75,6 @@ class SPDX3CheckBase(object):
     """
 
     def check_spdx_file(self, filename):
-        import oe.spdx30
-
         self.assertExists(filename)
 
         # Read the file
@@ -86,13 +86,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:
@@ -120,11 +123,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),
             )
 
 
@@ -188,12 +197,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")