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  }