diff options
author | Ian Ling <ian@iancaling.com> | 2021-06-15 14:59:52 -0700 |
---|---|---|
committer | Ian Ling <ian@iancaling.com> | 2021-06-16 11:28:39 -0700 |
commit | 9b060e5ac8c4614be5fd5f5e14561e8c4dff9ef2 (patch) | |
tree | 82ad0ddae5914e167ababa0188d4352320d8ab87 | |
parent | 8574b91809e949442fa0cbf3174d9ae83cd69f60 (diff) | |
download | spdx-tools-9b060e5ac8c4614be5fd5f5e14561e8c4dff9ef2.tar.gz |
Add relationship filter function
Signed-off-by: Ian Ling <ian@iancaling.com>
-rw-r--r-- | spdxlib/described_elements.go | 104 | ||||
-rw-r--r-- | spdxlib/described_elements_test.go | 56 | ||||
-rw-r--r-- | spdxlib/documents.go | 66 | ||||
-rw-r--r-- | spdxlib/documents_test.go | 185 | ||||
-rw-r--r-- | spdxlib/element_ids.go | 15 | ||||
-rw-r--r-- | spdxlib/element_ids_test.go | 16 | ||||
-rw-r--r-- | spdxlib/relationships.go | 31 | ||||
-rw-r--r-- | spdxlib/relationships_test.go | 145 |
8 files changed, 496 insertions, 122 deletions
diff --git a/spdxlib/described_elements.go b/spdxlib/described_elements.go index 6158f88..e8373da 100644 --- a/spdxlib/described_elements.go +++ b/spdxlib/described_elements.go @@ -5,8 +5,6 @@ package spdxlib import ( "fmt" - "sort" - "github.com/spdx/tools-golang/spdx" ) @@ -14,8 +12,7 @@ import ( // in this Document that it "describes," according to SPDX rules: // - If the document has only one Package, its ID is returned. // - If the document has 2+ Packages, it returns the IDs of those that have -// a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT. If no -// - +// a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT. func GetDescribedPackageIDs2_1(doc *spdx.Document2_1) ([]spdx.ElementID, error) { // if nil Packages map or zero packages in it, return empty slice if doc.Packages == nil { @@ -37,40 +34,28 @@ func GetDescribedPackageIDs2_1(doc *spdx.Document2_1) ([]spdx.ElementID, error) if doc.Relationships == nil { return nil, fmt.Errorf("multiple Packages in Document but Relationships slice is nil") } - // collect IDs as strings so we can sort them easily - eIDStrs := []string{} - for _, rln := range doc.Relationships { - if rln.Relationship == "DESCRIBES" && rln.RefA == spdx.MakeDocElementID("", "DOCUMENT") { - // confirm RefB is actually a package in this document - if _, ok := doc.Packages[rln.RefB.ElementRefID]; !ok { - // if it's an unpackaged file, that's valid (no error) but don't return it - if _, ok2 := doc.UnpackagedFiles[rln.RefB.ElementRefID]; !ok2 { - return nil, fmt.Errorf("Document DESCRIBES %s but no such Package or unpackaged File", string(rln.RefB.ElementRefID)) - } - } - eIDStrs = append(eIDStrs, string(rln.RefB.ElementRefID)) - } - if rln.Relationship == "DESCRIBED_BY" && rln.RefB == spdx.MakeDocElementID("", "DOCUMENT") { - // confirm RefA is actually a package in this document - // if it's an unpackaged file, that's valid (no error) but don't return it - if _, ok := doc.Packages[rln.RefA.ElementRefID]; !ok { - // if it's an unpackaged file, that's valid (no error) but don't return it - if _, ok2 := doc.UnpackagedFiles[rln.RefA.ElementRefID]; !ok2 { - return nil, fmt.Errorf("%s DESCRIBED_BY Document but no such Package or unpackaged File", string(rln.RefA.ElementRefID)) - } - } - eIDStrs = append(eIDStrs, string(rln.RefA.ElementRefID)) + + eIDs, err := FilterRelationships2_1(doc, func(relationship *spdx.Relationship2_1) *spdx.ElementID { + refDocument := spdx.MakeDocElementID("", "DOCUMENT") + + if relationship.Relationship == "DESCRIBES" && relationship.RefA == refDocument { + return &relationship.RefB.ElementRefID + } else if relationship.Relationship == "DESCRIBED_BY" && relationship.RefB == refDocument { + return &relationship.RefA.ElementRefID } + + return nil + }) + if err != nil { + return nil, err } - if len(eIDStrs) == 0 { + + if len(eIDs) == 0 { return nil, fmt.Errorf("no DESCRIBES or DESCRIBED_BY relationships found for this Document") } - // sort them, convert back to ElementIDs and return - sort.Strings(eIDStrs) - eIDs := []spdx.ElementID{} - for _, eIDStr := range eIDStrs { - eIDs = append(eIDs, spdx.ElementID(eIDStr)) - } + + eIDs = SortElementIDs(eIDs) + return eIDs, nil } @@ -78,8 +63,7 @@ func GetDescribedPackageIDs2_1(doc *spdx.Document2_1) ([]spdx.ElementID, error) // in this Document that it "describes," according to SPDX rules: // - If the document has only one Package, its ID is returned. // - If the document has 2+ Packages, it returns the IDs of those that have -// a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT. If no -// - +// a DESCRIBES (or DESCRIBED_BY) relationship to this DOCUMENT. func GetDescribedPackageIDs2_2(doc *spdx.Document2_2) ([]spdx.ElementID, error) { // if nil Packages map or zero packages in it, return empty slice if doc.Packages == nil { @@ -101,39 +85,27 @@ func GetDescribedPackageIDs2_2(doc *spdx.Document2_2) ([]spdx.ElementID, error) if doc.Relationships == nil { return nil, fmt.Errorf("multiple Packages in Document but Relationships slice is nil") } - // collect IDs as strings so we can sort them easily - eIDStrs := []string{} - for _, rln := range doc.Relationships { - if rln.Relationship == "DESCRIBES" && rln.RefA == spdx.MakeDocElementID("", "DOCUMENT") { - // confirm RefB is actually a package in this document - if _, ok := doc.Packages[rln.RefB.ElementRefID]; !ok { - // if it's an unpackaged file, that's valid (no error) but don't return it - if _, ok2 := doc.UnpackagedFiles[rln.RefB.ElementRefID]; !ok2 { - return nil, fmt.Errorf("Document DESCRIBES %s but no such Package or unpackaged File", string(rln.RefB.ElementRefID)) - } - } - eIDStrs = append(eIDStrs, string(rln.RefB.ElementRefID)) - } - if rln.Relationship == "DESCRIBED_BY" && rln.RefB == spdx.MakeDocElementID("", "DOCUMENT") { - // confirm RefA is actually a package in this document - // if it's an unpackaged file, that's valid (no error) but don't return it - if _, ok := doc.Packages[rln.RefA.ElementRefID]; !ok { - // if it's an unpackaged file, that's valid (no error) but don't return it - if _, ok2 := doc.UnpackagedFiles[rln.RefA.ElementRefID]; !ok2 { - return nil, fmt.Errorf("%s DESCRIBED_BY Document but no such Package or unpackaged File", string(rln.RefA.ElementRefID)) - } - } - eIDStrs = append(eIDStrs, string(rln.RefA.ElementRefID)) + + eIDs, err := FilterRelationships2_2(doc, func(relationship *spdx.Relationship2_2) *spdx.ElementID { + refDocument := spdx.MakeDocElementID("", "DOCUMENT") + + if relationship.Relationship == "DESCRIBES" && relationship.RefA == refDocument { + return &relationship.RefB.ElementRefID + } else if relationship.Relationship == "DESCRIBED_BY" && relationship.RefB == refDocument { + return &relationship.RefA.ElementRefID } + + return nil + }) + if err != nil { + return nil, err } - if len(eIDStrs) == 0 { + + if len(eIDs) == 0 { return nil, fmt.Errorf("no DESCRIBES or DESCRIBED_BY relationships found for this Document") } - // sort them, convert back to ElementIDs and return - sort.Strings(eIDStrs) - eIDs := []spdx.ElementID{} - for _, eIDStr := range eIDStrs { - eIDs = append(eIDs, spdx.ElementID(eIDStr)) - } + + eIDs = SortElementIDs(eIDs) + return eIDs, nil } diff --git a/spdxlib/described_elements_test.go b/spdxlib/described_elements_test.go index 4ba99da..32fa726 100644 --- a/spdxlib/described_elements_test.go +++ b/spdxlib/described_elements_test.go @@ -183,34 +183,6 @@ func Test2_1FailsToGetDescribedPackagesIfNilMap(t *testing.T) { } } -func Test2_1FailsToGetDescribedPackagesIfRelationshipForNonexistantPackageID(t *testing.T) { - // set up document and multiple packages, but no DESCRIBES relationships - doc := &spdx.Document2_1{ - CreationInfo: &spdx.CreationInfo2_1{ - SPDXVersion: "SPDX-2.1", - DataLicense: "CC0-1.0", - SPDXIdentifier: spdx.ElementID("DOCUMENT"), - }, - Packages: map[spdx.ElementID]*spdx.Package2_1{ - spdx.ElementID("p1"): &spdx.Package2_1{PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, - spdx.ElementID("p2"): &spdx.Package2_1{PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, - }, - Relationships: []*spdx.Relationship2_1{ - // different relationship - &spdx.Relationship2_1{ - RefA: spdx.MakeDocElementID("", "DOCUMENT"), - RefB: spdx.MakeDocElementID("", "p17"), - Relationship: "DESCRIBES", - }, - }, - } - - _, err := GetDescribedPackageIDs2_1(doc) - if err == nil { - t.Fatalf("expected non-nil error, got nil") - } -} - // ===== 2.2 tests ===== func Test2_2CanGetIDsOfDescribedPackages(t *testing.T) { @@ -385,31 +357,3 @@ func Test2_2FailsToGetDescribedPackagesIfNilMap(t *testing.T) { t.Fatalf("expected non-nil error, got nil") } } - -func Test2_2FailsToGetDescribedPackagesIfRelationshipForNonexistantPackageID(t *testing.T) { - // set up document and multiple packages, but no DESCRIBES relationships - doc := &spdx.Document2_2{ - CreationInfo: &spdx.CreationInfo2_2{ - SPDXVersion: "SPDX-2.2", - DataLicense: "CC0-1.0", - SPDXIdentifier: spdx.ElementID("DOCUMENT"), - }, - Packages: map[spdx.ElementID]*spdx.Package2_2{ - spdx.ElementID("p1"): &spdx.Package2_2{PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, - spdx.ElementID("p2"): &spdx.Package2_2{PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, - }, - Relationships: []*spdx.Relationship2_2{ - // different relationship - &spdx.Relationship2_2{ - RefA: spdx.MakeDocElementID("", "DOCUMENT"), - RefB: spdx.MakeDocElementID("", "p17"), - Relationship: "DESCRIBES", - }, - }, - } - - _, err := GetDescribedPackageIDs2_2(doc) - if err == nil { - t.Fatalf("expected non-nil error, got nil") - } -} diff --git a/spdxlib/documents.go b/spdxlib/documents.go new file mode 100644 index 0000000..0f90a02 --- /dev/null +++ b/spdxlib/documents.go @@ -0,0 +1,66 @@ +package spdxlib + +import ( + "fmt" + "github.com/spdx/tools-golang/spdx" +) + +// ValidateDocument2_1 returns an error if the Document is found to be invalid, or nil if the Document is valid. +// Currently, this only verifies that all Element IDs mentioned in Relationships exist in the Document as either a +// Package or an UnpackagedFile. +func ValidateDocument2_1(doc *spdx.Document2_1) error { + // cache a map of valid package IDs for quick lookups + validElementIDs := make(map[spdx.ElementID]bool) + for _, docPackage := range doc.Packages { + validElementIDs[docPackage.PackageSPDXIdentifier] = true + } + + for _, unpackagedFile := range doc.UnpackagedFiles { + validElementIDs[unpackagedFile.FileSPDXIdentifier] = true + } + + // add the Document element ID + validElementIDs[spdx.MakeDocElementID("", "DOCUMENT").ElementRefID] = true + + for _, relationship := range doc.Relationships { + if !validElementIDs[relationship.RefA.ElementRefID] { + return fmt.Errorf("%s used in relationship but no such package exists", string(relationship.RefA.ElementRefID)) + } + + if !validElementIDs[relationship.RefB.ElementRefID] { + return fmt.Errorf("%s used in relationship but no such package exists", string(relationship.RefB.ElementRefID)) + } + } + + return nil +} + +// ValidateDocument2_2 returns an error if the Document is found to be invalid, or nil if the Document is valid. +// Currently, this only verifies that all Element IDs mentioned in Relationships exist in the Document as either a +// Package or an UnpackagedFile. +func ValidateDocument2_2(doc *spdx.Document2_2) error { + // cache a map of package IDs for quick lookups + validElementIDs := make(map[spdx.ElementID]bool) + for _, docPackage := range doc.Packages { + validElementIDs[docPackage.PackageSPDXIdentifier] = true + } + + for _, unpackagedFile := range doc.UnpackagedFiles { + validElementIDs[unpackagedFile.FileSPDXIdentifier] = true + } + + // add the Document element ID + validElementIDs[spdx.MakeDocElementID("", "DOCUMENT").ElementRefID] = true + + for _, relationship := range doc.Relationships { + if !validElementIDs[relationship.RefA.ElementRefID] { + return fmt.Errorf("%s used in relationship but no such package exists", string(relationship.RefA.ElementRefID)) + } + + if !validElementIDs[relationship.RefB.ElementRefID] { + return fmt.Errorf("%s used in relationship but no such package exists", string(relationship.RefB.ElementRefID)) + } + } + + return nil +} diff --git a/spdxlib/documents_test.go b/spdxlib/documents_test.go new file mode 100644 index 0000000..60a39b9 --- /dev/null +++ b/spdxlib/documents_test.go @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +package spdxlib + +import ( + "testing" + + "github.com/spdx/tools-golang/spdx" +) + +// ===== 2.1 tests ===== + +func Test2_1ValidDocumentPassesValidation(t *testing.T) { + // set up document and some packages and relationships + doc := &spdx.Document2_1{ + CreationInfo: &spdx.CreationInfo2_1{ + SPDXVersion: "SPDX-2.1", + DataLicense: "CC0-1.0", + SPDXIdentifier: spdx.ElementID("DOCUMENT"), + }, + Packages: map[spdx.ElementID]*spdx.Package2_1{ + spdx.ElementID("p1"): {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, + spdx.ElementID("p2"): {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, + spdx.ElementID("p3"): {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, + spdx.ElementID("p4"): {PackageName: "pkg4", PackageSPDXIdentifier: "p4"}, + spdx.ElementID("p5"): {PackageName: "pkg5", PackageSPDXIdentifier: "p5"}, + }, + Relationships: []*spdx.Relationship2_1{ + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p1"), + Relationship: "DESCRIBES", + }, + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p5"), + Relationship: "DESCRIBES", + }, + // inverse relationship -- should also get detected + { + RefA: spdx.MakeDocElementID("", "p4"), + RefB: spdx.MakeDocElementID("", "DOCUMENT"), + Relationship: "DESCRIBED_BY", + }, + // different relationship + { + RefA: spdx.MakeDocElementID("", "p1"), + RefB: spdx.MakeDocElementID("", "p2"), + Relationship: "DEPENDS_ON", + }, + }, + } + + err := ValidateDocument2_1(doc) + if err != nil { + t.Fatalf("expected nil error, got: %s", err.Error()) + } +} + +func Test2_1InvalidDocumentFailsValidation(t *testing.T) { + // set up document and some packages and relationships + doc := &spdx.Document2_1{ + CreationInfo: &spdx.CreationInfo2_1{ + SPDXVersion: "SPDX-2.1", + DataLicense: "CC0-1.0", + SPDXIdentifier: spdx.ElementID("DOCUMENT"), + }, + Packages: map[spdx.ElementID]*spdx.Package2_1{ + spdx.ElementID("p1"): {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, + spdx.ElementID("p2"): {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, + spdx.ElementID("p3"): {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, + }, + Relationships: []*spdx.Relationship2_1{ + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p1"), + Relationship: "DESCRIBES", + }, + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p2"), + Relationship: "DESCRIBES", + }, + // invalid ID p99 + { + RefA: spdx.MakeDocElementID("", "p1"), + RefB: spdx.MakeDocElementID("", "p99"), + Relationship: "DEPENDS_ON", + }, + }, + } + + err := ValidateDocument2_1(doc) + if err == nil { + t.Fatalf("expected non-nil error, got nil") + } +} + +// ===== 2.2 tests ===== + +func Test2_2ValidDocumentPassesValidation(t *testing.T) { + // set up document and some packages and relationships + doc := &spdx.Document2_2{ + CreationInfo: &spdx.CreationInfo2_2{ + SPDXVersion: "SPDX-2.2", + DataLicense: "CC0-1.0", + SPDXIdentifier: spdx.ElementID("DOCUMENT"), + }, + Packages: map[spdx.ElementID]*spdx.Package2_2{ + spdx.ElementID("p1"): {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, + spdx.ElementID("p2"): {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, + spdx.ElementID("p3"): {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, + spdx.ElementID("p4"): {PackageName: "pkg4", PackageSPDXIdentifier: "p4"}, + spdx.ElementID("p5"): {PackageName: "pkg5", PackageSPDXIdentifier: "p5"}, + }, + Relationships: []*spdx.Relationship2_2{ + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p1"), + Relationship: "DESCRIBES", + }, + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p5"), + Relationship: "DESCRIBES", + }, + // inverse relationship -- should also get detected + { + RefA: spdx.MakeDocElementID("", "p4"), + RefB: spdx.MakeDocElementID("", "DOCUMENT"), + Relationship: "DESCRIBED_BY", + }, + // different relationship + { + RefA: spdx.MakeDocElementID("", "p1"), + RefB: spdx.MakeDocElementID("", "p2"), + Relationship: "DEPENDS_ON", + }, + }, + } + + err := ValidateDocument2_2(doc) + if err != nil { + t.Fatalf("expected nil error, got: %s", err.Error()) + } +} + +func Test2_2InvalidDocumentFailsValidation(t *testing.T) { + // set up document and some packages and relationships + doc := &spdx.Document2_2{ + CreationInfo: &spdx.CreationInfo2_2{ + SPDXVersion: "SPDX-2.2", + DataLicense: "CC0-1.0", + SPDXIdentifier: spdx.ElementID("DOCUMENT"), + }, + Packages: map[spdx.ElementID]*spdx.Package2_2{ + spdx.ElementID("p1"): {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, + spdx.ElementID("p2"): {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, + spdx.ElementID("p3"): {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, + }, + Relationships: []*spdx.Relationship2_2{ + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p1"), + Relationship: "DESCRIBES", + }, + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p5"), + Relationship: "DESCRIBES", + }, + // invalid ID p99 + { + RefA: spdx.MakeDocElementID("", "p1"), + RefB: spdx.MakeDocElementID("", "p99"), + Relationship: "DEPENDS_ON", + }, + }, + } + + err := ValidateDocument2_2(doc) + if err == nil { + t.Fatalf("expected non-nil error, got nil") + } +} diff --git a/spdxlib/element_ids.go b/spdxlib/element_ids.go new file mode 100644 index 0000000..2214d04 --- /dev/null +++ b/spdxlib/element_ids.go @@ -0,0 +1,15 @@ +package spdxlib + +import ( + "github.com/spdx/tools-golang/spdx" + "sort" +) + +// SortElementIDs sorts and returns the given slice of ElementIDs +func SortElementIDs(eIDs []spdx.ElementID) []spdx.ElementID { + sort.Slice(eIDs, func(i, j int) bool { + return eIDs[i] < eIDs[j] + }) + + return eIDs +}
\ No newline at end of file diff --git a/spdxlib/element_ids_test.go b/spdxlib/element_ids_test.go new file mode 100644 index 0000000..c541e4f --- /dev/null +++ b/spdxlib/element_ids_test.go @@ -0,0 +1,16 @@ +package spdxlib + +import ( + "github.com/spdx/tools-golang/spdx" + "reflect" + "testing" +) + +func TestSortElementIDs(t *testing.T) { + eIDs := []spdx.ElementID{"def", "abc", "123"} + eIDs = SortElementIDs(eIDs) + + if !reflect.DeepEqual(eIDs, []spdx.ElementID{"123", "abc", "def"}) { + t.Fatalf("expected sorted ElementIDs, got: %v", eIDs) + } +}
\ No newline at end of file diff --git a/spdxlib/relationships.go b/spdxlib/relationships.go new file mode 100644 index 0000000..8b7422e --- /dev/null +++ b/spdxlib/relationships.go @@ -0,0 +1,31 @@ +package spdxlib + +import "github.com/spdx/tools-golang/spdx" + +// FilterRelationships2_1 returns a slice of Element IDs returned by the given filter closure. The closure is passed +// one relationship at a time, and it can return an ElementID or nil. +func FilterRelationships2_1(doc *spdx.Document2_1, filter func(*spdx.Relationship2_1) *spdx.ElementID) ([]spdx.ElementID, error) { + elementIDs := []spdx.ElementID{} + + for _, relationship := range doc.Relationships { + if id := filter(relationship); id != nil { + elementIDs = append(elementIDs, *id) + } + } + + return elementIDs, nil +} + +// FilterRelationships2_2 returns a slice of Element IDs returned by the given filter closure. The closure is passed +// one relationship at a time, and it can return an ElementID or nil. +func FilterRelationships2_2(doc *spdx.Document2_2, filter func(*spdx.Relationship2_2) *spdx.ElementID) ([]spdx.ElementID, error) { + elementIDs := []spdx.ElementID{} + + for _, relationship := range doc.Relationships { + if id := filter(relationship); id != nil { + elementIDs = append(elementIDs, *id) + } + } + + return elementIDs, nil +} diff --git a/spdxlib/relationships_test.go b/spdxlib/relationships_test.go new file mode 100644 index 0000000..4a12a4a --- /dev/null +++ b/spdxlib/relationships_test.go @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +package spdxlib + +import ( + "testing" + + "github.com/spdx/tools-golang/spdx" +) + +// ===== 2.1 tests ===== + +func Test2_1FilterForDependencies(t *testing.T) { + // set up document and some packages and relationships + doc := &spdx.Document2_1{ + CreationInfo: &spdx.CreationInfo2_1{ + SPDXVersion: "SPDX-2.1", + DataLicense: "CC0-1.0", + SPDXIdentifier: spdx.ElementID("DOCUMENT"), + }, + Packages: map[spdx.ElementID]*spdx.Package2_1{ + spdx.ElementID("p1"): {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, + spdx.ElementID("p2"): {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, + spdx.ElementID("p3"): {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, + spdx.ElementID("p4"): {PackageName: "pkg4", PackageSPDXIdentifier: "p4"}, + spdx.ElementID("p5"): {PackageName: "pkg5", PackageSPDXIdentifier: "p5"}, + }, + Relationships: []*spdx.Relationship2_1{ + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p1"), + Relationship: "DESCRIBES", + }, + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p5"), + Relationship: "DESCRIBES", + }, + { + RefA: spdx.MakeDocElementID("", "p4"), + RefB: spdx.MakeDocElementID("", "DOCUMENT"), + Relationship: "DESCRIBED_BY", + }, + { + RefA: spdx.MakeDocElementID("", "p1"), + RefB: spdx.MakeDocElementID("", "p2"), + Relationship: "DEPENDS_ON", + }, + { + RefA: spdx.MakeDocElementID("", "p3"), + RefB: spdx.MakeDocElementID("", "p4"), + Relationship: "DEPENDENCY_OF", + }, + + }, + } + + eIDs, err := FilterRelationships2_1(doc, func(relationship *spdx.Relationship2_1) *spdx.ElementID { + p1EID := spdx.MakeDocElementID("", "p1") + if relationship.Relationship == "DEPENDS_ON" && relationship.RefA == p1EID { + return &relationship.RefB.ElementRefID + } else if relationship.Relationship == "DEPENDENCY_OF" && relationship.RefB == p1EID { + return &relationship.RefA.ElementRefID + } + + return nil + }) + if err != nil { + t.Fatalf("expected non-nil err, got: %s", err.Error()) + } + + if len(eIDs) != 1 { + t.Fatalf("expected 1 ElementID, got: %v", eIDs) + } + + if eIDs[0] != spdx.MakeDocElementID("", "p2").ElementRefID { + t.Fatalf("received unexpected relationship: %v", eIDs[0]) + } +} + +// ===== 2.2 tests ===== + +func Test2_2FindsDependsOnRelationships(t *testing.T) { + // set up document and some packages and relationships + doc := &spdx.Document2_2{ + CreationInfo: &spdx.CreationInfo2_2{ + SPDXVersion: "SPDX-2.2", + DataLicense: "CC0-1.0", + SPDXIdentifier: spdx.ElementID("DOCUMENT"), + }, + Packages: map[spdx.ElementID]*spdx.Package2_2{ + spdx.ElementID("p1"): {PackageName: "pkg1", PackageSPDXIdentifier: "p1"}, + spdx.ElementID("p2"): {PackageName: "pkg2", PackageSPDXIdentifier: "p2"}, + spdx.ElementID("p3"): {PackageName: "pkg3", PackageSPDXIdentifier: "p3"}, + spdx.ElementID("p4"): {PackageName: "pkg4", PackageSPDXIdentifier: "p4"}, + spdx.ElementID("p5"): {PackageName: "pkg5", PackageSPDXIdentifier: "p5"}, + }, + Relationships: []*spdx.Relationship2_2{ + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p1"), + Relationship: "DESCRIBES", + }, + { + RefA: spdx.MakeDocElementID("", "DOCUMENT"), + RefB: spdx.MakeDocElementID("", "p5"), + Relationship: "DESCRIBES", + }, + // inverse relationship -- should also get detected + { + RefA: spdx.MakeDocElementID("", "p4"), + RefB: spdx.MakeDocElementID("", "DOCUMENT"), + Relationship: "DESCRIBED_BY", + }, + // different relationship + { + RefA: spdx.MakeDocElementID("", "p1"), + RefB: spdx.MakeDocElementID("", "p2"), + Relationship: "DEPENDS_ON", + }, + }, + } + + eIDs, err := FilterRelationships2_2(doc, func(relationship *spdx.Relationship2_2) *spdx.ElementID { + p1EID := spdx.MakeDocElementID("", "p1") + if relationship.Relationship == "DEPENDS_ON" && relationship.RefA == p1EID { + return &relationship.RefB.ElementRefID + } else if relationship.Relationship == "DEPENDENCY_OF" && relationship.RefB == p1EID { + return &relationship.RefA.ElementRefID + } + + return nil + }) + if err != nil { + t.Fatalf("expected non-nil err, got: %s", err.Error()) + } + + if len(eIDs) != 1 { + t.Fatalf("expected 1 ElementID, got: %v", eIDs) + } + + if eIDs[0] != spdx.MakeDocElementID("", "p2").ElementRefID { + t.Fatalf("received unexpected relationship: %v", eIDs[0]) + } +} |