From e9b3ff15f40d6b258217b3467c662f816b078477 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Mon, 8 Jan 2024 17:20:14 -0500 Subject: [PATCH] os: relax tests and add examples for UserCacheDir and UserConfigDir MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Per https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html: “If, when attempting to write a file, the destination directory is non-existent an attempt should be made to create it with permission 0700. […] The application should be prepared to handle the case where the file could not be written […]. In such case it may choose to present an error message to the user.” In certain CI environments, these directories have well-defined locations but do not exist and cannot be created. In that case, we now choose to log and return from the test without failing it. To prevent the functions from falling back to being entirely untested, we still fail the test (and “present an error message to the user”) if either function returns an empty string without an error, or returns a path that refers to a non-directory or results in an error other than ErrNotExist. In addition, since the tests themselves no longer create subdirectories, we add examples illustrating the suggested pattern of usage. Fixes #64990. Change-Id: Ie72106424f5ebe36eaf9288c22710d74bb14a462 Reviewed-on: https://go-review.googlesource.com/c/go/+/554815 Reviewed-by: Austin Clements Run-TryBot: Bryan Mills LUCI-TryBot-Result: Go LUCI TryBot-Result: Gopher Robot Auto-Submit: Bryan Mills --- src/os/example_test.go | 76 ++++++++++++++++++++++++++++++++++++++++++ src/os/os_test.go | 34 ++++++++++--------- 2 files changed, 95 insertions(+), 15 deletions(-) diff --git a/src/os/example_test.go b/src/os/example_test.go index 656232c472..7437a74cd0 100644 --- a/src/os/example_test.go +++ b/src/os/example_test.go @@ -5,12 +5,14 @@ package os_test import ( + "bytes" "errors" "fmt" "io/fs" "log" "os" "path/filepath" + "sync" "time" ) @@ -317,3 +319,77 @@ func ExampleReadlink() { // Output: // hello.link links to hello.txt } + +func ExampleUserCacheDir() { + dir, dirErr := os.UserCacheDir() + if dirErr == nil { + dir = filepath.Join(dir, "ExampleUserCacheDir") + } + + getCache := func(name string) ([]byte, error) { + if dirErr != nil { + return nil, &os.PathError{Op: "getCache", Path: name, Err: os.ErrNotExist} + } + return os.ReadFile(filepath.Join(dir, name)) + } + + var mkdirOnce sync.Once + putCache := func(name string, b []byte) error { + if dirErr != nil { + return &os.PathError{Op: "putCache", Path: name, Err: dirErr} + } + mkdirOnce.Do(func() { + if err := os.MkdirAll(dir, 0700); err != nil { + log.Printf("can't create user cache dir: %v", err) + } + }) + return os.WriteFile(filepath.Join(dir, name), b, 0600) + } + + // Read and store cached data. + // … + _ = getCache + _ = putCache + + // Output: +} + +func ExampleUserConfigDir() { + dir, dirErr := os.UserConfigDir() + + var ( + configPath string + origConfig []byte + ) + if dirErr == nil { + configPath = filepath.Join(dir, "ExampleUserConfigDir", "example.conf") + var err error + origConfig, err = os.ReadFile(configPath) + if err != nil && !os.IsNotExist(err) { + // The user has a config file but we couldn't read it. + // Report the error instead of ignoring their configuration. + log.Fatal(err) + } + } + + // Use and perhaps make changes to the config. + config := bytes.Clone(origConfig) + // … + + // Save changes. + if !bytes.Equal(config, origConfig) { + if configPath == "" { + log.Printf("not saving config changes: %v", dirErr) + } else { + err := os.MkdirAll(filepath.Dir(configPath), 0700) + if err == nil { + err = os.WriteFile(configPath, config, 0600) + } + if err != nil { + log.Printf("error saving config changes: %v", err) + } + } + } + + // Output: +} diff --git a/src/os/os_test.go b/src/os/os_test.go index 2f5b117bd9..6adc3b5479 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -11,6 +11,7 @@ import ( "internal/testenv" "io" "io/fs" + "log" . "os" "os/exec" "path/filepath" @@ -33,6 +34,8 @@ func TestMain(m *testing.M) { Exit(0) } + log.SetFlags(log.LstdFlags | log.Lshortfile) + Exit(m.Run()) } @@ -2847,16 +2850,17 @@ func TestUserCacheDir(t *testing.T) { t.Fatalf("UserCacheDir returned %q; want non-empty path or error", dir) } - if err := MkdirAll(dir, 0777); err != nil { - t.Fatalf("could not create UserCacheDir: %v", err) - } - d, err := MkdirTemp(dir, "TestUserCacheDir") + fi, err := Stat(dir) if err != nil { - t.Fatalf("could not create a directory in UserCacheDir: %v", err) - } - if err := Remove(d); err != nil { + if IsNotExist(err) { + t.Log(err) + return + } t.Fatal(err) } + if !fi.IsDir() { + t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode()) + } } func TestUserConfigDir(t *testing.T) { @@ -2870,17 +2874,17 @@ func TestUserConfigDir(t *testing.T) { t.Fatalf("UserConfigDir returned %q; want non-empty path or error", dir) } - if err := MkdirAll(dir, 0777); err != nil { - t.Fatalf("could not create UserConfigDir: %v", err) - } - - d, err := MkdirTemp(dir, "TestUserConfigDir") + fi, err := Stat(dir) if err != nil { - t.Fatalf("could not create a directory in UserConfigDir: %v", err) - } - if err := Remove(d); err != nil { + if IsNotExist(err) { + t.Log(err) + return + } t.Fatal(err) } + if !fi.IsDir() { + t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode()) + } } func TestUserHomeDir(t *testing.T) { -- 2.48.1