github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/os/homebrew/homebrew_test.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 homebrew_test 16 17 import ( 18 "testing" 19 20 "github.com/google/go-cmp/cmp" 21 "github.com/google/go-cmp/cmp/cmpopts" 22 "github.com/google/osv-scalibr/extractor" 23 "github.com/google/osv-scalibr/extractor/filesystem" 24 "github.com/google/osv-scalibr/extractor/filesystem/os/homebrew" 25 "github.com/google/osv-scalibr/extractor/filesystem/simplefileapi" 26 "github.com/google/osv-scalibr/inventory" 27 "github.com/google/osv-scalibr/purl" 28 ) 29 30 func TestFileRequired(t *testing.T) { 31 tests := []struct { 32 name string 33 path string 34 wantIsRequired bool 35 }{ 36 { 37 name: "cellar.valid.json", 38 path: "somefolder/Cellar/rclone/1.67.0/INSTALL_RECEIPT.json", 39 wantIsRequired: true, 40 }, 41 { 42 name: "cellar.invalid.json.filename", 43 path: "somefolder/Cellar/rclone/1.67.0/other.json", 44 wantIsRequired: false, 45 }, 46 { 47 name: "cellar.invalid.json.extension", 48 path: "somefolder/Cellar/rclone/1.67.0/INSTALL_RECEIPT.json2", 49 wantIsRequired: false, 50 }, 51 { 52 name: "Caskroom.nonstandard.version.folder.check", 53 path: "somefolder/Caskroom/somefolder/latest/somefolder-app/source.properties", 54 wantIsRequired: true, 55 }, 56 { 57 name: "caskroom.wrapper.sh.variation", 58 path: "somefolder/Caskroom/testapp/1.1.1/testapp.wrapper.sh", 59 wantIsRequired: true, 60 }, 61 { 62 name: "caskroom.source.properties.longer.path.variation", 63 path: "somefolder/Caskroom/android-platform-tools/35.0.2/platform-tools/source.properties", 64 wantIsRequired: true, 65 }, 66 { 67 name: "caskroom.app.variation", 68 path: "somefolder/Caskroom/firefox/130.0.1/firefox.app", 69 wantIsRequired: true, 70 }, 71 { 72 name: "caskroom.missing.folder.failure", 73 path: "somefolder/Caskroom/somefolder/2.3", 74 wantIsRequired: false, 75 }, 76 { 77 name: "caskroom.missing.file.failure", 78 path: "somefolder/Caskroom/somefolder/1.1/", 79 wantIsRequired: false, 80 }, 81 { 82 name: "Caskroom.invalid.json.filename", 83 path: "somefolder/Caskroom/rclone/1.67.0/INSTALL_RECEIPT.json", 84 wantIsRequired: false, 85 }, 86 { 87 name: "generally.wrong.path", 88 path: "somefolder/otherfile.json", 89 wantIsRequired: false, 90 }, 91 { 92 name: "caskroom.invalid.suffix", 93 path: "somefolder/Caskroom/testapp/1.1.1/testapp.app/Contents/PkgInfo", 94 wantIsRequired: false, 95 }, 96 } 97 98 for _, tt := range tests { 99 t.Run(tt.name, func(t *testing.T) { 100 var e filesystem.Extractor = homebrew.Extractor{} 101 if got := e.FileRequired(simplefileapi.New(tt.path, nil)); got != tt.wantIsRequired { 102 t.Fatalf("FileRequired(%s): got %v, want %v", tt.path, got, tt.wantIsRequired) 103 } 104 }) 105 } 106 } 107 108 func pkgLess(i1, i2 *extractor.Package) bool { 109 return i1.Name < i2.Name 110 } 111 112 func TestExtract(t *testing.T) { 113 tests := []struct { 114 name string 115 path string 116 wantErr error 117 wantPackages []*extractor.Package 118 }{ 119 { 120 name: "cellar.valid.json", 121 path: "testdata/Cellar/rclone/1.67.0/INSTALL_RECEIPT.json", 122 wantPackages: []*extractor.Package{ 123 { 124 Name: "rclone", 125 Version: "1.67.0", 126 PURLType: purl.TypeBrew, 127 Locations: []string{"testdata/Cellar/rclone/1.67.0/INSTALL_RECEIPT.json"}, 128 Metadata: &homebrew.Metadata{}, 129 }, 130 }, 131 }, 132 { 133 name: "caskroom.valid.json", 134 path: "testdata/Caskroom/testapp/1.1.1/testapp.wrapper.sh", 135 wantPackages: []*extractor.Package{ 136 { 137 Name: "testapp", 138 Version: "1.1.1", 139 PURLType: purl.TypeBrew, 140 Locations: []string{"testdata/Caskroom/testapp/1.1.1/testapp.wrapper.sh"}, 141 Metadata: &homebrew.Metadata{}, 142 }, 143 }, 144 }, 145 { 146 name: "caskroom.null.variation", 147 path: "testdata/Caskroom/somefolder/2.2", 148 wantPackages: nil, 149 }, 150 { 151 name: "caskroom.other.variation", 152 path: "testdata/Caskroom/android-platform-tools/35.0.2/platform-tools/source.properties", 153 wantPackages: []*extractor.Package{ 154 { 155 Name: "android-platform-tools", 156 Version: "35.0.2", 157 PURLType: purl.TypeBrew, 158 Locations: []string{"testdata/Caskroom/android-platform-tools/35.0.2/platform-tools/source.properties"}, 159 Metadata: &homebrew.Metadata{}, 160 }, 161 }, 162 }, 163 } 164 165 for _, tt := range tests { 166 t.Run(tt.name, func(t *testing.T) { 167 var e filesystem.Extractor = homebrew.Extractor{} 168 input := &filesystem.ScanInput{Path: tt.path, Reader: nil} 169 got, err := e.Extract(t.Context(), input) 170 if diff := cmp.Diff(tt.wantErr, err, cmpopts.EquateErrors()); diff != "" { 171 t.Errorf("Extract(%s) unexpected error (-want +got):\n%s", tt.path, diff) 172 } 173 174 want := inventory.Inventory{Packages: tt.wantPackages} 175 176 if diff := cmp.Diff(want, got, cmpopts.SortSlices(pkgLess)); diff != "" { 177 t.Errorf("Extract(%s) (-want +got):\n%s", tt.path, diff) 178 } 179 }) 180 } 181 } 182 183 func TestSplitPath(t *testing.T) { 184 tests := []struct { 185 name string 186 path string 187 want *homebrew.BrewPath 188 }{ 189 { 190 name: "cellar_path", 191 path: "testdata/Cellar/rclone/1.67.0/INSTALL_RECEIPT.json", 192 want: &homebrew.BrewPath{ 193 AppName: "rclone", 194 AppVersion: "1.67.0", 195 AppFile: "install_receipt.json", 196 AppExt: "install_receipt.json", 197 }, 198 }, 199 { 200 name: "caskroom_path", 201 path: "testdata/Caskroom/testapp/1.1.1/testapp.wrapper.sh", 202 want: &homebrew.BrewPath{ 203 AppName: "testapp", 204 AppVersion: "1.1.1", 205 AppFile: "testapp.wrapper.sh", 206 AppExt: "testapp.wrapper.sh", 207 }, 208 }, 209 } 210 211 for _, tt := range tests { 212 t.Run(tt.name, func(t *testing.T) { 213 got := homebrew.SplitPath(tt.path) 214 if diff := cmp.Diff(tt.want, got); diff != "" { 215 t.Errorf("SpiltPath(%v) (-want +got):\n%s", tt.path, diff) 216 } 217 }) 218 } 219 }