diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..2bcde6f9d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# check out text files with lf on every platform (regardless of core.autocrlf), letting git detect binary files +* text=auto eol=lf + +# fixtures that must keep their crlf line endings byte for byte +syft/linux/testdata/unprintable -text +syft/pkg/cataloger/bitnami/testdata/no-rel/opt/bitnami/redis/.spdx-redis.spdx -text +syft/pkg/cataloger/java/testdata/jar-metadata/**/META-INF/MANIFEST.MF -text diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 2656ab1f1..358cbadbf 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -49,6 +49,13 @@ jobs: Unit-Test: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Unit tests" + runs-on: ubuntu-latest + needs: [Unit-Test-Linux, Unit-Test-Windows] + steps: + - run: echo All unit tests passed! + + Unit-Test-Linux: + name: "Unit tests (Linux)" runs-on: *test-runner permissions: contents: read @@ -68,6 +75,27 @@ jobs: - name: Check for capability drift run: make check-capability-drift + Unit-Test-Windows: + name: "Unit tests (Windows)" + runs-on: windows-latest + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1 + with: + persist-credentials: false + + # go-make's setup only (not ./.github/actions/bootstrap): no apt, binny tools, or fixture cache are needed + - name: Setup go + go-make tooling + uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1 + with: + # keep in sync with the go-version default in ./.github/actions/bootstrap + go-version: "1.26.2" + + # the full unit suite leans on linux docker fixtures, so this runs the windows-relevant subset + - name: Run unit tests + run: make unit:windows + Integration-Test: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Integration tests" diff --git a/.make/main.go b/.make/main.go index f0f08700d..58db41a96 100644 --- a/.make/main.go +++ b/.make/main.go @@ -1,6 +1,7 @@ package main import ( + "os" "path/filepath" "runtime" "strconv" @@ -52,7 +53,7 @@ func main() { raceFlag = " -race" } Run( - "go test -count=1 -timeout=30m -v"+raceFlag+" ./cmd/syft/internal/test/integration/...", + "go test -count=1 -timeout=30m"+raceFlag+" ./cmd/syft/internal/test/integration/...", run.Env("GODEBUG", "dontfreezetheworld=1"), ) }, @@ -96,6 +97,23 @@ func main() { }, }, + // windows unit tests: deliberately not hooked into "test" (no RunsOn). The full unit suite leans on + // linux docker fixture images and posix-only assumptions, so this covers the packages that own + // host path handling (where windows bugs live) and skips the tests that need docker image fixtures. + // ponytail: curated package list, widen it as more of the suite is made windows-safe. + Task{ + Name: "unit:windows", + Description: "run the windows-relevant subset of unit tests", + Run: func() { + Run("go test -count=1"+ + ` -skip "Image|Squash|AllLayers|MixFileTypes|GivenCoordinates"`+ + " ./syft/internal/windows/... ./syft/internal/fileresolver/... ./syft/source/directorysource/... ./syft/source/filesource/..."+ + " ./syft/file ./syft/file/cataloger/filedigest/... ./syft/file/cataloger/filecontent/...", + run.Stdout(os.Stderr), + ) + }, + }, + // default validation pipeline (replaces Taskfile `default`/`pr-validations`/`validations`). Task{ Name: "default", diff --git a/syft/internal/fileresolver/chroot_context.go b/syft/internal/fileresolver/chroot_context.go index 88647be3a..deb8cd000 100644 --- a/syft/internal/fileresolver/chroot_context.go +++ b/syft/internal/fileresolver/chroot_context.go @@ -216,5 +216,8 @@ func (r ChrootContext) ToChrootPath(nativePath string) string { responsePath = strings.TrimPrefix(responsePath, prefix) } - return responsePath + // chroot paths are posix regardless of host (these end up as location paths in the SBOM). This uses + // filepath (not path) since responsePath is native here (FromPosix above, then trimmed of a native prefix), + // and only filepath.ToSlash knows the host separator to convert. + return filepath.ToSlash(responsePath) } diff --git a/syft/internal/fileresolver/chroot_context_test.go b/syft/internal/fileresolver/chroot_context_test.go index 06f6e283a..fbe2f20e1 100644 --- a/syft/internal/fileresolver/chroot_context_test.go +++ b/syft/internal/fileresolver/chroot_context_test.go @@ -3,6 +3,7 @@ package fileresolver import ( "os" "path/filepath" + "runtime" "testing" "github.com/stretchr/testify/assert" @@ -10,6 +11,9 @@ import ( ) func Test_ChrootContext_RequestResponse(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("fixture relies on symlinks, which git does not check out as links on windows by default") + } // / // somewhere/ // outside.txt @@ -563,6 +567,9 @@ func TestNewChrootContext_BaseValidation(t *testing.T) { } func TestToNativeGlob(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("chroot context and expectations are posix paths") + } tests := []struct { name string chrootContext ChrootContext diff --git a/syft/internal/fileresolver/directory_indexer_test.go b/syft/internal/fileresolver/directory_indexer_test.go index d24a86201..39685ccce 100644 --- a/syft/internal/fileresolver/directory_indexer_test.go +++ b/syft/internal/fileresolver/directory_indexer_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "os" - "path" "path/filepath" "runtime" "sort" @@ -18,6 +17,7 @@ import ( "github.com/wagoodman/go-progress" "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/syft/syft/internal/windows" ) type indexerMock struct { @@ -138,6 +138,9 @@ func TestDirectoryIndexer_handleFileAccessErr(t *testing.T) { } func TestDirectoryIndexer_IncludeRootPathInIndex(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("indexes the posix filesystem root") + } filterFn := func(_, path string, _ os.FileInfo, _ error) error { if path != "/" { return fs.SkipDir @@ -206,11 +209,7 @@ func TestDirectoryIndexer_index(t *testing.T) { info, err := os.Stat(test.path) assert.NoError(t, err) - // note: the index uses absolute paths, so assertions MUST keep this in mind - cwd, err := os.Getwd() - require.NoError(t, err) - - p := file.Path(path.Join(cwd, test.path)) + p := indexedPath(t, test.path) assert.Equal(t, true, tree.HasPath(p)) exists, ref, err := tree.File(p) assert.Equal(t, true, exists) @@ -226,6 +225,9 @@ func TestDirectoryIndexer_index(t *testing.T) { } func TestDirectoryIndexer_index_for_AncestorSymlinks(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("fixture relies on symlinks, which git does not check out as links on windows by default") + } // note: this test is testing the effects from NewFromDirectory, indexTree, and addPathToIndex _, filename, _, ok := runtime.Caller(0) require.True(t, ok) @@ -256,11 +258,7 @@ func TestDirectoryIndexer_index_for_AncestorSymlinks(t *testing.T) { info, err := os.Stat(test.path) assert.NoError(t, err) - // note: the index uses absolute paths, so assertions MUST keep this in mind - cwd, err := os.Getwd() - require.NoError(t, err) - - p := file.Path(path.Join(cwd, test.path)) + p := indexedPath(t, test.path) assert.Equal(t, true, tree.HasPath(p)) exists, ref, err := tree.File(p) assert.Equal(t, true, exists) @@ -287,6 +285,9 @@ func TestDirectoryIndexer_index_survive_badSymlink(t *testing.T) { } func TestDirectoryIndexer_index_survive_inaccessibleSymlinkTarget(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("relies on symlinks and posix permissions") + } // a symlink that resolves into a directory we don't have permission to traverse should be skipped // with a warning like any other inaccessible path, not abort the entire scan (see #3286). if os.Geteuid() == 0 { @@ -323,6 +324,9 @@ func TestDirectoryIndexer_index_survive_inaccessibleSymlinkTarget(t *testing.T) } func TestDirectoryIndexer_SkipsAlreadyVisitedLinkDestinations(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("fixture relies on symlinks, which git does not check out as links on windows by default") + } var observedPaths []string pathObserver := func(_, p string, _ os.FileInfo, _ error) error { fields := strings.Split(p, "testdata/symlinks-prune-indexing") @@ -430,6 +434,11 @@ func TestDirectoryIndexer_IndexesAllTypes(t *testing.T) { } func Test_allContainedPaths(t *testing.T) { + if runtime.GOOS == "windows" { + // ponytail: production passes native windows paths here, which these posix cases don't represent. + // Ancestor indexing for a root reached through a symlink is likely broken on windows; fix separately. + t.Skip("cases are posix paths") + } tests := []struct { name string @@ -485,6 +494,9 @@ func Test_allContainedPaths(t *testing.T) { } func Test_relativePath(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("exercises a posix-only test helper") + } tests := []struct { name string basePath string @@ -548,3 +560,17 @@ func relativePath(basePath, givenPath string) string { return relPath } + +// indexedPath returns how the given cwd-relative path is keyed in the index (absolute and always posix) +func indexedPath(t *testing.T, rel string) file.Path { + t.Helper() + cwd, err := os.Getwd() + require.NoError(t, err) + // filepath (not path): cwd is a native host path, so it must be joined with the host separator before + // being converted to the posix form the index is keyed on + p := filepath.Join(cwd, rel) + if windows.HostRunningOnWindows() { + p = windows.ToPosix(p) + } + return file.Path(p) +} diff --git a/syft/internal/fileresolver/directory_roundtrip_test.go b/syft/internal/fileresolver/directory_roundtrip_test.go new file mode 100644 index 000000000..22f39cc18 --- /dev/null +++ b/syft/internal/fileresolver/directory_roundtrip_test.go @@ -0,0 +1,48 @@ +package fileresolver + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + stereoscopeFile "github.com/anchore/stereoscope/pkg/file" +) + +// a location handed out by the resolver should be resolvable again by its own path. This is what the file +// digest cataloger relies on, and it silently found nothing on windows (see #5325). +func TestDirectory_LocationPathsRoundTrip(t *testing.T) { + // resolve symlinks up front (e.g. /var -> /private/var on macOS) so the cwd prefix matches the indexed paths + root, err := filepath.EvalSymlinks(t.TempDir()) + require.NoError(t, err) + require.NoError(t, os.MkdirAll(filepath.Join(root, "sub", "dir"), 0o755)) + for _, p := range []string{"top.txt", filepath.Join("sub", "mid.txt"), filepath.Join("sub", "dir", "deep.txt")} { + require.NoError(t, os.WriteFile(filepath.Join(root, p), []byte(p), 0o600)) + } + + // mirror `syft dir:.` from within the scanned directory + t.Chdir(root) + + resolver, err := NewFromDirectory(".", "") + require.NoError(t, err) + + var regular int + for loc := range resolver.AllLocations(context.Background()) { + md, err := resolver.FileMetadataByLocation(loc) + require.NoError(t, err) + if md.Type != stereoscopeFile.TypeRegular { + continue + } + regular++ + + assert.NotContains(t, loc.RealPath, `\`, "location paths should be posix") + + found, err := resolver.FilesByPath(loc.RealPath) + require.NoError(t, err) + assert.Len(t, found, 1, "unable to resolve %q by its own path", loc.RealPath) + } + assert.Equal(t, 3, regular) +} diff --git a/syft/internal/fileresolver/directory_windows_test.go b/syft/internal/fileresolver/directory_windows_test.go deleted file mode 100644 index 115fb30a3..000000000 --- a/syft/internal/fileresolver/directory_windows_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package fileresolver - -import "testing" - -func Test_windowsToPosix(t *testing.T) { - type args struct { - windowsPath string - } - tests := []struct { - name string - args args - wantPosixPath string - }{ - { - name: "basic case", - args: args{ - windowsPath: `C:\some\windows\place`, - }, - wantPosixPath: "/c/some/windows/place", - }, - { - name: "escaped case", - args: args{ - windowsPath: `C:\\some\\windows\\place`, - }, - wantPosixPath: "/c/some/windows/place", - }, - { - name: "forward slash", - args: args{ - windowsPath: `C:/foo/bar`, - }, - wantPosixPath: "/c/foo/bar", - }, - { - name: "mix slash", - args: args{ - windowsPath: `C:\foo/bar\`, - }, - wantPosixPath: "/c/foo/bar", - }, - { - name: "case sensitive case", - args: args{ - windowsPath: `C:\Foo/bAr\`, - }, - wantPosixPath: "/c/Foo/bAr", - }, - { - name: "special char case", - args: args{ - windowsPath: `C:\ふー\バー`, - }, - wantPosixPath: "/c/ふー/バー", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if gotPosixPath := windowsToPosix(tt.args.windowsPath); gotPosixPath != tt.wantPosixPath { - t.Errorf("windowsToPosix() = %v, want %v", gotPosixPath, tt.wantPosixPath) - } - }) - } -} - -func Test_posixToWindows(t *testing.T) { - type args struct { - posixPath string - } - tests := []struct { - name string - args args - wantWindowsPath string - }{ - { - name: "basic case", - args: args{ - posixPath: "/c/some/windows/place", - }, - wantWindowsPath: `C:\some\windows\place`, - }, - { - name: "escaped case", - args: args{ - posixPath: "/c/some/windows/place", - }, - wantWindowsPath: `C:\\some\\windows\\place`, - }, - { - name: "forward slash", - args: args{ - posixPath: "/c/foo/bar", - }, - wantWindowsPath: `C:/foo/bar`, - }, - { - name: "mix slash", - args: args{ - posixPath: "/c/foo/bar", - }, - wantWindowsPath: `C:\foo/bar\`, - }, - { - name: "case sensitive case", - args: args{ - posixPath: "/c/Foo/bAr", - }, - wantWindowsPath: `C:\Foo/bAr\`, - }, - { - name: "special char case", - args: args{ - posixPath: "/c/ふー/バー", - }, - wantWindowsPath: `C:\ふー\バー`, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if gotWindowsPath := posixToWindows(tt.args.posixPath); gotWindowsPath != tt.wantWindowsPath { - t.Errorf("posixToWindows() = %v, want %v", gotWindowsPath, tt.wantWindowsPath) - } - }) - } -} diff --git a/syft/internal/fileresolver/file_indexer_test.go b/syft/internal/fileresolver/file_indexer_test.go index d9ba1d8c9..a4e0364d5 100644 --- a/syft/internal/fileresolver/file_indexer_test.go +++ b/syft/internal/fileresolver/file_indexer_test.go @@ -3,13 +3,11 @@ package fileresolver import ( "io/fs" "os" - "path" + "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/anchore/stereoscope/pkg/file" ) // - Verify that both the parent and the path are indexed @@ -38,11 +36,7 @@ func Test_index(t *testing.T) { info, err := os.Stat(test.path) assert.NoError(t, err) - // note: the index uses absolute paths, so assertions MUST keep this in mind - cwd, err := os.Getwd() - require.NoError(t, err) - - p := file.Path(path.Join(cwd, test.path)) + p := indexedPath(t, test.path) assert.Equal(t, true, tree.HasPath(p)) exists, ref, err := tree.File(p) assert.Equal(t, true, exists) @@ -71,7 +65,9 @@ func Test_ignoresPathIfFiltered(t *testing.T) { testPath := "testdata/system_paths/target/home/place" cwd, cwdErr := os.Getwd() require.NoError(t, cwdErr) - ignorePath := path.Join(cwd, testPath) + // filepath (not path): filters see the native absolute path (before posix normalization), so the + // expected value must be joined with the host separator to match on windows + ignorePath := filepath.Join(cwd, testPath) filterFn := func(_, path string, _ os.FileInfo, _ error) error { if path == ignorePath { return ErrSkipPath @@ -91,7 +87,8 @@ func Test_ignoresPathIfParentFiltered(t *testing.T) { cwd, cwdErr := os.Getwd() require.NoError(t, cwdErr) - ignorePath := path.Join(cwd, parentPath) + // filepath (not path): see Test_ignoresPathIfFiltered + ignorePath := filepath.Join(cwd, parentPath) filterFn := func(_, path string, _ os.FileInfo, _ error) error { if path == ignorePath { return fs.SkipDir diff --git a/syft/internal/fileresolver/filetree_resolver.go b/syft/internal/fileresolver/filetree_resolver.go index 68b3b8395..20c60b57a 100644 --- a/syft/internal/fileresolver/filetree_resolver.go +++ b/syft/internal/fileresolver/filetree_resolver.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os" + "path/filepath" stereoscopeFile "github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/filetree" @@ -34,8 +35,17 @@ func nativeOSFileOpener(ref stereoscopeFile.Reference) (io.ReadCloser, error) { return stereoscopeFile.NewLazyReadCloser(filePath), nil } +// requestPath converts a user (chroot) path into the path the file tree is keyed on. The tree is always posix +// (windows paths are volume-encoded, e.g. /c/some/path), while ToNativePath yields a native host path. func (r *FiletreeResolver) requestPath(userPath string) (string, error) { - return r.Chroot.ToNativePath(userPath) + nativePath, err := r.Chroot.ToNativePath(userPath) + if err != nil { + return "", err + } + if windows.HostRunningOnWindows() { + return windows.ToPosix(nativePath), nil + } + return nativePath, nil } // responsePath takes a path from the underlying fs domain and converts it to a path that is relative to the root of the file resolver. @@ -85,10 +95,6 @@ func (r FiletreeResolver) FilesByPath(userPaths ...string) ([]file.Location, err continue } - if windows.HostRunningOnWindows() { - userStrPath = windows.ToPosix(userStrPath) - } - if ref.HasReference() { references = append(references, file.NewVirtualLocationFromDirectory( @@ -103,8 +109,19 @@ func (r FiletreeResolver) FilesByPath(userPaths ...string) ([]file.Location, err return references, nil } +// requestGlob is the glob equivalent of requestPath. func (r FiletreeResolver) requestGlob(pattern string) (string, error) { - return r.Chroot.ToNativeGlob(pattern) + glob, err := r.Chroot.ToNativeGlob(pattern) + if err != nil { + return "", err + } + // only a glob that was anchored to the root comes back native. Globs that start with a wildcard + // (e.g. **/foo) are returned untouched and are already posix, and ToPosix would root them at "/". + // This uses filepath (not path) since we are asking whether this is a native absolute path. + if windows.HostRunningOnWindows() && filepath.IsAbs(glob) { + return windows.ToPosix(glob), nil + } + return glob, nil } // FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image. diff --git a/syft/internal/windows/path.go b/syft/internal/windows/path.go index c091b89fd..fc16cbe5f 100644 --- a/syft/internal/windows/path.go +++ b/syft/internal/windows/path.go @@ -35,6 +35,10 @@ func ToPosix(windowsPath string) (posixPath string) { func FromPosix(posixPath string) (windowsPath string) { // decode the volume (e.g. /c/ --> C:\\) - There should always be a volume name. // The volume may be a UNC path (e.g. /\\localhost\C$\ --> \\localhost\C$\) + if !strings.HasPrefix(posixPath, "/") { + // not a volume-encoded posix path (e.g. relative or already native), so there is nothing to decode + return posixPath + } pathFields := strings.Split(posixPath, "/") if len(pathFields) < 2 { // no separator means no volume to decode diff --git a/syft/internal/windows/path_windows_test.go b/syft/internal/windows/path_windows_test.go new file mode 100644 index 000000000..ad278fb9e --- /dev/null +++ b/syft/internal/windows/path_windows_test.go @@ -0,0 +1,35 @@ +package windows + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestToPosix(t *testing.T) { + tests := map[string]string{ + `C:\some\windows\place`: "/c/some/windows/place", + `C:\\some\\windows\\place`: "/c/some/windows/place", + `C:/foo/bar`: "/c/foo/bar", + `C:\foo/bar\`: "/c/foo/bar", + `C:\Foo/bAr\`: "/c/Foo/bAr", + `C:\ふー\バー`: "/c/ふー/バー", + } + for in, want := range tests { + assert.Equal(t, want, ToPosix(in), in) + } +} + +func TestFromPosix(t *testing.T) { + tests := map[string]string{ + "/c/some/windows/place": `C:\some\windows\place`, + "/c/Foo/bAr": `C:\Foo\bAr`, + "/c/ふー/バー": `C:\ふー\バー`, + // non volume-encoded input is left alone + "relative/path": "relative/path", + `D:\already\native`: `D:\already\native`, + } + for in, want := range tests { + assert.Equal(t, want, FromPosix(in), in) + } +} diff --git a/syft/source/directorysource/directory_source.go b/syft/source/directorysource/directory_source.go index dcbbeaa62..259ab2cac 100644 --- a/syft/source/directorysource/directory_source.go +++ b/syft/source/directorysource/directory_source.go @@ -216,7 +216,8 @@ func cleanDirPath(path, base string) string { relPath, err := filepath.Rel(cleanBase, cleanRoot) if err == nil { - path = relPath + // filepath.Rel yields a native path, but source names are posix like every other path syft reports + path = filepath.ToSlash(relPath) } // this is odd, but this means we can't use base } diff --git a/syft/source/directorysource/directory_source_test.go b/syft/source/directorysource/directory_source_test.go index fc4160e58..88e7b3430 100644 --- a/syft/source/directorysource/directory_source_test.go +++ b/syft/source/directorysource/directory_source_test.go @@ -4,6 +4,7 @@ import ( "io/fs" "os" "path/filepath" + "runtime" "testing" "github.com/google/go-cmp/cmp" @@ -325,6 +326,9 @@ func Test_DirectorySource_Exclusions(t *testing.T) { } func Test_getDirectoryExclusionFunctions_crossPlatform(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("emulates windows paths on a posix host, see directory_source_win_test.go for the windows equivalent") + } testCases := []struct { desc string root string @@ -465,10 +469,11 @@ func Test_DirectorySource_ID(t *testing.T) { testutil.Chdir(t, "..") // run with source/testdata tests := []struct { - name string - cfg Config - want artifact.ID - wantErr require.ErrorAssertionFunc + name string + cfg Config + want artifact.ID + wantErr require.ErrorAssertionFunc + skipOnWindows bool }{ { name: "empty", @@ -486,6 +491,8 @@ func Test_DirectorySource_ID(t *testing.T) { name: "with odd unclean path through non-existent directory", cfg: Config{Path: "testdata/does-not-exist/../"}, wantErr: require.Error, + // windows cleans the path lexically, so the non-existent directory is never visited + skipOnWindows: true, }, { name: "to a file (not a directory)", @@ -543,6 +550,9 @@ func Test_DirectorySource_ID(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if tt.skipOnWindows && runtime.GOOS == "windows" { + t.Skip("posix-specific path behavior") + } if tt.wantErr == nil { tt.wantErr = require.NoError } diff --git a/syft/source/directorysource/directory_source_win_test.go b/syft/source/directorysource/directory_source_win_test.go index 8f99a59d3..d7d38f86a 100644 --- a/syft/source/directorysource/directory_source_win_test.go +++ b/syft/source/directorysource/directory_source_win_test.go @@ -56,8 +56,9 @@ func Test_DirectorySource_crossPlatformExclusions(t *testing.T) { require.NoError(t, err) for _, f := range fns { - result := f(test.path, nil, nil) - require.Equal(t, test.match, result) + // a matched exclusion is signaled by a skip error + err := f("", test.path, nil, nil) + require.Equal(t, test.match, err != nil) } }) } diff --git a/syft/source/filesource/file_source_test.go b/syft/source/filesource/file_source_test.go index 87fe2a795..bba5c61e2 100644 --- a/syft/source/filesource/file_source_test.go +++ b/syft/source/filesource/file_source_test.go @@ -6,6 +6,7 @@ import ( "os/exec" "path" "path/filepath" + "runtime" "syscall" "testing" @@ -96,6 +97,9 @@ func TestNewFromFile(t *testing.T) { } func TestNewFromFile_WithArchive(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("archive fixtures are generated with a shell script") + } testutil.Chdir(t, "..") // run with source/testdata testCases := []struct {