github.com/google/osv-scalibr@v0.4.1/testing/fakeextractor/fake_extractor.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package fakeextractor provides a Extractor implementation to be used in tests. 16 // 17 //nolint:plugger // This package contains test only mocks 18 package fakeextractor 19 20 import ( 21 "context" 22 "errors" 23 "path/filepath" 24 25 "github.com/google/go-cmp/cmp" 26 "github.com/google/osv-scalibr/extractor" 27 "github.com/google/osv-scalibr/extractor/filesystem" 28 "github.com/google/osv-scalibr/inventory" 29 "github.com/google/osv-scalibr/plugin" 30 ) 31 32 // NamesErr is a list of Package names and an error. 33 type NamesErr struct { 34 Names []string 35 Err error 36 } 37 38 // fakeExtractor is an Extractor implementation to be used in tests. 39 type fakeExtractor struct { 40 name string 41 version int 42 requiredFiles map[string]bool 43 pathToNamesErr map[string]NamesErr 44 dirExtractor bool 45 } 46 47 // AllowUnexported is a utility function to be used with cmp.Diff to 48 // compare structs that contain the fake extractor. 49 var AllowUnexported = cmp.AllowUnexported(fakeExtractor{}) 50 51 // New returns a fake fakeExtractor. 52 // 53 // The fakeExtractor returns FileRequired(path) = true for any path in requiredFiles. 54 // The fakeExtractor returns the package and error from pathToNamesErr given the same path to Extract(...). 55 func New(name string, version int, requiredFiles []string, pathToNamesErr map[string]NamesErr) filesystem.Extractor { 56 rfs := map[string]bool{} 57 for _, path := range requiredFiles { 58 rfs[path] = true 59 } 60 61 // Maintain non-nil fields to avoid nil pointers on access such as FileRequired(...). 62 if len(pathToNamesErr) == 0 { 63 pathToNamesErr = map[string]NamesErr{} 64 } 65 66 return &fakeExtractor{ 67 name: name, 68 version: version, 69 requiredFiles: rfs, 70 pathToNamesErr: pathToNamesErr, 71 dirExtractor: false, 72 } 73 } 74 75 // NewDirExtractor returns a fake fakeExtractor designed for extracting directories. 76 // 77 // The fakeExtractor returns FileRequired(path) = true for any path in requiredFiles. 78 // The fakeExtractor returns the package and error from pathToNamesErr given the same path to Extract(...). 79 func NewDirExtractor(name string, version int, requiredFiles []string, pathToNamesErr map[string]NamesErr) filesystem.Extractor { 80 ext := New(name, version, requiredFiles, pathToNamesErr).(*fakeExtractor) 81 ext.dirExtractor = true 82 return ext 83 } 84 85 // Name returns the extractor's name. 86 func (e *fakeExtractor) Name() string { return e.name } 87 88 // Version returns the extractor's version. 89 func (e *fakeExtractor) Version() int { return e.version } 90 91 // Requirements returns the extractor's requirements. 92 func (e *fakeExtractor) Requirements() *plugin.Capabilities { 93 return &plugin.Capabilities{ 94 ExtractFromDirs: e.dirExtractor, 95 } 96 } 97 98 // FileRequired should return true if the file described by path and mode is 99 // relevant for the extractor. 100 // 101 // FileRequired returns true if the path was in requiredFiles and its value is true during 102 // construction in New(..., requiredFiles, ...) and false otherwise. 103 // Note: because mapfs forces all paths to slash, we have to align with it here. 104 func (e *fakeExtractor) FileRequired(api filesystem.FileAPI) bool { 105 return e.requiredFiles[filepath.ToSlash(api.Path())] 106 } 107 108 // Extract extracts package data relevant for the extractor from a given file. 109 // 110 // Extract returns the package list and error associated with input.Path from the pathToPackageErr map used 111 // during construction in NewExtractor(..., pathToPackageErr, ...). 112 // Note: because mapfs forces all paths to slash, we have to align with it here. 113 func (e *fakeExtractor) Extract(ctx context.Context, input *filesystem.ScanInput) (inventory.Inventory, error) { 114 path := filepath.ToSlash(input.Path) 115 namesErr, ok := e.pathToNamesErr[path] 116 if !ok { 117 return inventory.Inventory{}, errors.New("unrecognized path") 118 } 119 120 pkgs := []*extractor.Package{} 121 for _, name := range namesErr.Names { 122 pkgs = append(pkgs, &extractor.Package{ 123 Name: name, 124 Locations: []string{path}, 125 }) 126 } 127 128 return inventory.Inventory{Packages: pkgs}, namesErr.Err 129 }