mirror of
https://github.com/anchore/syft.git
synced 2026-11-06 01:16:32 +01:00
* ci: run the full unit suite on windows via make unit Drops the curated `unit:windows` subset; the windows job now bootstraps like linux and runs `make unit`. Exceptions get added as failures show up. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * ci: skip fixture refresh for windows unit tests binny has no windows release, so the bootstrap action can't install the tools the fixture refresh needs. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * test: skip linux container fixtures when docker can't run them Windows CI runners only run windows containers, so image fixtures go through `testutils.SkipWithoutLinuxContainers`. No behavior change on linux/mac. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: close leaked file handles and posix squashfs paths on windows - output writers are now closed on early returns in `scan`/`convert` (open handles block cleanup on windows) - `newSBOMMultiWriter` used `path.Dir` on host paths, so output subdirectories were never created on windows - squashfs walk paths were built with `filepath.Join` - skip a few posix-only test cases (dir perms, xdg fallback, go-rpmdb ndb handle leak upstream) Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix(golang): resolve go source module dir natively on windows - `parseGoModFile` passed the resolver's posix (`/c/...`) path to the go toolchain, so go source resolution silently found nothing on windows - skip container-built fixtures (go archs, java builds) and golden image tarballs on windows; stereoscope's layer cache filenames contain `:`, which windows rejects Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: keep posix paths posix on windows - `ChrootContext.ToChrootPath` nested volumes (`D:\cwd\C:\root`) when the scan root was on another drive, so the prefix was never trimmed - bitnami, python wheel/egg, and alpm parsers used `filepath` on posix location paths - alpm backup-file lookups silently missed on windows Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * ci: pull the test fixture cache for windows unit tests Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: don't map virtual file tree paths through the host on windows `FiletreeResolver` without a chroot (snaps) ran request paths through `filepath.Abs`, rooting them on the current volume (`/payload.txt` -> `/d/payload.txt`), so snap scans on windows found nothing. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: match tar entry globs as posix on windows - `matchesAnyGlob` used `doublestar.PathMatch` (host separator) on tar entry names, so tar-wrapped java archives silently produced nothing on windows - test helpers now treat binary snippet keys and `FromFile` locations as posix Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * incorporate windows path fix Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * incorporate go-rpmdb close fix Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * close squashfs fixture writers before finalizing - windows dir listings can report a zero size for files still open, so the manifest was packed empty (also adds a temporary windows diagnostic for the binary cataloger) Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * temp: surface windows binary cataloger diagnostics Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: keep directory symlink targets posix on windows - the directory indexer joined symlink targets with native separators, so links in the posix file tree pointed nowhere and globs on link names found nothing - binary fixture logical keys used native separators, so nested snippets were picked as the fixture dir on windows Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * keep addSymlinkToIndex under the funlen limit Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * drop windows skips covered by the stereoscope and go-rpmdb bumps Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
331 lines
6.7 KiB
Go
331 lines
6.7 KiB
Go
package options
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/adrg/xdg"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/anchore/go-homedir"
|
|
"github.com/anchore/syft/internal"
|
|
"github.com/anchore/syft/internal/cache"
|
|
)
|
|
|
|
func Test_defaultDir(t *testing.T) {
|
|
tmpDir := filepath.Join(t.TempDir(), "cache-temp")
|
|
xdgCacheDir := filepath.Join(tmpDir, "fake-xdg-cache")
|
|
homeDir := filepath.Join(tmpDir, "fake-home")
|
|
|
|
tests := []struct {
|
|
name string
|
|
env map[string]string
|
|
expected string
|
|
skipOnWindows bool
|
|
}{
|
|
{
|
|
name: "no-xdg",
|
|
env: map[string]string{
|
|
"HOME": homeDir,
|
|
},
|
|
expected: homeDir,
|
|
// xdg falls back to LOCALAPPDATA on windows, not HOME
|
|
skipOnWindows: true,
|
|
},
|
|
{
|
|
name: "xdg-cache",
|
|
env: map[string]string{
|
|
"XDG_CACHE_HOME": xdgCacheDir,
|
|
},
|
|
expected: xdgCacheDir,
|
|
},
|
|
}
|
|
|
|
// capture all the initial environment variables to reset them before we reset library caches
|
|
env := map[string]string{
|
|
"HOME": "",
|
|
"XDG_DATA_HOME": "",
|
|
"XDG_DATA_DIRS": "",
|
|
"XDG_CONFIG_HOME": "",
|
|
"XDG_CONFIG_DIRS": "",
|
|
"XDG_STATE_HOME": "",
|
|
"XDG_CACHE_HOME": "",
|
|
"XDG_RUNTIME_DIR": "",
|
|
}
|
|
for k := range env {
|
|
env[k] = os.Getenv(k)
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.skipOnWindows && runtime.GOOS == "windows" {
|
|
t.Skip("posix-only cache dir fallback")
|
|
}
|
|
restoreCache(t)
|
|
|
|
for k, v := range test.env {
|
|
patchEnv(t, k, v)
|
|
}
|
|
xdg.Reload()
|
|
|
|
got := defaultDir()
|
|
|
|
require.True(t, strings.HasPrefix(got, test.expected))
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_cacheOptions(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
tests := []struct {
|
|
name string
|
|
opts Cache
|
|
test func(t *testing.T)
|
|
}{
|
|
{
|
|
name: "disable-1",
|
|
opts: Cache{
|
|
Dir: "some-dir",
|
|
TTL: "0",
|
|
},
|
|
test: func(t *testing.T) {
|
|
c := cache.GetManager().GetCache("test-disable-1", "v-disable-1")
|
|
err := c.Write("key-disable-1", strings.NewReader("some-value-disable-1"))
|
|
require.NoError(t, err)
|
|
rdr, err := c.Read("key-disable-1")
|
|
require.Nil(t, rdr)
|
|
require.Error(t, err)
|
|
},
|
|
},
|
|
{
|
|
name: "disable-2",
|
|
opts: Cache{
|
|
Dir: "some-dir",
|
|
TTL: "0s",
|
|
},
|
|
test: func(t *testing.T) {
|
|
c := cache.GetManager().GetCache("test-disable-2", "v-disable-2")
|
|
err := c.Write("key-disable-2", strings.NewReader("some-value-disable-2"))
|
|
require.NoError(t, err)
|
|
rdr, err := c.Read("key-disable-2")
|
|
require.Nil(t, rdr)
|
|
require.Error(t, err)
|
|
},
|
|
},
|
|
|
|
{
|
|
name: "disable-3",
|
|
opts: Cache{
|
|
Dir: "some-dir",
|
|
TTL: "0d",
|
|
},
|
|
test: func(t *testing.T) {
|
|
c := cache.GetManager().GetCache("test-disable-3", "v-disable-3")
|
|
err := c.Write("key-disable-3", strings.NewReader("some-value-disable-3"))
|
|
require.NoError(t, err)
|
|
rdr, err := c.Read("key-disable-3")
|
|
require.Nil(t, rdr)
|
|
require.Error(t, err)
|
|
},
|
|
},
|
|
{
|
|
name: "in-memory",
|
|
opts: Cache{
|
|
Dir: "",
|
|
TTL: "10m",
|
|
},
|
|
test: func(t *testing.T) {
|
|
c := cache.GetManager().GetCache("test-mem", "v-mem")
|
|
err := c.Write("key-mem", strings.NewReader("some-value-mem"))
|
|
require.NoError(t, err)
|
|
rdr, err := c.Read("key-mem")
|
|
require.NotNil(t, rdr)
|
|
defer internal.CloseAndLogError(rdr, "")
|
|
require.NoError(t, err)
|
|
data, err := io.ReadAll(rdr)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "some-value-mem", string(data))
|
|
require.NoDirExists(t, filepath.Join("test-mem", "v-mem"))
|
|
},
|
|
},
|
|
{
|
|
name: "on disk",
|
|
opts: Cache{
|
|
Dir: tmp,
|
|
TTL: "10m",
|
|
},
|
|
test: func(t *testing.T) {
|
|
c := cache.GetManager().GetCache("test-disk", "v-disk")
|
|
err := c.Write("key-disk", strings.NewReader("some-value-disk"))
|
|
require.NoError(t, err)
|
|
rdr, err := c.Read("key-disk")
|
|
require.NotNil(t, rdr)
|
|
defer internal.CloseAndLogError(rdr, "")
|
|
require.NoError(t, err)
|
|
data, err := io.ReadAll(rdr)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "some-value-disk", string(data))
|
|
require.DirExists(t, filepath.Join(tmp, "test-disk", "v-disk"))
|
|
},
|
|
},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
original := cache.GetManager()
|
|
defer cache.SetManager(original)
|
|
|
|
err := test.opts.PostLoad()
|
|
require.NoError(t, err)
|
|
|
|
test.test(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_parseDuration(t *testing.T) {
|
|
tests := []struct {
|
|
duration string
|
|
expect time.Duration
|
|
err require.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
duration: "0d",
|
|
expect: 0,
|
|
},
|
|
{
|
|
duration: "0m",
|
|
expect: 0,
|
|
},
|
|
{
|
|
duration: "0s",
|
|
expect: 0,
|
|
},
|
|
{
|
|
duration: "0",
|
|
expect: 0,
|
|
},
|
|
{
|
|
duration: "1d",
|
|
expect: 24 * time.Hour,
|
|
},
|
|
{
|
|
duration: "7d",
|
|
expect: 7 * 24 * time.Hour,
|
|
},
|
|
{
|
|
duration: "365D",
|
|
expect: 365 * 24 * time.Hour,
|
|
},
|
|
{
|
|
duration: "7d1h1m1s",
|
|
expect: 7*24*time.Hour + time.Hour + time.Minute + time.Second,
|
|
},
|
|
{
|
|
duration: "7d 1h 1m 1s",
|
|
expect: 7*24*time.Hour + time.Hour + time.Minute + time.Second,
|
|
},
|
|
{
|
|
duration: "2h",
|
|
expect: 2 * time.Hour,
|
|
},
|
|
{
|
|
duration: "2h5m",
|
|
expect: 2*time.Hour + 5*time.Minute,
|
|
},
|
|
{
|
|
duration: "2h 5m",
|
|
expect: 2*time.Hour + 5*time.Minute,
|
|
},
|
|
{
|
|
duration: "d24h",
|
|
err: require.Error,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.duration, func(t *testing.T) {
|
|
got, err := parseDuration(test.duration)
|
|
if test.err != nil {
|
|
test.err(t, err)
|
|
return
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
require.Equal(t, test.expect, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_durationToString(t *testing.T) {
|
|
tests := []struct {
|
|
duration time.Duration
|
|
expect string
|
|
err require.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
expect: "1d",
|
|
duration: 24 * time.Hour,
|
|
},
|
|
{
|
|
expect: "7d",
|
|
duration: 7 * 24 * time.Hour,
|
|
},
|
|
{
|
|
expect: "7d1h1m1s",
|
|
duration: 7*24*time.Hour + time.Hour + time.Minute + time.Second,
|
|
},
|
|
{
|
|
expect: "2h0m0s",
|
|
duration: 2 * time.Hour,
|
|
},
|
|
{
|
|
expect: "2h5m0s",
|
|
duration: 2*time.Hour + 5*time.Minute,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.expect, func(t *testing.T) {
|
|
got := durationToString(test.duration)
|
|
require.Equal(t, test.expect, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
// restoreCache ensures cache settings are restored after test
|
|
func restoreCache(t testing.TB) {
|
|
t.Helper()
|
|
origEnabled := homedir.CacheEnabled()
|
|
|
|
t.Cleanup(func() {
|
|
homedir.SetCacheEnable(origEnabled)
|
|
homedir.Reset()
|
|
})
|
|
}
|
|
|
|
// patchEnv modifies an environment variable for the duration of the test
|
|
func patchEnv(t testing.TB, key, value string) {
|
|
t.Helper()
|
|
original := os.Getenv(key)
|
|
|
|
if value != "" {
|
|
os.Setenv(key, value)
|
|
} else {
|
|
os.Unsetenv(key)
|
|
}
|
|
|
|
t.Cleanup(func() {
|
|
if original != "" {
|
|
os.Setenv(key, original)
|
|
} else {
|
|
os.Unsetenv(key)
|
|
}
|
|
xdg.Reload()
|
|
})
|
|
}
|