github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/internal/pkgtest/observing_resolver.go (about)

     1  package pkgtest
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"sort"
     7  
     8  	"github.com/scylladb/go-set/strset"
     9  
    10  	"github.com/anchore/syft/syft/file"
    11  )
    12  
    13  var _ file.Resolver = (*ObservingResolver)(nil)
    14  
    15  type ObservingResolver struct {
    16  	decorated          file.Resolver
    17  	pathQueries        map[string][]string
    18  	pathResponses      []file.Location
    19  	contentQueries     []file.Location
    20  	emptyPathResponses map[string][]string
    21  }
    22  
    23  func NewObservingResolver(resolver file.Resolver) *ObservingResolver {
    24  	return &ObservingResolver{
    25  		decorated:          resolver,
    26  		pathResponses:      make([]file.Location, 0),
    27  		emptyPathResponses: make(map[string][]string),
    28  		pathQueries:        make(map[string][]string),
    29  	}
    30  }
    31  
    32  // testing helpers...
    33  
    34  func (r *ObservingResolver) ObservedPathQuery(input string) bool {
    35  	for _, vs := range r.pathQueries {
    36  		for _, v := range vs {
    37  			if v == input {
    38  				return true
    39  			}
    40  		}
    41  	}
    42  	return false
    43  }
    44  
    45  func (r *ObservingResolver) ObservedPathResponses(path string) bool {
    46  	for _, loc := range r.pathResponses {
    47  		if loc.RealPath == path {
    48  			return true
    49  		}
    50  	}
    51  	return false
    52  }
    53  
    54  func (r *ObservingResolver) ObservedContentQueries(path string) bool {
    55  	for _, loc := range r.contentQueries {
    56  		if loc.RealPath == path {
    57  			return true
    58  		}
    59  	}
    60  	return false
    61  }
    62  
    63  func (r *ObservingResolver) AllContentQueries() []string {
    64  	observed := strset.New()
    65  	for _, loc := range r.contentQueries {
    66  		observed.Add(loc.RealPath)
    67  	}
    68  	return observed.List()
    69  }
    70  
    71  func (r *ObservingResolver) AllPathQueries() map[string][]string {
    72  	return r.pathQueries
    73  }
    74  
    75  func (r *ObservingResolver) PruneUnfulfilledPathResponses(ignore map[string][]string, ignorePaths ...string) {
    76  	if ignore == nil {
    77  		return
    78  	}
    79  	// remove any paths that were ignored for specific calls
    80  	for k, v := range ignore {
    81  		results := r.emptyPathResponses[k]
    82  		for _, ig := range v {
    83  			for i, result := range results {
    84  				if result == ig {
    85  					results = append(results[:i], results[i+1:]...)
    86  					break
    87  				}
    88  			}
    89  		}
    90  		if len(results) > 0 {
    91  			r.emptyPathResponses[k] = results
    92  		} else {
    93  			delete(r.emptyPathResponses, k)
    94  		}
    95  	}
    96  
    97  	// remove any paths that were ignored for all calls
    98  	for _, ig := range ignorePaths {
    99  		for k, v := range r.emptyPathResponses {
   100  			for i, result := range v {
   101  				if result == ig {
   102  					v = append(v[:i], v[i+1:]...)
   103  					break
   104  				}
   105  			}
   106  			if len(v) > 0 {
   107  				r.emptyPathResponses[k] = v
   108  			} else {
   109  				delete(r.emptyPathResponses, k)
   110  			}
   111  		}
   112  	}
   113  }
   114  
   115  func (r *ObservingResolver) HasUnfulfilledPathRequests() bool {
   116  	return len(r.emptyPathResponses) > 0
   117  }
   118  
   119  func (r *ObservingResolver) PrettyUnfulfilledPathRequests() string {
   120  	var res string
   121  	var keys []string
   122  
   123  	for k := range r.emptyPathResponses {
   124  		keys = append(keys, k)
   125  	}
   126  
   127  	sort.Strings(keys)
   128  
   129  	for _, k := range keys {
   130  		res += fmt.Sprintf("   %s: %+v\n", k, r.emptyPathResponses[k])
   131  	}
   132  	return res
   133  }
   134  
   135  // For the file path resolver...
   136  
   137  func (r *ObservingResolver) addPathQuery(name string, input ...string) {
   138  	r.pathQueries[name] = append(r.pathQueries[name], input...)
   139  }
   140  
   141  func (r *ObservingResolver) addPathResponse(locs ...file.Location) {
   142  	r.pathResponses = append(r.pathResponses, locs...)
   143  }
   144  
   145  func (r *ObservingResolver) addEmptyPathResponse(name string, locs []file.Location, paths ...string) {
   146  	if len(locs) == 0 {
   147  		results := r.emptyPathResponses[name]
   148  		results = append(results, paths...)
   149  		r.emptyPathResponses[name] = results
   150  	}
   151  }
   152  
   153  func (r *ObservingResolver) FilesByPath(paths ...string) ([]file.Location, error) {
   154  	name := "FilesByPath"
   155  	r.addPathQuery(name, paths...)
   156  
   157  	locs, err := r.decorated.FilesByPath(paths...)
   158  
   159  	r.addPathResponse(locs...)
   160  	r.addEmptyPathResponse(name, locs, paths...)
   161  	return locs, err
   162  }
   163  
   164  func (r *ObservingResolver) FilesByGlob(patterns ...string) ([]file.Location, error) {
   165  	name := "FilesByGlob"
   166  	r.addPathQuery(name, patterns...)
   167  
   168  	locs, err := r.decorated.FilesByGlob(patterns...)
   169  
   170  	r.addPathResponse(locs...)
   171  	r.addEmptyPathResponse(name, locs, patterns...)
   172  	return locs, err
   173  }
   174  
   175  func (r *ObservingResolver) FilesByMIMEType(types ...string) ([]file.Location, error) {
   176  	name := "FilesByMIMEType"
   177  	r.addPathQuery(name, types...)
   178  
   179  	locs, err := r.decorated.FilesByMIMEType(types...)
   180  
   181  	r.addPathResponse(locs...)
   182  	r.addEmptyPathResponse(name, locs, types...)
   183  	return locs, err
   184  }
   185  
   186  func (r *ObservingResolver) RelativeFileByPath(l file.Location, path string) *file.Location {
   187  	name := "RelativeFileByPath"
   188  	r.addPathQuery(name, path)
   189  
   190  	loc := r.decorated.RelativeFileByPath(l, path)
   191  
   192  	if loc != nil {
   193  		r.addPathResponse(*loc)
   194  	} else {
   195  		results := r.emptyPathResponses[name]
   196  		results = append(results, path)
   197  		r.emptyPathResponses[name] = results
   198  	}
   199  	return loc
   200  }
   201  
   202  // For the content resolver methods...
   203  
   204  func (r *ObservingResolver) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
   205  	r.contentQueries = append(r.contentQueries, location)
   206  	reader, err := r.decorated.FileContentsByLocation(location)
   207  	return reader, err
   208  }
   209  
   210  // For the remaining resolver methods...
   211  
   212  func (r *ObservingResolver) AllLocations() <-chan file.Location {
   213  	return r.decorated.AllLocations()
   214  }
   215  
   216  func (r *ObservingResolver) HasPath(s string) bool {
   217  	return r.decorated.HasPath(s)
   218  }
   219  
   220  func (r *ObservingResolver) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
   221  	return r.decorated.FileMetadataByLocation(location)
   222  }