diff --git a/go.mod b/go.mod index 6555bb8..48ac549 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/adrg/xdg go 1.14 -require github.com/stretchr/testify v1.6.1 +require github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index afe7890..26500d5 100644 --- a/go.sum +++ b/go.sum @@ -2,9 +2,10 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/paths_plan9.go b/paths_plan9.go index 05638af..b35b02b 100644 --- a/paths_plan9.go +++ b/paths_plan9.go @@ -1,5 +1,3 @@ -// +build plan9 - package xdg import ( diff --git a/paths_unix.go b/paths_unix.go index 8dd0766..3edef5f 100644 --- a/paths_unix.go +++ b/paths_unix.go @@ -1,4 +1,4 @@ -// +build aix dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix dragonfly freebsd js,wasm nacl linux netbsd openbsd solaris package xdg diff --git a/paths_unix_test.go b/paths_unix_test.go index 685caa3..fbe412c 100644 --- a/paths_unix_test.go +++ b/paths_unix_test.go @@ -1,4 +1,4 @@ -// +build aix dragonfly freebsd linux nacl netbsd openbsd solaris +// +build aix dragonfly freebsd js,wasm nacl linux netbsd openbsd solaris package xdg_test diff --git a/paths_windows_test.go b/paths_windows_test.go index bd08043..d4142da 100644 --- a/paths_windows_test.go +++ b/paths_windows_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/adrg/xdg" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestDefaultBaseDirs(t *testing.T) { @@ -74,30 +74,30 @@ func TestDefaultBaseDirs(t *testing.T) { } // Test environment variable fallbacks. - assert.NoError(t, os.Unsetenv("APPDATA")) - assert.NoError(t, os.Unsetenv("LOCALAPPDATA")) - assert.NoError(t, os.Unsetenv("PROGRAMDATA")) - assert.NoError(t, os.Unsetenv("windir")) - assert.NoError(t, os.Setenv("SystemDrive", home)) - assert.NoError(t, os.Setenv("SystemRoot", winDir)) + require.NoError(t, os.Unsetenv("APPDATA")) + require.NoError(t, os.Unsetenv("LOCALAPPDATA")) + require.NoError(t, os.Unsetenv("PROGRAMDATA")) + require.NoError(t, os.Unsetenv("windir")) + require.NoError(t, os.Setenv("SystemDrive", home)) + require.NoError(t, os.Setenv("SystemRoot", winDir)) testDirs(t, envSamples...) // Test environment variables set. - assert.NoError(t, os.Setenv("APPDATA", appData)) - assert.NoError(t, os.Setenv("LOCALAPPDATA", localAppData)) - assert.NoError(t, os.Setenv("PROGRAMDATA", programData)) - assert.NoError(t, os.Setenv("windir", winDir)) + require.NoError(t, os.Setenv("APPDATA", appData)) + require.NoError(t, os.Setenv("LOCALAPPDATA", localAppData)) + require.NoError(t, os.Setenv("PROGRAMDATA", programData)) + require.NoError(t, os.Setenv("windir", winDir)) testDirs(t, envSamples...) // Test no environment variables set. - assert.NoError(t, os.Unsetenv("APPDATA")) - assert.NoError(t, os.Unsetenv("LOCALAPPDATA")) - assert.NoError(t, os.Unsetenv("PROGRAMDATA")) - assert.NoError(t, os.Unsetenv("windir")) - assert.NoError(t, os.Unsetenv("SystemDrive")) - assert.NoError(t, os.Unsetenv("SystemRoot")) + require.NoError(t, os.Unsetenv("APPDATA")) + require.NoError(t, os.Unsetenv("LOCALAPPDATA")) + require.NoError(t, os.Unsetenv("PROGRAMDATA")) + require.NoError(t, os.Unsetenv("windir")) + require.NoError(t, os.Unsetenv("SystemDrive")) + require.NoError(t, os.Unsetenv("SystemRoot")) envSamples[1].expected = []string{roamingAppData, home} envSamples[3].expected = []string{home} @@ -115,9 +115,9 @@ func TestCustomBaseDirs(t *testing.T) { localAppData := filepath.Join(appData, "Local") programData := filepath.Join(home, "ProgramData") - assert.NoError(t, os.Setenv("APPDATA", appData)) - assert.NoError(t, os.Setenv("LOCALAPPDATA", localAppData)) - assert.NoError(t, os.Setenv("PROGRAMDATA", programData)) + require.NoError(t, os.Setenv("APPDATA", appData)) + require.NoError(t, os.Setenv("LOCALAPPDATA", localAppData)) + require.NoError(t, os.Setenv("PROGRAMDATA", programData)) testDirs(t, &envSample{ @@ -213,11 +213,11 @@ func TestDefaultUserDirs(t *testing.T) { } // Test %PUBLIC% not set. - assert.NoError(t, os.Unsetenv("PUBLIC")) + require.NoError(t, os.Unsetenv("PUBLIC")) testDirs(t, samples...) // Test %PUBLIC% set. - assert.NoError(t, os.Setenv("PUBLIC", public)) + require.NoError(t, os.Setenv("PUBLIC", public)) testDirs(t, samples...) } diff --git a/stat.go b/stat.go new file mode 100644 index 0000000..4ddcf8b --- /dev/null +++ b/stat.go @@ -0,0 +1,10 @@ +// +build !windows + +package xdg + +import "os" + +func pathExists(path string) bool { + _, err := os.Stat(path) + return err == nil || os.IsExist(err) +} diff --git a/stat_windows.go b/stat_windows.go new file mode 100644 index 0000000..8065975 --- /dev/null +++ b/stat_windows.go @@ -0,0 +1,15 @@ +package xdg + +import ( + "os" + "path/filepath" +) + +func pathExists(path string) bool { + fi, err := os.Lstat(path) + if fi != nil && fi.Mode()&os.ModeSymlink != 0 { + _, err = filepath.EvalSymlinks(path) + } + + return err == nil || os.IsExist(err) +} diff --git a/utils.go b/utils.go index 8d5b196..8cd8104 100644 --- a/utils.go +++ b/utils.go @@ -33,11 +33,6 @@ func homeDir() string { return "" } -func exists(path string) bool { - _, err := os.Stat(path) - return err == nil || os.IsExist(err) -} - func expandPath(path, homeDir string) string { if path == "" || homeDir == "" { return path @@ -58,7 +53,7 @@ func createPath(name string, paths []string) (string, error) { path := filepath.Join(p, name) dir := filepath.Dir(path) - if exists(dir) { + if pathExists(dir) { return path, nil } if err := os.MkdirAll(dir, os.ModeDir|0700); err == nil { @@ -76,7 +71,7 @@ func searchFile(name string, paths []string) (string, error) { var searchedPaths []string for _, p := range paths { path := filepath.Join(p, name) - if exists(path) { + if pathExists(path) { return path, nil } diff --git a/xdg_test.go b/xdg_test.go index b0146ff..ef8fa19 100644 --- a/xdg_test.go +++ b/xdg_test.go @@ -2,10 +2,11 @@ package xdg_test import ( "os" + "path/filepath" "testing" "github.com/adrg/xdg" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type envSample struct { @@ -17,14 +18,12 @@ type envSample struct { func testDirs(t *testing.T, samples ...*envSample) { // Test home directory. - if !assert.NotEmpty(t, xdg.Home) { - t.FailNow() - } + require.NotEmpty(t, xdg.Home) t.Logf("Home: %s", xdg.Home) // Set environment variables. for _, sample := range samples { - assert.NoError(t, os.Setenv(sample.name, sample.value)) + require.NoError(t, os.Setenv(sample.name, sample.value)) } xdg.Reload() @@ -38,19 +37,19 @@ func testDirs(t *testing.T, samples ...*envSample) { actual = *v } - assert.Equal(t, sample.expected, actual) + require.Equal(t, sample.expected, actual) t.Logf("%s: %v", sample.name, actual) } } -func TestBaseDirFuncs(t *testing.T) { - type inputData struct { - relPaths []string - pathFunc func(string) (string, error) - searchFunc func(string) (string, error) - } +type testInputData struct { + relPaths []string + pathFunc func(string) (string, error) + searchFunc func(string) (string, error) +} - inputs := []*inputData{ +func TestBaseDirFuncs(t *testing.T) { + inputs := []*testInputData{ { relPaths: []string{"app.data", "appname/app.data"}, pathFunc: xdg.DataFile, @@ -78,33 +77,93 @@ func TestBaseDirFuncs(t *testing.T) { }, } + // Test base directories for regular files. + testBaseDirsRegular(t, inputs) + + // Test base directories for symbolic links. + for _, input := range inputs { + input.relPaths = []string{input.relPaths[1]} + } + + testBaseDirsSymlinks(t, inputs) +} + +func testBaseDirsRegular(t *testing.T, inputs []*testInputData) { for _, input := range inputs { for _, relPath := range input.relPaths { // Get suitable path for input file. expFullPath, err := input.pathFunc(relPath) - assert.NoError(t, err) + require.NoError(t, err) // Create input file. f, err := os.Create(expFullPath) - assert.NoError(t, err) - assert.NoError(t, f.Close()) + require.NoError(t, err) + require.NoError(t, f.Close()) // Search input file after creation. actFullPath, err := input.searchFunc(relPath) - assert.NoError(t, err) - assert.Equal(t, expFullPath, actFullPath) + require.NoError(t, err) + require.Equal(t, expFullPath, actFullPath) // Remove created file. - assert.NoError(t, os.Remove(expFullPath)) + require.NoError(t, os.Remove(expFullPath)) + + // Search input file after removal. + _, err = input.searchFunc(relPath) + require.Error(t, err) + + // Check that the same path is returned. + actFullPath, err = input.pathFunc(relPath) + require.NoError(t, err) + require.Equal(t, expFullPath, actFullPath) + } + } +} + +func testBaseDirsSymlinks(t *testing.T, inputs []*testInputData) { + for _, input := range inputs { + for _, relPath := range input.relPaths { + // Get suitable path for input file. + expFullPath, err := input.pathFunc(relPath) + require.NoError(t, err) + + // Replace input directory with symlink. + symlinkDir := filepath.Dir(expFullPath) + inputDir := filepath.Join(filepath.Dir(symlinkDir), "inputdir") + + require.NoError(t, os.Remove(symlinkDir)) + require.NoError(t, os.Mkdir(inputDir, os.ModeDir|0700)) + require.NoError(t, os.Symlink(inputDir, symlinkDir)) + + // Create input file. + inputPath := filepath.Join(symlinkDir, "input.file") + + f, err := os.Create(inputPath) + require.NoError(t, err) + require.NoError(t, f.Close()) + + // Create symbolic link. + require.NoError(t, os.Symlink(inputPath, expFullPath)) + + // Search input file after creation. + actFullPath, err := input.searchFunc(relPath) + require.NoError(t, err) + require.Equal(t, expFullPath, actFullPath) + + // Remove created symbolic links, files and directories. + require.NoError(t, os.Remove(expFullPath)) + require.NoError(t, os.Remove(inputPath)) + require.NoError(t, os.Remove(symlinkDir)) + require.NoError(t, os.Remove(inputDir)) // Search input file after removal. _, err = input.searchFunc(relPath) - assert.Error(t, err) + require.Error(t, err) // Check that the same path is returned. actFullPath, err = input.pathFunc(relPath) - assert.NoError(t, err) - assert.Equal(t, expFullPath, actFullPath) + require.NoError(t, err) + require.Equal(t, expFullPath, actFullPath) } } }