github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/gentoo/license_test.go (about)

     1  package gentoo
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  // you can get a good sense of test fixtures with:
    15  //   docker run --rm -it gentoo/stage3 bash -c 'find var/db/pkg/ | grep LICENSE | xargs cat'
    16  
    17  func Test_extractLicenses(t *testing.T) {
    18  
    19  	tests := []struct {
    20  		name           string
    21  		license        string
    22  		wantExpression string
    23  	}{
    24  		{
    25  			name:           "empty",
    26  			license:        "",
    27  			wantExpression: "",
    28  		},
    29  		{
    30  			name:           "single",
    31  			license:        "GPL-2",
    32  			wantExpression: "GPL-2",
    33  		},
    34  		{
    35  			name:           "multiple",
    36  			license:        "GPL-2 GPL-3 ", // note the extra space
    37  			wantExpression: "GPL-2 AND GPL-3",
    38  		},
    39  		{
    40  			name:           "license choices",
    41  			license:        "|| ( GPL-2 GPL-3 )\n", // note the newline
    42  			wantExpression: "GPL-2 OR GPL-3",
    43  		},
    44  		{
    45  			// this might not be correct behavior, but we do our best with missing info
    46  			name:           "license choices with missing useflag suffix",
    47  			license:        "GPL-3+ LGPL-3+ || ( GPL-3+ libgcc libstdc++ gcc-runtime-library-exception-3.1 ) FDL-1.3+",                // no use flag so what do we do with FDL here?
    48  			wantExpression: "GPL-3+ AND LGPL-3+ AND (GPL-3+ OR libgcc OR libstdc++ OR gcc-runtime-library-exception-3.1 OR FDL-1.3+)", // "OR FDL-1.3+" is probably wrong at the end...
    49  		},
    50  	}
    51  	for _, tt := range tests {
    52  		t.Run(tt.name, func(t *testing.T) {
    53  			raw, expression := extractLicenses(nil, nil, strings.NewReader(tt.license))
    54  			assert.Equalf(t, tt.wantExpression, expression, "unexpected expression for %v", tt.license)
    55  			assert.Equalf(t, strings.TrimSpace(tt.license), raw, "unexpected raw for %v", tt.license)
    56  		})
    57  	}
    58  }
    59  
    60  func TestParseLicenseGroups(t *testing.T) {
    61  	tests := []struct {
    62  		name        string
    63  		input       string
    64  		expected    map[string][]string
    65  		expectError require.ErrorAssertionFunc
    66  	}{
    67  		{
    68  			name:  "basic nesting example",
    69  			input: "test-fixtures/license-groups/example1",
    70  			expected: map[string][]string{
    71  				"FSF-APPROVED": {
    72  					"Apache-2.0", "BSD", "BSD-2", "GPL-2", "GPL-3", "LGPL-2.1", "LGPL-3", "X11", "ZLIB",
    73  					"Apache-1.1", "BSD-4", "MPL-1.0", "MPL-1.1", "PSF-2.0",
    74  				},
    75  				"GPL-COMPATIBLE": {
    76  					"Apache-2.0", "BSD", "BSD-2", "GPL-2", "GPL-3", "LGPL-2.1", "LGPL-3", "X11", "ZLIB",
    77  				},
    78  			},
    79  		},
    80  		{
    81  			name:        "error on cycles",
    82  			input:       "test-fixtures/license-groups/cycle",
    83  			expectError: require.Error,
    84  		},
    85  		{
    86  			name:        "error on self references",
    87  			input:       "test-fixtures/license-groups/self",
    88  			expectError: require.Error,
    89  		},
    90  		{
    91  			name:        "error on missing reference",
    92  			input:       "test-fixtures/license-groups/missing",
    93  			expectError: require.Error,
    94  		},
    95  	}
    96  
    97  	for _, tc := range tests {
    98  		t.Run(tc.name, func(t *testing.T) {
    99  			if tc.expectError == nil {
   100  				tc.expectError = require.NoError
   101  			}
   102  
   103  			contents, err := os.ReadFile(tc.input)
   104  			require.NoError(t, err)
   105  
   106  			actual, err := parseLicenseGroups(bytes.NewReader(contents))
   107  			tc.expectError(t, err)
   108  			if err != nil {
   109  				return
   110  			}
   111  
   112  			if d := cmp.Diff(tc.expected, actual); d != "" {
   113  				t.Errorf("unexpected license groups (-want +got):\n%s", d)
   114  			}
   115  		})
   116  	}
   117  }
   118  
   119  func TestReplaceLicenseGroups(t *testing.T) {
   120  	tests := []struct {
   121  		name     string
   122  		licenses []string
   123  		groups   map[string][]string
   124  		expected []string
   125  	}{
   126  		{
   127  			name:     "nil groups",
   128  			licenses: []string{"MIT", "Apache-2.0", "@GPL"},
   129  			groups:   nil,
   130  			expected: []string{"MIT", "Apache-2.0", "@GPL"},
   131  		},
   132  		{
   133  			name:     "empty groups",
   134  			licenses: []string{"MIT", "Apache-2.0", "@GPL"},
   135  			groups:   map[string][]string{},
   136  			expected: []string{"MIT", "Apache-2.0", "@GPL"},
   137  		},
   138  		{
   139  			name:     "no group references",
   140  			licenses: []string{"MIT", "Apache-2.0", "GPL-2.0"},
   141  			groups:   map[string][]string{"GPL": {"GPL-2.0", "GPL-3.0"}},
   142  			expected: []string{"MIT", "Apache-2.0", "GPL-2.0"},
   143  		},
   144  		{
   145  			name:     "single group reference",
   146  			licenses: []string{"MIT", "@GPL", "Apache-2.0"},
   147  			groups:   map[string][]string{"GPL": {"GPL-2.0", "GPL-3.0"}},
   148  			expected: []string{"MIT", "GPL-2.0", "GPL-3.0", "Apache-2.0"},
   149  		},
   150  		{
   151  			name:     "multiple group references",
   152  			licenses: []string{"@MIT-LIKE", "@GPL", "BSD-3"},
   153  			groups: map[string][]string{
   154  				"MIT-LIKE": {"MIT", "ISC"},
   155  				"GPL":      {"GPL-2.0", "GPL-3.0"},
   156  			},
   157  			expected: []string{"MIT", "ISC", "GPL-2.0", "GPL-3.0", "BSD-3"},
   158  		},
   159  		{
   160  			name:     "unknown group reference",
   161  			licenses: []string{"MIT", "@UNKNOWN", "Apache-2.0"},
   162  			groups:   map[string][]string{"GPL": {"GPL-2.0", "GPL-3.0"}},
   163  			expected: []string{"MIT", "@UNKNOWN", "Apache-2.0"},
   164  		},
   165  		{
   166  			name:     "reference at end",
   167  			licenses: []string{"MIT", "Apache-2.0", "@GPL"},
   168  			groups:   map[string][]string{"GPL": {"GPL-2.0", "GPL-3.0"}},
   169  			expected: []string{"MIT", "Apache-2.0", "GPL-2.0", "GPL-3.0"},
   170  		},
   171  	}
   172  
   173  	for _, tc := range tests {
   174  		t.Run(tc.name, func(t *testing.T) {
   175  			inputLicenses := make([]string, len(tc.licenses))
   176  			copy(inputLicenses, tc.licenses)
   177  
   178  			actual := replaceLicenseGroups(inputLicenses, tc.groups)
   179  
   180  			assert.Equal(t, tc.expected, actual)
   181  		})
   182  	}
   183  }