>From 4b9338d0083d1b79319a82e9e6858c553ea3111c Mon Sep 17 00:00:00 2001
From: Stefano Tondo <[email protected]>
Date: Mon, 16 Feb 2026 21:40:14 +0100
Subject: [OE-core][PATCH] oeqa/selftest: Add test for SPDX_CONCLUDED_LICENSE
 behavior

Add a selftest for the SPDX_CONCLUDED_LICENSE variable (introduced in
commit bb21c6a429) to verify the conditional behavior doesn't change
or break, as requested during review.

The test verifies three scenarios:
1. Global SPDX_CONCLUDED_LICENSE creates a hasConcludedLicense
   relationship on the package
2. Package-specific override (SPDX_CONCLUDED_LICENSE:<pkg>) takes
   precedence over the global value
3. When unset, no hasConcludedLicense relationship is added

Signed-off-by: Stefano Tondo <[email protected]>
---
 meta/lib/oeqa/selftest/cases/spdx.py | 112 +++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/meta/lib/oeqa/selftest/cases/spdx.py 
b/meta/lib/oeqa/selftest/cases/spdx.py
index 41ef52fce1..13386ef648 100644
--- a/meta/lib/oeqa/selftest/cases/spdx.py
+++ b/meta/lib/oeqa/selftest/cases/spdx.py
@@ -146,6 +146,118 @@ class SPDX30Check(SPDX3CheckBase, OESelftestTestCase):
             
"{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/packages/package-base-files.spdx.json",
         )

+    def test_concluded_license(self):
+        """
+        Test that SPDX_CONCLUDED_LICENSE creates a hasConcludedLicense
+        relationship with correct conditional behavior:
+
+        1. When SPDX_CONCLUDED_LICENSE is set, a hasConcludedLicense
+           relationship is added to the package
+        2. Package-specific overrides (SPDX_CONCLUDED_LICENSE:<pkg>)
+           take precedence over the global value
+        3. When unset, no hasConcludedLicense relationship is present
+        """
+        # Test 1: Verify hasConcludedLicense is created when variable is set
+        objset = self.check_recipe_spdx(
+            "base-files",
+            
"{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/packages/package-base-files.spdx.json",
+            extraconf="""\
+                SPDX_CONCLUDED_LICENSE = "MIT"
+                """,
+        )
+
+        # Find base-files package
+        for pkg in objset.foreach_type(oe.spdx30.software_Package):
+            if "base-files" in pkg.name:
+                break
+        else:
+            self.assertTrue(False, "Unable to find base-files package")
+
+        # Verify hasConcludedLicense relationship exists
+        for rel in objset.foreach_type(oe.spdx30.Relationship):
+            if (
+                rel.relationshipType
+                == oe.spdx30.RelationshipType.hasConcludedLicense
+                and any(pkg._id == f._id for f in rel.from_)
+            ):
+                self.assertGreater(
+                    len(rel.to),
+                    0,
+                    "hasConcludedLicense should reference a license",
+                )
+                break
+        else:
+            self.assertTrue(
+                False,
+                "SPDX_CONCLUDED_LICENSE should create hasConcludedLicense 
relationship",
+            )
+
+        # Test 2: Verify package-specific override takes precedence
+        objset = self.check_recipe_spdx(
+            "base-files",
+            
"{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/packages/package-base-files.spdx.json",
+            extraconf="""\
+                SPDX_CONCLUDED_LICENSE = "GPL-2.0-only"
+                SPDX_CONCLUDED_LICENSE:base-files = "BSD-3-Clause"
+                """,
+        )
+
+        for pkg in objset.foreach_type(oe.spdx30.software_Package):
+            if "base-files" in pkg.name:
+                break
+        else:
+            self.assertTrue(False, "Unable to find base-files package")
+
+        for rel in objset.foreach_type(oe.spdx30.Relationship):
+            if (
+                rel.relationshipType
+                == oe.spdx30.RelationshipType.hasConcludedLicense
+                and any(pkg._id == f._id for f in rel.from_)
+            ):
+                # Verify the concluded license is the package-specific one
+                for to_id in rel.to:
+                    for lic in objset.foreach_type(
+                        oe.spdx30.simplelicensing_AnyLicenseInfo
+                    ):
+                        if lic._id == to_id._id and hasattr(
+                            lic, "simplelicensing_licenseExpression"
+                        ):
+                            self.assertIn(
+                                "BSD-3-Clause",
+                                lic.simplelicensing_licenseExpression,
+                                "Package-specific override should take 
precedence",
+                            )
+                            break
+                break
+        else:
+            self.assertTrue(
+                False,
+                "Package-specific SPDX_CONCLUDED_LICENSE should create 
relationship",
+            )
+
+        # Test 3: Verify no hasConcludedLicense when variable is unset
+        objset = self.check_recipe_spdx(
+            "base-files",
+            
"{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/packages/package-base-files.spdx.json",
+        )
+
+        for pkg in objset.foreach_type(oe.spdx30.software_Package):
+            if "base-files" in pkg.name:
+                break
+        else:
+            self.assertTrue(False, "Unable to find base-files package")
+
+        for rel in objset.foreach_type(oe.spdx30.Relationship):
+            if (
+                rel.relationshipType
+                == oe.spdx30.RelationshipType.hasConcludedLicense
+                and any(pkg._id == f._id for f in rel.from_)
+            ):
+                self.assertTrue(
+                    False,
+                    "No hasConcludedLicense should exist when 
SPDX_CONCLUDED_LICENSE is unset",
+                )
+
     def test_gcc_include_source(self):
         objset = self.check_recipe_spdx(
             "gcc",
--
2.53.0
________________________________
From: Ross Burton <[email protected]>
Sent: Tuesday, February 10, 2026 13:19
To: [email protected] <[email protected]>
Cc: [email protected] 
<[email protected]>; Tondo, Stefano (ext) (SI B PRO AUT 
PD ZUG SW 2) <[email protected]>; Marko, Peter (FT D EU SK BFS1) 
<[email protected]>; Freihofer, Adrian (SI B PRO TI EAC CCP) 
<[email protected]>; Joshua Watt <[email protected]>
Subject: Re: [OE-core] [PATCH v4] spdx30_tasks: Add concluded license support 
with SPDX_CONCLUDED_LICENSE

Hi Stefano,

> On 7 Jan 2026, at 18:15, Stefano Tondo via lists.openembedded.org 
> <[email protected]> wrote:
> Add hasConcludedLicense relationship to SBOM packages with support for
> manual license conclusion override via SPDX_CONCLUDED_LICENSE variable.
>
> The concluded license represents the license determination after manual
> or external license analysis. This should be set manually in recipes or
> layers when:
>
> 1. Manual license review identifies differences from the declared LICENSE
> 2. External license scanning tools detect additional license information
> 3. Legal review concludes a different license applies
>
> The hasConcludedLicense relationship is ONLY added to the SBOM when
> SPDX_CONCLUDED_LICENSE is explicitly set. When unset or empty, no
> concluded license is included in the SBOM, correctly indicating that
> no license analysis was performed (per SPDX semantics).

Could you add a test case to oeqa to this, so that we know the conditional 
behaviour doesn’t change or break?

Thanks
Ross
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#231224): 
https://lists.openembedded.org/g/openembedded-core/message/231224
Mute This Topic: https://lists.openembedded.org/mt/117139043/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to