github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/file/mock_resolver.go (about) 1 package file 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path" 8 9 "github.com/bmatcuk/doublestar/v4" 10 11 "github.com/anchore/stereoscope/pkg/file" 12 ) 13 14 var _ Resolver = (*MockResolver)(nil) 15 16 // MockResolver implements the FileResolver interface and is intended for use *only in test code*. 17 // It provides an implementation that can resolve local filesystem paths using only a provided discrete list of file 18 // paths, which are typically paths to test fixtures. 19 type MockResolver struct { 20 locations []Location 21 metadata map[Coordinates]Metadata 22 mimeTypeIndex map[string][]Location 23 extension map[string][]Location 24 basename map[string][]Location 25 } 26 27 // NewMockResolverForPaths creates a new MockResolver, where the only resolvable 28 // files are those specified by the supplied paths. 29 func NewMockResolverForPaths(paths ...string) *MockResolver { 30 var locations []Location 31 extension := make(map[string][]Location) 32 basename := make(map[string][]Location) 33 for _, p := range paths { 34 loc := NewLocation(p) 35 locations = append(locations, loc) 36 ext := path.Ext(p) 37 extension[ext] = append(extension[ext], loc) 38 bn := path.Base(p) 39 basename[bn] = append(basename[bn], loc) 40 } 41 42 return &MockResolver{ 43 locations: locations, 44 metadata: make(map[Coordinates]Metadata), 45 extension: extension, 46 basename: basename, 47 } 48 } 49 50 func NewMockResolverForPathsWithMetadata(metadata map[Coordinates]Metadata) *MockResolver { 51 var locations []Location 52 var mimeTypeIndex = make(map[string][]Location) 53 extension := make(map[string][]Location) 54 basename := make(map[string][]Location) 55 for c, m := range metadata { 56 l := NewLocationFromCoordinates(c) 57 locations = append(locations, l) 58 mimeTypeIndex[m.MIMEType] = append(mimeTypeIndex[m.MIMEType], l) 59 ext := path.Ext(l.RealPath) 60 extension[ext] = append(extension[ext], l) 61 bn := path.Base(l.RealPath) 62 basename[bn] = append(basename[bn], l) 63 } 64 65 return &MockResolver{ 66 locations: locations, 67 metadata: metadata, 68 mimeTypeIndex: mimeTypeIndex, 69 extension: extension, 70 basename: basename, 71 } 72 } 73 74 // HasPath indicates if the given path exists in the underlying source. 75 func (r MockResolver) HasPath(path string) bool { 76 for _, l := range r.locations { 77 if l.RealPath == path { 78 return true 79 } 80 } 81 return false 82 } 83 84 // String returns the string representation of the MockResolver. 85 func (r MockResolver) String() string { 86 return fmt.Sprintf("mock:(%s,...)", r.locations[0].RealPath) 87 } 88 89 // FileContentsByLocation fetches file contents for a single location. If the 90 // path does not exist, an error is returned. 91 func (r MockResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) { 92 for _, l := range r.locations { 93 if l.Coordinates == location.Coordinates { 94 return os.Open(location.RealPath) 95 } 96 } 97 98 return nil, fmt.Errorf("no file for location: %v", location) 99 } 100 101 // FilesByPath returns all Locations that match the given paths. 102 func (r MockResolver) FilesByPath(paths ...string) ([]Location, error) { 103 var results []Location 104 for _, p := range paths { 105 for _, location := range r.locations { 106 if p == location.RealPath { 107 results = append(results, NewLocation(p)) 108 } 109 } 110 } 111 112 return results, nil 113 } 114 115 // FilesByGlob returns all Locations that match the given path glob pattern. 116 func (r MockResolver) FilesByGlob(patterns ...string) ([]Location, error) { 117 var results []Location 118 for _, pattern := range patterns { 119 for _, location := range r.locations { 120 matches, err := doublestar.Match(pattern, location.RealPath) 121 if err != nil { 122 return nil, err 123 } 124 if matches { 125 results = append(results, location) 126 } 127 } 128 } 129 130 return results, nil 131 } 132 133 // RelativeFileByPath returns a single Location for the given path. 134 func (r MockResolver) RelativeFileByPath(_ Location, path string) *Location { 135 paths, err := r.FilesByPath(path) 136 if err != nil { 137 return nil 138 } 139 140 if len(paths) < 1 { 141 return nil 142 } 143 144 return &paths[0] 145 } 146 147 func (r MockResolver) AllLocations() <-chan Location { 148 results := make(chan Location) 149 go func() { 150 defer close(results) 151 for _, l := range r.locations { 152 results <- l 153 } 154 }() 155 return results 156 } 157 158 func (r MockResolver) FileMetadataByLocation(l Location) (Metadata, error) { 159 info, err := os.Stat(l.RealPath) 160 if err != nil { 161 return Metadata{}, err 162 } 163 164 // other types not supported 165 ty := file.TypeRegular 166 if info.IsDir() { 167 ty = file.TypeDirectory 168 } 169 170 return Metadata{ 171 FileInfo: info, 172 Type: ty, 173 UserID: 0, // not supported 174 GroupID: 0, // not supported 175 }, nil 176 } 177 178 func (r MockResolver) FilesByMIMEType(types ...string) ([]Location, error) { 179 var locations []Location 180 for _, ty := range types { 181 locations = append(r.mimeTypeIndex[ty], locations...) 182 } 183 return locations, nil 184 } 185 186 func (r MockResolver) FilesByExtension(extensions ...string) ([]Location, error) { 187 var results []Location 188 for _, ext := range extensions { 189 results = append(results, r.extension[ext]...) 190 } 191 return results, nil 192 } 193 194 func (r MockResolver) FilesByBasename(filenames ...string) ([]Location, error) { 195 var results []Location 196 for _, filename := range filenames { 197 results = append(results, r.basename[filename]...) 198 } 199 return results, nil 200 } 201 202 func (r MockResolver) FilesByBasenameGlob(_ ...string) ([]Location, error) { 203 // TODO implement me 204 panic("implement me") 205 } 206 207 func (r MockResolver) Write(_ Location, _ io.Reader) error { 208 return nil 209 }