split catalogers into two sets, one for images another for directory scans

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2020-10-06 13:11:35 -04:00 committed by Toure Dunnon
parent 1c320a8382
commit 10b44f5311
19 changed files with 218 additions and 149 deletions

View file

@ -130,7 +130,7 @@ unit: fixtures ## Run unit tests (with coverage)
.PHONY: integration
integration: ## Run integration tests
$(call title,Running integration tests)
go test -v -tags=integration ./test/integration
go test -tags=integration ./test/integration
# note: this is used by CI to determine if the integration test fixture cache (docker image tars) should be busted
integration-fingerprint:

View file

@ -44,7 +44,7 @@ func parseGemfileLockEntries(_ string, reader io.Reader) ([]pkg.Package, error)
Name: candidate[0],
Version: strings.Trim(candidate[1], "()"),
Language: pkg.Ruby,
Type: pkg.BundlerPkg,
Type: pkg.GemPkg,
})
}
}

View file

@ -94,7 +94,7 @@ func TestParseGemfileLockEntries(t *testing.T) {
t.Errorf("bad language (pkg=%+v): %+v", a.Name, a.Language)
}
if a.Type != pkg.BundlerPkg {
if a.Type != pkg.GemPkg {
t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type)
}
}

View file

@ -104,7 +104,7 @@ func parseGemspecEntries(_ string, reader io.Reader) ([]pkg.Package, error) {
Name: metadata.Name,
Version: metadata.Version,
Language: pkg.Ruby,
Type: pkg.BundlerPkg,
Type: pkg.GemPkg,
Metadata: metadata,
})
}

View file

@ -43,7 +43,7 @@ func TestParseGemspec(t *testing.T) {
t.Errorf("bad language (pkg=%+v): %+v", a.Name, a.Language)
}
if a.Type != pkg.BundlerPkg {
if a.Type != pkg.GemPkg {
t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type)
}
}

View file

