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 }