github.com/anchore/syft@v1.38.2/syft/pkg/license_set_test.go (about) 1 package pkg 2 3 import ( 4 "context" 5 "os" 6 "testing" 7 8 "github.com/google/go-cmp/cmp" 9 "github.com/stretchr/testify/require" 10 11 "github.com/anchore/syft/internal/licenses" 12 "github.com/anchore/syft/syft/file" 13 "github.com/anchore/syft/syft/license" 14 ) 15 16 func TestLicenseSet_Add(t *testing.T) { 17 scanner, err := licenses.NewDefaultScanner() 18 require.NoError(t, err) 19 ctx := licenses.SetContextLicenseScanner(context.Background(), scanner) 20 tests := []struct { 21 name string 22 licenses []License 23 want []License 24 }{ 25 { 26 name: "add one simple license", 27 licenses: []License{ 28 NewLicenseWithContext(ctx, "MIT"), 29 }, 30 want: []License{ 31 NewLicenseWithContext(ctx, "MIT"), 32 }, 33 }, 34 { 35 name: "add multiple simple licenses", 36 licenses: []License{ 37 NewLicenseWithContext(ctx, "MIT"), 38 NewLicenseWithContext(ctx, "MIT"), 39 NewLicenseWithContext(ctx, "Apache-2.0"), 40 }, 41 want: []License{ 42 NewLicenseWithContext(ctx, "Apache-2.0"), 43 NewLicenseWithContext(ctx, "MIT"), 44 }, 45 }, 46 { 47 name: "attempt to add a license with no name", 48 licenses: []License{ 49 NewLicenseWithContext(ctx, ""), 50 }, 51 want: []License{}, 52 }, 53 { 54 name: "keep multiple licenses sorted", 55 licenses: []License{ 56 NewLicenseWithContext(ctx, "MIT"), 57 NewLicenseWithContext(ctx, "Apache-2.0"), 58 }, 59 want: []License{ 60 NewLicenseWithContext(ctx, "Apache-2.0"), 61 NewLicenseWithContext(ctx, "MIT"), 62 }, 63 }, 64 { 65 name: "deduplicate licenses with locations", 66 licenses: []License{ 67 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocationFromCoordinates(file.Coordinates{RealPath: "/place", FileSystemID: "1"})), 68 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocationFromCoordinates(file.Coordinates{RealPath: "/place", FileSystemID: "1"})), 69 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocationFromCoordinates(file.Coordinates{RealPath: "/place", FileSystemID: "2"})), 70 }, 71 want: []License{ 72 NewLicenseFromLocationsWithContext( 73 ctx, 74 "MIT", 75 file.NewLocationFromCoordinates(file.Coordinates{RealPath: "/place", FileSystemID: "1"}), 76 file.NewLocationFromCoordinates(file.Coordinates{RealPath: "/place", FileSystemID: "2"}), 77 ), 78 }, 79 }, 80 { 81 name: "same licenses with different locations", 82 licenses: []License{ 83 NewLicenseWithContext(ctx, "MIT"), 84 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocationFromCoordinates(file.Coordinates{RealPath: "/place", FileSystemID: "2"})), 85 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocationFromCoordinates(file.Coordinates{RealPath: "/place", FileSystemID: "1"})), 86 }, 87 want: []License{ 88 NewLicenseFromLocationsWithContext( 89 ctx, 90 "MIT", 91 file.NewLocationFromCoordinates(file.Coordinates{RealPath: "/place", FileSystemID: "1"}), 92 file.NewLocationFromCoordinates(file.Coordinates{RealPath: "/place", FileSystemID: "2"}), 93 ), 94 }, 95 }, 96 { 97 name: "same license from different sources", 98 licenses: []License{ 99 NewLicenseWithContext(ctx, "MIT"), 100 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("/place")), 101 NewLicenseFromURLsWithContext(ctx, "MIT", "https://example.com"), 102 }, 103 want: []License{ 104 { 105 Value: "MIT", 106 SPDXExpression: "MIT", 107 Type: license.Declared, 108 URLs: []string{"https://example.com"}, 109 Locations: file.NewLocationSet(file.NewLocation("/place")), 110 }, 111 }, 112 }, 113 { 114 name: "different licenses from different sources with different types constitute two licenses", 115 licenses: []License{ 116 NewLicenseFromTypeWithContext(ctx, "MIT", license.Concluded), 117 NewLicenseFromTypeWithContext(ctx, "MIT", license.Declared), 118 NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation("/place")), 119 NewLicenseFromURLsWithContext(ctx, "MIT", "https://example.com"), 120 }, 121 want: []License{ 122 { 123 Value: "MIT", 124 SPDXExpression: "MIT", 125 Type: license.Concluded, 126 Locations: file.NewLocationSet(), 127 }, 128 { 129 Value: "MIT", 130 SPDXExpression: "MIT", 131 Type: license.Declared, 132 URLs: []string{"https://example.com"}, 133 Locations: file.NewLocationSet(file.NewLocation("/place")), 134 }, 135 }, 136 }, 137 { 138 name: "licenses that are unknown with different contents can exist in the same set", 139 licenses: []License{ 140 NewLicenseWithContext(ctx, readFileAsString("../../internal/licenses/test-fixtures/nvidia-software-and-cuda-supplement")), 141 NewLicenseWithContext(ctx, readFileAsString("../../internal/licenses/test-fixtures/apache-license-2.0")), 142 }, 143 want: []License{ 144 { 145 SPDXExpression: "Apache-2.0", 146 Value: "Apache-2.0", 147 Type: license.Declared, 148 Contents: readFileAsString("../../internal/licenses/test-fixtures/apache-license-2.0"), 149 Locations: file.NewLocationSet(), 150 }, 151 { 152 Value: "sha256:eebcea3ab1d1a28e671de90119ffcfb35fe86951e4af1b17af52b7a82fcf7d0a", 153 Contents: readFileAsString("../../internal/licenses/test-fixtures/nvidia-software-and-cuda-supplement"), 154 Type: license.Declared, 155 }, 156 }, 157 }, 158 } 159 for _, tt := range tests { 160 t.Run(tt.name, func(t *testing.T) { 161 s := NewLicenseSet() 162 s.Add(tt.licenses...) 163 testMe := s.ToSlice() 164 if d := cmp.Diff(tt.want, testMe, cmp.Comparer(defaultLicenseComparer)); d != "" { 165 t.Errorf("unexpected license set (-want +got):\n%s", d) 166 } 167 }) 168 } 169 } 170 171 func defaultLocationComparer(x, y file.Location) bool { 172 return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.AccessPath, y.AccessPath) 173 } 174 175 func defaultLicenseComparer(x, y License) bool { 176 return cmp.Equal(x, y, cmp.Comparer(defaultLocationComparer), cmp.Comparer( 177 func(x, y file.LocationSet) bool { 178 xs := x.ToSlice() 179 ys := y.ToSlice() 180 if len(xs) != len(ys) { 181 return false 182 } 183 for i, xe := range xs { 184 ye := ys[i] 185 if !defaultLocationComparer(xe, ye) { 186 return false 187 } 188 } 189 return true 190 }, 191 )) 192 } 193 194 func readFileAsString(filepath string) string { 195 data, err := os.ReadFile(filepath) 196 if err != nil { 197 panic(err) 198 } 199 return string(data) 200 }