From: Joshua Watt Date: Tue, 3 Dec 2024 22:37:10 +0000 (-0700) Subject: lib: oeqa: spdx: Add tests for extra options X-Git-Tag: yocto-5.2~1096 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=14f534f15f7fe6362723d7f064d39783c5bd758f;p=thirdparty%2Fopenembedded%2Fopenembedded-core-contrib.git lib: oeqa: spdx: Add tests for extra options 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 Signed-off-by: Mathieu Dubois-Briand Signed-off-by: Richard Purdie --- diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py index 7d01715ce24..9a3b188dbbb 100644 --- a/meta/lib/oe/sbom30.py +++ b/meta/lib/oe/sbom30.py @@ -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): diff --git a/meta/lib/oeqa/selftest/cases/spdx.py b/meta/lib/oeqa/selftest/cases/spdx.py index 83840702199..9b35793d139 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): @@ -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")