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  }