github.com/anchore/syft@v1.38.2/syft/internal/fileresolver/excluding_file_test.go (about)

     1  package fileresolver
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  
    11  	"github.com/anchore/syft/syft/file"
    12  )
    13  
    14  func TestExcludingResolver(t *testing.T) {
    15  
    16  	tests := []struct {
    17  		name      string
    18  		locations []string
    19  		excludeFn excludeFn
    20  		expected  []string
    21  	}{
    22  		{
    23  			name:      "keeps locations",
    24  			locations: []string{"a", "b", "c"},
    25  			excludeFn: func(s string) bool {
    26  				return false
    27  			},
    28  			expected: []string{"a", "b", "c"},
    29  		},
    30  		{
    31  			name:      "removes locations",
    32  			locations: []string{"d", "e", "f"},
    33  			excludeFn: func(s string) bool {
    34  				return true
    35  			},
    36  			expected: []string{},
    37  		},
    38  		{
    39  			name:      "removes first match",
    40  			locations: []string{"g", "h", "i"},
    41  			excludeFn: func(s string) bool {
    42  				return s == "g"
    43  			},
    44  			expected: []string{"h", "i"},
    45  		},
    46  		{
    47  			name:      "removes last match",
    48  			locations: []string{"j", "k", "l"},
    49  			excludeFn: func(s string) bool {
    50  				return s == "l"
    51  			},
    52  			expected: []string{"j", "k"},
    53  		},
    54  	}
    55  	for _, test := range tests {
    56  		t.Run(test.name, func(t *testing.T) {
    57  			resolver := &mockResolver{
    58  				locations: test.locations,
    59  			}
    60  			er := NewExcludingDecorator(resolver, test.excludeFn)
    61  
    62  			locations, _ := er.FilesByPath()
    63  			assert.ElementsMatch(t, locationPaths(locations), test.expected)
    64  
    65  			locations, _ = er.FilesByGlob()
    66  			assert.ElementsMatch(t, locationPaths(locations), test.expected)
    67  
    68  			locations, _ = er.FilesByMIMEType()
    69  			assert.ElementsMatch(t, locationPaths(locations), test.expected)
    70  
    71  			locations = []file.Location{}
    72  
    73  			ctx, cancel := context.WithCancel(context.Background())
    74  			defer cancel()
    75  			channel := er.AllLocations(ctx)
    76  			for location := range channel {
    77  				locations = append(locations, location)
    78  			}
    79  			assert.ElementsMatch(t, locationPaths(locations), test.expected)
    80  
    81  			diff := difference(test.locations, test.expected)
    82  
    83  			for _, path := range diff {
    84  				assert.False(t, er.HasPath(path))
    85  				c, err := er.FileContentsByLocation(file.NewLocation(path))
    86  				assert.Nil(t, c)
    87  				assert.Error(t, err)
    88  				m, err := er.FileMetadataByLocation(file.NewLocation(path))
    89  				assert.Empty(t, m.LinkDestination)
    90  				assert.Error(t, err)
    91  				l := er.RelativeFileByPath(file.NewLocation(""), path)
    92  				assert.Nil(t, l)
    93  			}
    94  
    95  			for _, path := range test.expected {
    96  				assert.True(t, er.HasPath(path))
    97  				c, err := er.FileContentsByLocation(file.NewLocation(path))
    98  				assert.NotNil(t, c)
    99  				assert.Nil(t, err)
   100  				m, err := er.FileMetadataByLocation(file.NewLocation(path))
   101  				assert.NotEmpty(t, m.LinkDestination)
   102  				assert.Nil(t, err)
   103  				l := er.RelativeFileByPath(file.NewLocation(""), path)
   104  				assert.NotNil(t, l)
   105  			}
   106  		})
   107  	}
   108  }
   109  
   110  // difference returns the elements in `a` that aren't in `b`.
   111  func difference(a, b []string) []string {
   112  	mb := make(map[string]struct{}, len(b))
   113  	for _, x := range b {
   114  		mb[x] = struct{}{}
   115  	}
   116  	var diff []string
   117  	for _, x := range a {
   118  		if _, found := mb[x]; !found {
   119  			diff = append(diff, x)
   120  		}
   121  	}
   122  	return diff
   123  }
   124  
   125  func locationPaths(locations []file.Location) []string {
   126  	paths := []string{}
   127  	for _, l := range locations {
   128  		paths = append(paths, l.RealPath)
   129  	}
   130  	return paths
   131  }
   132  
   133  type mockResolver struct {
   134  	locations []string
   135  }
   136  
   137  func (r *mockResolver) getLocations() ([]file.Location, error) {
   138  	out := []file.Location{}
   139  	for _, path := range r.locations {
   140  		out = append(out, file.NewLocation(path))
   141  	}
   142  	return out, nil
   143  }
   144  
   145  func (r *mockResolver) FileContentsByLocation(_ file.Location) (io.ReadCloser, error) {
   146  	return io.NopCloser(strings.NewReader("Hello, world!")), nil
   147  }
   148  
   149  func (r *mockResolver) FileMetadataByLocation(_ file.Location) (file.Metadata, error) {
   150  	return file.Metadata{
   151  		LinkDestination: "MOCK",
   152  	}, nil
   153  }
   154  
   155  func (r *mockResolver) HasPath(_ string) bool {
   156  	return true
   157  }
   158  
   159  func (r *mockResolver) FilesByPath(_ ...string) ([]file.Location, error) {
   160  	return r.getLocations()
   161  }
   162  
   163  func (r *mockResolver) FilesByGlob(_ ...string) ([]file.Location, error) {
   164  	return r.getLocations()
   165  }
   166  
   167  func (r *mockResolver) FilesByMIMEType(_ ...string) ([]file.Location, error) {
   168  	return r.getLocations()
   169  }
   170  
   171  func (r *mockResolver) FilesByExtension(_ ...string) ([]file.Location, error) {
   172  	return r.getLocations()
   173  }
   174  
   175  func (r *mockResolver) FilesByBasename(_ ...string) ([]file.Location, error) {
   176  	return r.getLocations()
   177  }
   178  
   179  func (r *mockResolver) FilesByBasenameGlob(_ ...string) ([]file.Location, error) {
   180  	return r.getLocations()
   181  }
   182  
   183  func (r *mockResolver) RelativeFileByPath(_ file.Location, path string) *file.Location {
   184  	l := file.NewLocation(path)
   185  	return &l
   186  }
   187  
   188  func (r *mockResolver) AllLocations(ctx context.Context) <-chan file.Location {
   189  	c := make(chan file.Location)
   190  	go func() {
   191  		defer close(c)
   192  		locations, _ := r.getLocations()
   193  		for _, location := range locations {
   194  			select {
   195  			case <-ctx.Done():
   196  				return
   197  			case c <- location:
   198  				continue
   199  			}
   200  		}
   201  	}()
   202  	return c
   203  }