diff --git a/assert_test.go b/assert_test.go index 865b5d7..e48ed7e 100644 --- a/assert_test.go +++ b/assert_test.go @@ -14,5 +14,5 @@ func (it *internalTest) Errorf(s string, args ...any) { } msg := strings.TrimSpace(fmt.Sprintf(s, args...)) it.capture = msg - fmt.Println(msg) + it.t.Log(msg) } diff --git a/internal/assertions/assertions.go b/internal/assertions/assertions.go index 727c24b..0dcc18e 100644 --- a/internal/assertions/assertions.go +++ b/internal/assertions/assertions.go @@ -1115,6 +1115,25 @@ func DirNotExistsFS(system fs.FS, directory string) (s string) { return } +func FileMode(path string, permissions fs.FileMode) (s string) { + info, err := os.Stat(path) + if err != nil { + s = "expected to stat path\n" + s += bullet(" name: %s\n", path) + s += bullet("error: %s\n", err) + return + } + + mode := info.Mode() + if permissions != mode { + s = "expected different file permissions\n" + s += bullet("name: %s\n", path) + s += bullet(" exp: %s\n", permissions) + s += bullet(" got: %s\n", mode) + } + return +} + func FileModeFS(system fs.FS, path string, permissions fs.FileMode) (s string) { info, err := fs.Stat(system, path) if err != nil { @@ -1134,6 +1153,29 @@ func FileModeFS(system fs.FS, path string, permissions fs.FileMode) (s string) { return } +func DirMode(path string, permissions fs.FileMode) (s string) { + info, err := os.Stat(path) + if err != nil { + s = "expected to stat path\n" + s += bullet(" name: %s\n", path) + s += bullet("error: %s\n", err) + return + } + if !info.IsDir() { + s = "expected to stat a directory\n" + s += bullet("name: %s\n", path) + return + } + mode := info.Mode() + if permissions != mode { + s = "expected different file permissions\n" + s += bullet("name: %s\n", path) + s += bullet(" exp: %s\n", permissions) + s += bullet(" got: %s\n", mode) + } + return +} + func DirModeFS(system fs.FS, path string, permissions fs.FileMode) (s string) { info, err := fs.Stat(system, path) if err != nil { diff --git a/internal/brokenfs/fs_default.go b/internal/brokenfs/fs_default.go deleted file mode 100644 index 3c1a993..0000000 --- a/internal/brokenfs/fs_default.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) The Test Authors -// SPDX-License-Identifier: MPL-2.0 - -//go:build !windows - -package brokenfs - -const ( - Root = "/" -) diff --git a/internal/brokenfs/fs_windows.go b/internal/brokenfs/fs_windows.go deleted file mode 100644 index 84bff50..0000000 --- a/internal/brokenfs/fs_windows.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) The Test Authors -// SPDX-License-Identifier: MPL-2.0 - -//go:build windows - -package brokenfs - -import ( - "os" -) - -var ( - Root = os.Getenv("HOMEDRIVE") -) - -func init() { - if Root == "" { - Root = "C:" - } -} diff --git a/must/assert_test.go b/must/assert_test.go index d922c0d..98b6ac8 100644 --- a/must/assert_test.go +++ b/must/assert_test.go @@ -14,5 +14,5 @@ func (it *internalTest) Fatalf(s string, args ...any) { } msg := strings.TrimSpace(fmt.Sprintf(s, args...)) it.capture = msg - fmt.Println(msg) + it.t.Log(msg) } diff --git a/must/must.go b/must/must.go index eb35397..e027474 100644 --- a/must/must.go +++ b/must/must.go @@ -8,13 +8,11 @@ package must import ( "io" "io/fs" - "os" "regexp" "strings" "github.com/shoenig/test/interfaces" "github.com/shoenig/test/internal/assertions" - "github.com/shoenig/test/internal/brokenfs" "github.com/shoenig/test/internal/constraints" "github.com/shoenig/test/internal/util" "github.com/shoenig/test/wait" @@ -609,8 +607,7 @@ func FileModeFS(t T, system fs.FS, path string, permissions fs.FileMode, setting // FileMode asserts the file or directory at path on the OS filesystem has exactly the given permission bits. func FileMode(t T, path string, permissions fs.FileMode, settings ...Setting) { t.Helper() - path = strings.TrimPrefix(path, "/") - invoke(t, assertions.FileModeFS(os.DirFS(brokenfs.Root), path, permissions), settings...) + invoke(t, assertions.FileMode(path, permissions), settings...) } // DirModeFS asserts the directory at path on fs.FS has exactly the given permission bits. @@ -625,8 +622,7 @@ func DirModeFS(t T, system fs.FS, path string, permissions fs.FileMode, settings // DirMode asserts the directory at path on the OS filesystem has exactly the given permission bits. func DirMode(t T, path string, permissions fs.FileMode, settings ...Setting) { t.Helper() - path = strings.TrimPrefix(path, "/") - invoke(t, assertions.DirModeFS(os.DirFS(brokenfs.Root), path, permissions), settings...) + invoke(t, assertions.DirMode(path, permissions), settings...) } // FileContainsFS asserts the file on fs.FS contains content as a substring. diff --git a/must/must_test.go b/must/must_test.go index 58961b5..3e16817 100644 --- a/must/must_test.go +++ b/must/must_test.go @@ -13,8 +13,8 @@ import ( "os" "path/filepath" "regexp" - "runtime" "testing" + "testing/fstest" "time" "github.com/shoenig/test/wait" @@ -24,12 +24,6 @@ import ( var _testdata embed.FS var testfs, _ = fs.Sub(_testdata, "testdata") -func needsOS(t *testing.T, os string) { - if os != runtime.GOOS { - t.Skip("not supported on this OS") - } -} - func TestNil(t *testing.T) { tc := newCase(t, `expected to be nil; is not nil`) t.Cleanup(tc.assert) @@ -1492,60 +1486,159 @@ func TestDirNotExists(t *testing.T) { } func TestFileModeFS(t *testing.T) { - needsOS(t, "linux") + t.Run("same permissions", func(t *testing.T) { + tc := newCase(t, "") + t.Cleanup(tc.assertNot) - tc := newCase(t, `expected different file permissions`) - t.Cleanup(tc.assert) + const name = "file1" + const perm = 0555 | fs.ModeDir + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: perm, + }, + } + FileModeFS(tc, system, name, perm) + }) + t.Run("different permissions", func(t *testing.T) { + tc := newCase(t, `expected different file permissions`) + t.Cleanup(tc.assert) - var unexpected os.FileMode = 0673 // (actual 0655) - FileModeFS(tc, os.DirFS("/bin"), "find", unexpected) + const name = "file1" + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: 0655, + }, + } + FileModeFS(tc, system, name, 0673) + }) +} + +func createFileWithPerm(t *testing.T, perm fs.FileMode) (path string) { + t.Helper() + f, err := os.CreateTemp(t.TempDir(), "") + if err != nil { + t.Fatal("failed to created temp file") + } + f.Close() + err = os.Chmod(f.Name(), perm) + if err != nil { + t.Fatal("failed to set file permissions") + } + t.Log("created temp file", f.Name()) + return f.Name() +} + +func createDirWithPerm(t *testing.T, perm fs.FileMode) (path string) { + t.Helper() + path, err := os.MkdirTemp(t.TempDir(), "") + if err != nil { + t.Fatal("failed to created temp dir") + } + err = os.Chmod(path, perm) + if err != nil { + t.Fatal("failed to set file permissions") + } + t.Log("created temp dir", path) + return path } func TestFileMode(t *testing.T) { - needsOS(t, "linux") + // Windows does not use Unix permissions. File permissions set on Windows + // with os.Chmod end up as something equivalent to -r--r--r-- or -rw-rw-rw-. - tc := newCase(t, `expected different file permissions`) - t.Cleanup(tc.assert) + t.Run("different permissions", func(t *testing.T) { + tc := newCase(t, `expected different file permissions`) + t.Cleanup(tc.assert) - var unexpected os.FileMode = 0673 // (actual 0655) - FileMode(tc, "/bin/find", unexpected) + path := createFileWithPerm(t, 0666) + FileMode(tc, path, 0755) + }) + + t.Run("same permissions", func(t *testing.T) { + tc := newCase(t, "") + t.Cleanup(tc.assertNot) + + const perm fs.FileMode = 0666 + path := createFileWithPerm(t, perm) + FileMode(tc, path, perm) + }) } func TestDirModeFS(t *testing.T) { - needsOS(t, "linux") - t.Run("different permissions", func(t *testing.T) { tc := newCase(t, `expected different file permissions`) t.Cleanup(tc.assert) - var unexpected os.FileMode = 0755 // (actual 0755) - DirModeFS(tc, os.DirFS("/"), "bin", unexpected) + const name = "dir1" + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: 0555 | fs.ModeDir, + }, + } + + var unexpected os.FileMode = 0755 | fs.ModeDir + DirModeFS(tc, system, name, unexpected) + }) + + t.Run("same permissions", func(t *testing.T) { + tc := newCase(t, `expected different file permissions`) + t.Cleanup(tc.assertNot) + + const name = "dir1" + const perm = 0555 | fs.ModeDir + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: perm, + }, + } + + DirModeFS(tc, system, name, perm) }) t.Run("not a dir", func(t *testing.T) { tc := newCase(t, `expected to stat a directory`) t.Cleanup(tc.assert) - DirModeFS(tc, os.DirFS("/bin"), "find", os.FileMode(0)) + const name = "file1" + const perm = 0555 + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: perm, + }, + } + + DirModeFS(tc, system, name, perm) }) } func TestDirMode(t *testing.T) { - needsOS(t, "linux") + // Windows does not use Unix permissions. Dir permissions set on Windows + // with os.Chmod end up as something equivalent to dr-xr-xr-x or drwxrwxrwx. t.Run("different permissions", func(t *testing.T) { tc := newCase(t, `expected different file permissions`) t.Cleanup(tc.assert) - var unexpected os.FileMode = 0755 // (actual 0755) - DirMode(tc, "/bin", unexpected) + path := createDirWithPerm(t, 0777) + DirMode(tc, path, 0755) + }) + + t.Run("same permissions", func(t *testing.T) { + tc := newCase(t, "") + t.Cleanup(tc.assertNot) + + const perm fs.FileMode = 0777 + path := createDirWithPerm(t, perm) + DirMode(tc, path, perm|fs.ModeDir) }) t.Run("not a dir", func(t *testing.T) { tc := newCase(t, `expected to stat a directory`) t.Cleanup(tc.assert) - DirMode(tc, "/bin/find", os.FileMode(0)) + const perm fs.FileMode = 0777 + path := createFileWithPerm(t, perm) + DirMode(tc, path, perm) }) } diff --git a/test.go b/test.go index 20effc9..f621f8a 100644 --- a/test.go +++ b/test.go @@ -6,13 +6,11 @@ package test import ( "io" "io/fs" - "os" "regexp" "strings" "github.com/shoenig/test/interfaces" "github.com/shoenig/test/internal/assertions" - "github.com/shoenig/test/internal/brokenfs" "github.com/shoenig/test/internal/constraints" "github.com/shoenig/test/internal/util" "github.com/shoenig/test/wait" @@ -607,8 +605,7 @@ func FileModeFS(t T, system fs.FS, path string, permissions fs.FileMode, setting // FileMode asserts the file or directory at path on the OS filesystem has exactly the given permission bits. func FileMode(t T, path string, permissions fs.FileMode, settings ...Setting) { t.Helper() - path = strings.TrimPrefix(path, "/") - invoke(t, assertions.FileModeFS(os.DirFS(brokenfs.Root), path, permissions), settings...) + invoke(t, assertions.FileMode(path, permissions), settings...) } // DirModeFS asserts the directory at path on fs.FS has exactly the given permission bits. @@ -623,8 +620,7 @@ func DirModeFS(t T, system fs.FS, path string, permissions fs.FileMode, settings // DirMode asserts the directory at path on the OS filesystem has exactly the given permission bits. func DirMode(t T, path string, permissions fs.FileMode, settings ...Setting) { t.Helper() - path = strings.TrimPrefix(path, "/") - invoke(t, assertions.DirModeFS(os.DirFS(brokenfs.Root), path, permissions), settings...) + invoke(t, assertions.DirMode(path, permissions), settings...) } // FileContainsFS asserts the file on fs.FS contains content as a substring. diff --git a/test_test.go b/test_test.go index 953d89c..579f0ac 100644 --- a/test_test.go +++ b/test_test.go @@ -11,8 +11,8 @@ import ( "os" "path/filepath" "regexp" - "runtime" "testing" + "testing/fstest" "time" "github.com/shoenig/test/wait" @@ -22,12 +22,6 @@ import ( var _testdata embed.FS var testfs, _ = fs.Sub(_testdata, "testdata") -func needsOS(t *testing.T, os string) { - if os != runtime.GOOS { - t.Skip("not supported on this OS") - } -} - func TestNil(t *testing.T) { tc := newCase(t, `expected to be nil; is not nil`) t.Cleanup(tc.assert) @@ -1490,60 +1484,159 @@ func TestDirNotExists(t *testing.T) { } func TestFileModeFS(t *testing.T) { - needsOS(t, "linux") + t.Run("same permissions", func(t *testing.T) { + tc := newCase(t, "") + t.Cleanup(tc.assertNot) - tc := newCase(t, `expected different file permissions`) - t.Cleanup(tc.assert) + const name = "file1" + const perm = 0555 | fs.ModeDir + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: perm, + }, + } + FileModeFS(tc, system, name, perm) + }) + t.Run("different permissions", func(t *testing.T) { + tc := newCase(t, `expected different file permissions`) + t.Cleanup(tc.assert) - var unexpected os.FileMode = 0673 // (actual 0655) - FileModeFS(tc, os.DirFS("/bin"), "find", unexpected) + const name = "file1" + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: 0655, + }, + } + FileModeFS(tc, system, name, 0673) + }) +} + +func createFileWithPerm(t *testing.T, perm fs.FileMode) (path string) { + t.Helper() + f, err := os.CreateTemp(t.TempDir(), "") + if err != nil { + t.Fatal("failed to created temp file") + } + f.Close() + err = os.Chmod(f.Name(), perm) + if err != nil { + t.Fatal("failed to set file permissions") + } + t.Log("created temp file", f.Name()) + return f.Name() +} + +func createDirWithPerm(t *testing.T, perm fs.FileMode) (path string) { + t.Helper() + path, err := os.MkdirTemp(t.TempDir(), "") + if err != nil { + t.Fatal("failed to created temp dir") + } + err = os.Chmod(path, perm) + if err != nil { + t.Fatal("failed to set file permissions") + } + t.Log("created temp dir", path) + return path } func TestFileMode(t *testing.T) { - needsOS(t, "linux") + // Windows does not use Unix permissions. File permissions set on Windows + // with os.Chmod end up as something equivalent to -r--r--r-- or -rw-rw-rw-. - tc := newCase(t, `expected different file permissions`) - t.Cleanup(tc.assert) + t.Run("different permissions", func(t *testing.T) { + tc := newCase(t, `expected different file permissions`) + t.Cleanup(tc.assert) - var unexpected os.FileMode = 0673 // (actual 0655) - FileMode(tc, "/bin/find", unexpected) + path := createFileWithPerm(t, 0666) + FileMode(tc, path, 0755) + }) + + t.Run("same permissions", func(t *testing.T) { + tc := newCase(t, "") + t.Cleanup(tc.assertNot) + + const perm fs.FileMode = 0666 + path := createFileWithPerm(t, perm) + FileMode(tc, path, perm) + }) } func TestDirModeFS(t *testing.T) { - needsOS(t, "linux") - t.Run("different permissions", func(t *testing.T) { tc := newCase(t, `expected different file permissions`) t.Cleanup(tc.assert) - var unexpected os.FileMode = 0755 // (actual 0755) - DirModeFS(tc, os.DirFS("/"), "bin", unexpected) + const name = "dir1" + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: 0555 | fs.ModeDir, + }, + } + + var unexpected os.FileMode = 0755 | fs.ModeDir + DirModeFS(tc, system, name, unexpected) + }) + + t.Run("same permissions", func(t *testing.T) { + tc := newCase(t, `expected different file permissions`) + t.Cleanup(tc.assertNot) + + const name = "dir1" + const perm = 0555 | fs.ModeDir + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: perm, + }, + } + + DirModeFS(tc, system, name, perm) }) t.Run("not a dir", func(t *testing.T) { tc := newCase(t, `expected to stat a directory`) t.Cleanup(tc.assert) - DirModeFS(tc, os.DirFS("/bin"), "find", os.FileMode(0)) + const name = "file1" + const perm = 0555 + system := fstest.MapFS{ + name: &fstest.MapFile{ + Mode: perm, + }, + } + + DirModeFS(tc, system, name, perm) }) } func TestDirMode(t *testing.T) { - needsOS(t, "linux") + // Windows does not use Unix permissions. Dir permissions set on Windows + // with os.Chmod end up as something equivalent to dr-xr-xr-x or drwxrwxrwx. t.Run("different permissions", func(t *testing.T) { tc := newCase(t, `expected different file permissions`) t.Cleanup(tc.assert) - var unexpected os.FileMode = 0755 // (actual 0755) - DirMode(tc, "/bin", unexpected) + path := createDirWithPerm(t, 0777) + DirMode(tc, path, 0755) + }) + + t.Run("same permissions", func(t *testing.T) { + tc := newCase(t, "") + t.Cleanup(tc.assertNot) + + const perm fs.FileMode = 0777 + path := createDirWithPerm(t, perm) + DirMode(tc, path, perm|fs.ModeDir) }) t.Run("not a dir", func(t *testing.T) { tc := newCase(t, `expected to stat a directory`) t.Cleanup(tc.assert) - DirMode(tc, "/bin/find", os.FileMode(0)) + const perm fs.FileMode = 0777 + path := createFileWithPerm(t, perm) + DirMode(tc, path, perm) }) }