@ -33,16 +33,30 @@ type Cataloger interface {
// TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering).
}
// All returns a slice of all locally defined catalogers (defined in child packages).
func All() []Cataloger {
// ImageCatalogers returns a slice of locally implemented catalogers that are fit for detecting installations of packages.
func ImageCatalogers() []Cataloger {
return []Cataloger{
bundler.NewGemspecCataloger(),
python.NewPythonCataloger(), // TODO: split and replace me
javascript.NewJavascriptCataloger(), // TODO: split and replace me
deb.NewDpkgdbCataloger(),
rpmdb.NewRpmdbCataloger(),
java.NewJavaCataloger(),
apkdb.NewApkdbCataloger(),
golang.NewGoModCataloger(),
}
}
// DirectoryCatalogers returns a slice of locally implemented catalogers that are fit for detecting packages from index files (and select installations)
func DirectoryCatalogers() []Cataloger {
return []Cataloger{
bundler.NewGemfileLockCataloger(),
python.NewPythonCataloger(), // TODO: split and replace me
javascript.NewJavascriptCataloger(), // TODO: split and replace me
deb.NewDpkgdbCataloger(),
bundler.NewGemfileLockCataloger(),
python.NewPythonCataloger(),
rpmdb.NewRpmdbCataloger(),
java.NewJavaCataloger(),
apkdb.NewApkdbCataloger(),
golang.NewGoModCataloger(),
javascript.NewJavascriptCataloger(),
}
}

View file

@ -17,6 +17,8 @@ Similar to the cataloging process, Linux distribution identification is also per
package syft
import (
"fmt"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cataloger"
@ -64,15 +66,17 @@ func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {
log.Info("building the catalog")
// conditionally have two sets of catalogers
//var catalogers []cataloger.Cataloger
//// if image
//// use one set of catalogers
//catalogers = ...
//
//// if dir
//// use another set of catalogers
var catalogers []cataloger.Cataloger
switch s.Scheme {
case scope.ImageScheme:
catalogers = cataloger.ImageCatalogers()
case scope.DirectoryScheme:
catalogers = cataloger.DirectoryCatalogers()
default:
return nil, fmt.Errorf("unable to determine cataloger set from scheme=%+v", s.Scheme)
}
return cataloger.Catalog(s.Resolver, cataloger.All()...)
return cataloger.Catalog(s.Resolver, catalogers...)
}
// SetLogger sets the logger object used for all syft logging calls.

View file

@ -1,9 +1,10 @@
package pkg
import (
"testing"
"github.com/anchore/syft/syft/distro"
"github.com/sergi/go-diff/diffmatchpatch"
"testing"
)
func TestPackage_pURL(t *testing.T) {
@ -56,7 +57,7 @@ func TestPackage_pURL(t *testing.T) {
pkg: Package{
Name: "name",
Version: "v0.1.0",
Type: BundlerPkg,
Type: GemPkg,
},
expected: "pkg:gem/name@v0.1.0",
},

View file

@ -8,7 +8,7 @@ type Type string
const (
UnknownPkg Type = "UnknownPackage"
ApkPkg Type = "apk"
BundlerPkg Type = "bundle"
GemPkg Type = "gem"
DebPkg Type = "deb"
EggPkg Type = "egg"
// PacmanPkg Type = "pacman"
@ -26,7 +26,7 @@ const (
var AllPkgs = []Type{
ApkPkg,
BundlerPkg,
GemPkg,
DebPkg,
EggPkg,
// PacmanPkg,
@ -45,7 +45,7 @@ func (t Type) PackageURLType() string {
switch t {
case ApkPkg:
return "alpine"
case BundlerPkg:
case GemPkg:
return packageurl.TypeGem
case DebPkg:
return "deb"

View file

@ -34,9 +34,7 @@ func NewPresenter(catalog *pkg.Catalog, s scope.Scope, d distro.Distro) *Present
func (pres *Presenter) Present(output io.Writer) error {
bom := NewDocumentFromCatalog(pres.catalog, pres.distro)
srcObj := pres.scope.Source()
switch src := srcObj.(type) {
switch src := pres.scope.Source.(type) {
case scope.DirSource:
bom.BomDescriptor.Component = &BdComponent{
Component: Component{

View file

@ -15,8 +15,7 @@ type ImageLocation struct {
}
func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) {
srcObj := s.Source()
switch src := srcObj.(type) {
switch src := s.Source.(type) {
case scope.ImageSource:
locations := make([]ImageLocation, len(p.Source))
for idx := range p.Source {

View file

@ -12,8 +12,7 @@ type Source struct {
}
func NewSource(s scope.Scope) (Source, error) {
srcObj := s.Source()
switch src := srcObj.(type) {
switch src := s.Source.(type) {
case scope.ImageSource:
return Source{
Type: "image",
@ -22,7 +21,7 @@ func NewSource(s scope.Scope) (Source, error) {
case scope.DirSource:
return Source{
Type: "directory",
Target: s.DirSrc.Path,
Target: src.Path,
}, nil
default:
return Source{}, fmt.Errorf("unsupported source: %T", src)

View file

@ -27,9 +27,8 @@ func (pres *Presenter) Present(output io.Writer) error {
// init the tabular writer
w := new(tabwriter.Writer)
w.Init(output, 0, 8, 0, '\t', tabwriter.AlignRight)
srcObj := pres.scope.Source()
switch src := srcObj.(type) {
switch src := pres.scope.Source.(type) {
case scope.DirSource:
fmt.Fprintln(w, fmt.Sprintf("[Path: %s]", src.Path))
case scope.ImageSource:

View file

@ -20,12 +20,12 @@ import (
)
const (
unknownScheme scheme = "unknown-scheme"
directoryScheme scheme = "directory-scheme"
imageScheme scheme = "image-scheme"
UnknownScheme Scheme = "unknown-scheme"
DirectoryScheme Scheme = "directory-scheme"
ImageScheme Scheme = "image-scheme"
)
type scheme string
type Scheme string
// ImageSource represents a data source that is a container image
type ImageSource struct {
@ -42,8 +42,8 @@ type DirSource struct {
type Scope struct {
Option Option // specific perspective to catalog
Resolver Resolver // a Resolver object to use in file path/glob resolution and file contents resolution
ImgSrc ImageSource // the specific image to be cataloged
DirSrc DirSource // the specific directory to be cataloged
Source interface{} // the specific source object to be cataloged
Scheme Scheme // the source data scheme type (directory or image)
}
// NewScope produces a Scope based on userInput like dir: or image:tag
@ -55,7 +55,7 @@ func NewScope(userInput string, o Option) (Scope, func(), error) {
}
switch parsedScheme {
case directoryScheme:
case DirectoryScheme:
fileMeta, err := fs.Stat(location)
if err != nil {
return Scope{}, func() {}, fmt.Errorf("unable to stat dir=%q: %w", location, err)
@ -71,7 +71,7 @@ func NewScope(userInput string, o Option) (Scope, func(), error) {
}
return s, func() {}, nil
case imageScheme:
case ImageScheme:
img, err := stereoscope.GetImage(location)
cleanup := func() {
stereoscope.Cleanup()
@ -97,9 +97,10 @@ func NewScopeFromDir(path string) (Scope, error) {
Resolver: &resolvers.DirectoryResolver{
Path: path,
},
DirSrc: DirSource{
Source: DirSource{
Path: path,
},
Scheme: DirectoryScheme,
}, nil
}
@ -118,59 +119,48 @@ func NewScopeFromImage(img *image.Image, option Option) (Scope, error) {
return Scope{
Option: option,
Resolver: resolver,
ImgSrc: ImageSource{
Source: ImageSource{
Img: img,
},
Scheme: ImageScheme,
}, nil
}
// Source returns the configured data source (either a dir source or container image source)
func (s Scope) Source() interface{} {
if s.ImgSrc != (ImageSource{}) {
return s.ImgSrc
}
if s.DirSrc != (DirSource{}) {
return s.DirSrc
}
return nil
}
type sourceDetector func(string) (image.Source, string, error)
func detectScheme(fs afero.Fs, imageDetector sourceDetector, userInput string) (scheme, string, error) {
func detectScheme(fs afero.Fs, imageDetector sourceDetector, userInput string) (Scheme, string, error) {
if strings.HasPrefix(userInput, "dir:") {
// blindly trust the user's scheme
dirLocation, err := homedir.Expand(strings.TrimPrefix(userInput, "dir:"))
if err != nil {
return unknownScheme, "", fmt.Errorf("unable to expand directory path: %w", err)
return UnknownScheme, "", fmt.Errorf("unable to expand directory path: %w", err)
}
return directoryScheme, dirLocation, nil
return DirectoryScheme, dirLocation, nil
}
// we should attempt to let stereoscope determine what the source is first --just because the source is a valid directory
// doesn't mean we yet know if it is an OCI layout directory (to be treated as an image) or if it is a generic filesystem directory.
source, imageSpec, err := imageDetector(userInput)
if err != nil {
return unknownScheme, "", fmt.Errorf("unable to detect the scheme from %q: %w", userInput, err)
return UnknownScheme, "", fmt.Errorf("unable to detect the scheme from %q: %w", userInput, err)
}
if source == image.UnknownSource {
dirLocation, err := homedir.Expand(userInput)
if err != nil {
return unknownScheme, "", fmt.Errorf("unable to expand potential directory path: %w", err)
return UnknownScheme, "", fmt.Errorf("unable to expand potential directory path: %w", err)
}
fileMeta, err := fs.Stat(dirLocation)
if err != nil {
return unknownScheme, "", nil
return UnknownScheme, "", nil
}
if fileMeta.IsDir() {
return directoryScheme, dirLocation, nil
return DirectoryScheme, dirLocation, nil
}
return unknownScheme, "", nil
return UnknownScheme, "", nil
}
return imageScheme, imageSpec, nil
return ImageScheme, imageSpec, nil
}

View file

@ -1,13 +1,13 @@
package scope
import (
"github.com/mitchellh/go-homedir"
"github.com/spf13/afero"
"os"
"testing"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/image"
"github.com/mitchellh/go-homedir"
"github.com/spf13/afero"
)
func TestNewScopeFromImageFails(t *testing.T) {
@ -78,8 +78,8 @@ func TestDirectoryScope(t *testing.T) {
if err != nil {
t.Errorf("could not create NewDirScope: %w", err)
}
if p.DirSrc.Path != test.input {
t.Errorf("mismatched stringer: '%s' != '%s'", p.DirSrc.Path, test.input)
if p.Source.(DirSource).Path != test.input {
t.Errorf("mismatched stringer: '%s' != '%s'", p.Source.(DirSource).Path, test.input)
}
refs, err := p.Resolver.FilesByPath(test.inputPaths...)
@ -229,7 +229,7 @@ func TestDetectScheme(t *testing.T) {
userInput string
dirs []string
detection detectorResult
expectedScheme scheme
expectedScheme Scheme
expectedLocation string
}{
{
@ -239,7 +239,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource,
ref: "wagoodman/dive:latest",
},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
expectedLocation: "wagoodman/dive:latest",
},
{
@ -249,7 +249,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource,
ref: "wagoodman/dive",
},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
expectedLocation: "wagoodman/dive",
},
{
@ -259,7 +259,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource,
ref: "wagoodman/dive:latest",
},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
expectedLocation: "wagoodman/dive:latest",
},
{
@ -269,7 +269,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource,
ref: "wagoodman/dive",
},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
expectedLocation: "wagoodman/dive",
},
{
@ -279,7 +279,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource,
ref: "latest",
},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
// we want to be able to handle this case better, however, I don't see a way to do this
// the user will need to provide more explicit input (docker:docker:latest)
expectedLocation: "latest",
@ -291,7 +291,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource,
ref: "docker:latest",
},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
// we want to be able to handle this case better, however, I don't see a way to do this
// the user will need to provide more explicit input (docker:docker:latest)
expectedLocation: "docker:latest",
@ -303,7 +303,7 @@ func TestDetectScheme(t *testing.T) {
src: image.OciTarballSource,
ref: "some/path-to-file",
},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
expectedLocation: "some/path-to-file",
},
{
@ -314,7 +314,7 @@ func TestDetectScheme(t *testing.T) {
ref: "some/path-to-dir",
},
dirs: []string{"some/path-to-dir"},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
expectedLocation: "some/path-to-dir",
},
{
@ -325,7 +325,7 @@ func TestDetectScheme(t *testing.T) {
ref: "",
},
dirs: []string{"some/path-to-dir"},
expectedScheme: directoryScheme,
expectedScheme: DirectoryScheme,
expectedLocation: "some/path-to-dir",
},
{
@ -335,7 +335,7 @@ func TestDetectScheme(t *testing.T) {
src: image.DockerDaemonSource,
ref: "some/path-to-dir",
},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
expectedLocation: "some/path-to-dir",
},
{
@ -346,7 +346,7 @@ func TestDetectScheme(t *testing.T) {
ref: "",
},
dirs: []string{"some/path-to-dir"},
expectedScheme: directoryScheme,
expectedScheme: DirectoryScheme,
expectedLocation: "some/path-to-dir",
},
{
@ -356,7 +356,7 @@ func TestDetectScheme(t *testing.T) {
src: image.UnknownSource,
ref: "",
},
expectedScheme: directoryScheme,
expectedScheme: DirectoryScheme,
expectedLocation: ".",
},
{
@ -366,7 +366,7 @@ func TestDetectScheme(t *testing.T) {
src: image.UnknownSource,
ref: "",
},
expectedScheme: directoryScheme,
expectedScheme: DirectoryScheme,
expectedLocation: ".",
},
// we should support tilde expansion
@ -377,7 +377,7 @@ func TestDetectScheme(t *testing.T) {
src: image.OciDirectorySource,
ref: "~/some-path",
},
expectedScheme: imageScheme,
expectedScheme: ImageScheme,
expectedLocation: "~/some-path",
},
{
@ -388,26 +388,26 @@ func TestDetectScheme(t *testing.T) {
ref: "",
},
dirs: []string{"~/some-path"},
expectedScheme: directoryScheme,
expectedScheme: DirectoryScheme,
expectedLocation: "~/some-path",
},
{
name: "tilde-expansion-dir-explicit-exists",
userInput: "dir:~/some-path",
dirs: []string{"~/some-path"},
expectedScheme: directoryScheme,
expectedScheme: DirectoryScheme,
expectedLocation: "~/some-path",
},
{
name: "tilde-expansion-dir-explicit-dne",
userInput: "dir:~/some-path",
expectedScheme: directoryScheme,
expectedScheme: DirectoryScheme,
expectedLocation: "~/some-path",
},
{
name: "tilde-expansion-dir-implicit-dne",
userInput: "~/some-path",
expectedScheme: unknownScheme,
expectedScheme: UnknownScheme,
expectedLocation: "",
},
}

View file

@ -108,6 +108,10 @@ func TestJsonSchemaImg(t *testing.T) {
t.Fatalf("failed to catalog image: %+v", err)
}
var cases []testCase
cases = append(cases, commonTestCases...)
cases = append(cases, imageOnlyTestCases...)
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
testJsonSchema(t, catalog, theScope, "img")
@ -121,6 +125,10 @@ func TestJsonSchemaDirs(t *testing.T) {
t.Errorf("unable to create scope from dir: %+v", err)
}
var cases []testCase
cases = append(cases, commonTestCases...)
cases = append(cases, dirOnlyTestCases...)
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
testJsonSchema(t, catalog, theScope, "dir")

View file

@ -4,12 +4,86 @@ package integration
import "github.com/anchore/syft/syft/pkg"
var cases = []struct {
type testCase struct {
name string
pkgType pkg.Type
pkgLanguage pkg.Language
pkgInfo map[string]string
}{
}
var imageOnlyTestCases = []testCase{
{
name: "find gemspec packages",
pkgType: pkg.GemPkg,
pkgLanguage: pkg.Ruby,
pkgInfo: map[string]string{
"bundler": "2.1.4",
},
},
}
var dirOnlyTestCases = []testCase{
{
name: "find gemfile packages",
pkgType: pkg.GemPkg,
pkgLanguage: pkg.Ruby,
pkgInfo: map[string]string{
"actionmailer": "4.1.1",
"actionpack": "4.1.1",
"actionview": "4.1.1",
"activemodel": "4.1.1",
"activerecord": "4.1.1",
"activesupport": "4.1.1",
"arel": "5.0.1.20140414130214",
"bootstrap-sass": "3.1.1.1",
"builder": "3.2.2",
"coffee-rails": "4.0.1",
"coffee-script": "2.2.0",
"coffee-script-source": "1.7.0",
"erubis": "2.7.0",
"execjs": "2.0.2",
"hike": "1.2.3",
"i18n": "0.6.9",
"jbuilder": "2.0.7",
"jquery-rails": "3.1.0",
"json": "1.8.1",
"kgio": "2.9.2",
"libv8": "3.16.14.3",
"mail": "2.5.4",
"mime-types": "1.25.1",
"minitest": "5.3.4",
"multi_json": "1.10.1",
"mysql2": "0.3.16",
"polyglot": "0.3.4",
"rack": "1.5.2",
"rack-test": "0.6.2",
"rails": "4.1.1",
"railties": "4.1.1",
"raindrops": "0.13.0",
"rake": "10.3.2",
"rdoc": "4.1.1",
"ref": "1.0.5",
"sass": "3.2.19",
"sass-rails": "4.0.3",
"sdoc": "0.4.0",
"spring": "1.1.3",
"sprockets": "2.11.0",
"sprockets-rails": "2.1.3",
"sqlite3": "1.3.9",
"therubyracer": "0.12.1",
"thor": "0.19.1",
"thread_safe": "0.3.3",
"tilt": "1.4.1",
"treetop": "1.4.15",
"turbolinks": "2.2.2",
"tzinfo": "1.2.0",
"uglifier": "2.5.0",
"unicorn": "4.8.3",
},
},
}
var commonTestCases = []testCase{
{
name: "find rpmdb packages",
pkgType: pkg.RpmPkg,
@ -98,64 +172,6 @@ var cases = []struct {
"mypy": "v0.770",
},
},
{
name: "find bundler packages",
pkgType: pkg.BundlerPkg,
pkgLanguage: pkg.Ruby,
pkgInfo: map[string]string{
"actionmailer": "4.1.1",
"actionpack": "4.1.1",
"actionview": "4.1.1",
"activemodel": "4.1.1",
"activerecord": "4.1.1",
"activesupport": "4.1.1",
"arel": "5.0.1.20140414130214",
"bootstrap-sass": "3.1.1.1",
"builder": "3.2.2",
"coffee-rails": "4.0.1",
"coffee-script": "2.2.0",
"coffee-script-source": "1.7.0",
"erubis": "2.7.0",
"execjs": "2.0.2",
"hike": "1.2.3",
"i18n": "0.6.9",
"jbuilder": "2.0.7",
"jquery-rails": "3.1.0",
"json": "1.8.1",
"kgio": "2.9.2",
"libv8": "3.16.14.3",
"mail": "2.5.4",
"mime-types": "1.25.1",
"minitest": "5.3.4",
"multi_json": "1.10.1",
"mysql2": "0.3.16",
"polyglot": "0.3.4",
"rack": "1.5.2",
"rack-test": "0.6.2",
"rails": "4.1.1",
"railties": "4.1.1",
"raindrops": "0.13.0",
"rake": "10.3.2",
"rdoc": "4.1.1",
"ref": "1.0.5",
"sass": "3.2.19",
"sass-rails": "4.0.3",
"sdoc": "0.4.0",
"spring": "1.1.3",
"sprockets": "2.11.0",
"sprockets-rails": "2.1.3",
"sqlite3": "1.3.9",
"therubyracer": "0.12.1",
"thor": "0.19.1",
"thread_safe": "0.3.3",
"tilt": "1.4.1",
"treetop": "1.4.15",
"turbolinks": "2.2.2",
"tzinfo": "1.2.0",
"uglifier": "2.5.0",
"unicorn": "4.8.3",
},
},
{
name: "find apkdb packages",

View file

@ -3,9 +3,11 @@
package integration
import (
"github.com/anchore/stereoscope/pkg/imagetest"
"testing"
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/go-test/deep"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/pkg"
@ -35,6 +37,10 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Add(string(p))
}
var cases []testCase
cases = append(cases, commonTestCases...)
cases = append(cases, imageOnlyTestCases...)
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
pkgCount := 0
@ -81,10 +87,16 @@ func TestPkgCoverageImage(t *testing.T) {
// ensure that integration test cases stay in sync with the available catalogers
if len(observedLanguages) < len(definedLanguages) {
t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", len(definedLanguages), len(observedLanguages))
for _, d := range deep.Equal(observedLanguages, definedLanguages) {
t.Errorf("diff: %+v", d)
}
}
if len(observedPkgs) < len(definedPkgs) {
t.Errorf("package coverage incomplete (packages=%d, coverage=%d)", len(definedPkgs), len(observedPkgs))
for _, d := range deep.Equal(observedPkgs, definedPkgs) {
t.Errorf("diff: %+v", d)
}
}
}
@ -107,6 +119,10 @@ func TestPkgCoverageDirectory(t *testing.T) {
definedPkgs.Add(string(p))
}
var cases []testCase
cases = append(cases, commonTestCases...)
cases = append(cases, dirOnlyTestCases...)
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
pkgCount := 0
@ -150,7 +166,7 @@ func TestPkgCoverageDirectory(t *testing.T) {
observedPkgs.Remove(string(pkg.UnknownPkg))
definedPkgs.Remove(string(pkg.UnknownPkg))
// ensure that integration test cases stay in sync with the available catalogers
// ensure that integration test commonTestCases stay in sync with the available catalogers
if len(observedLanguages) < len(definedLanguages) {
t.Errorf("language coverage incomplete (languages=%d, coverage=%d)", len(definedLanguages), len(observedLanguages))
}

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
# -*- encoding: utf-8 -*-
# stub: bundler 2.1.4 ruby lib
Gem::Specification.new do |s|
s.name = "bundler".freeze
s.version = "2.1.4"
s.required_rubygems_version = Gem::Requirement.new(">= 2.5.2".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["Andr\u00E9 Arko".freeze, "Samuel Giddins".freeze, "Colby Swandale".freeze, "Hiroshi Shibata".freeze, "David Rodr\u00EDguez".freeze, "Grey Baker".f
s.bindir = "exe".freeze
s.date = "2020-01-05"
s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably".freeze
s.email = ["team@bundler.io".freeze]
s.executables = ["bundle".freeze, "bundler".freeze]
s.files = ["exe/bundle".freeze, "exe/bundler".freeze]
s.homepage = "https://bundler.io".freeze
s.licenses = ["MIT".freeze]
s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze)
s.rubygems_version = "3.1.2".freeze
s.summary = "The best way to manage your application's dependencies".freeze
s.installed_by_version = "3.1.2" if s.respond_to? :installed_by_version
end