From 2718789bc7937c58a7a65e53d9cc941b04682f99 Mon Sep 17 00:00:00 2001 From: Emmanuel T Odeke Date: Wed, 2 Oct 2019 13:36:27 -0700 Subject: [PATCH] io/ioutil: support predictable prefix,suffix for TempDir with * Allow TempDir to create directories with predictable prefixes and suffixes, separated by the last "*", for example: "prefix*suffix" will now expand to "prefix" + + "suffix" RELNOTE=yes Fixes #33805. Change-Id: I85fa73ae6a684ce820d1810c82a60765eb9c4a42 Reviewed-on: https://go-review.googlesource.com/c/go/+/198488 Run-TryBot: Emmanuel Odeke TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/io/ioutil/example_test.go | 23 +++++++++++++++++ src/io/ioutil/tempfile.go | 32 +++++++++++++++--------- src/io/ioutil/tempfile_test.go | 45 ++++++++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 19 deletions(-) diff --git a/src/io/ioutil/example_test.go b/src/io/ioutil/example_test.go index a7d340b77f..bc2b6fba73 100644 --- a/src/io/ioutil/example_test.go +++ b/src/io/ioutil/example_test.go @@ -53,6 +53,29 @@ func ExampleTempDir() { } } +func ExampleTempDir_suffix() { + parentDir := os.TempDir() + logsDir, err := ioutil.TempDir(parentDir, "*-logs") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(logsDir) // clean up + + // Logs can be cleaned out earlier if needed by searching + // for all directories whose suffix ends in *-logs. + globPattern := filepath.Join(parentDir, "*-logs") + matches, err := filepath.Glob(globPattern) + if err != nil { + log.Fatalf("Failed to match %q: %v", globPattern, err) + } + + for _, match := range matches { + if err := os.RemoveAll(match); err != nil { + log.Printf("Failed to remove %q: %v", match, err) + } + } +} + func ExampleTempFile() { content := []byte("temporary file's content") tmpfile, err := ioutil.TempFile("", "example") diff --git a/src/io/ioutil/tempfile.go b/src/io/ioutil/tempfile.go index ba8783b9a0..3aa23c5f01 100644 --- a/src/io/ioutil/tempfile.go +++ b/src/io/ioutil/tempfile.go @@ -52,12 +52,7 @@ func TempFile(dir, pattern string) (f *os.File, err error) { dir = os.TempDir() } - var prefix, suffix string - if pos := strings.LastIndex(pattern, "*"); pos != -1 { - prefix, suffix = pattern[:pos], pattern[pos+1:] - } else { - prefix = pattern - } + prefix, suffix := prefixAndSuffix(pattern) nconflict := 0 for i := 0; i < 10000; i++ { @@ -76,21 +71,36 @@ func TempFile(dir, pattern string) (f *os.File, err error) { return } -// TempDir creates a new temporary directory in the directory dir -// with a name beginning with prefix and returns the path of the -// new directory. If dir is the empty string, TempDir uses the +// prefixAndSuffix splits pattern by the last wildcard "*", if applicable, +// returning prefix as the part before "*" and suffix as the part after "*". +func prefixAndSuffix(pattern string) (prefix, suffix string) { + if pos := strings.LastIndex(pattern, "*"); pos != -1 { + prefix, suffix = pattern[:pos], pattern[pos+1:] + } else { + prefix = pattern + } + return +} + +// TempDir creates a new temporary directory in the directory dir. +// The directory name is generated by taking pattern and applying a +// random string to the end. If pattern includes a "*", the random string +// replaces the last "*". TempDir returns the name of the new directory. +// If dir is the empty string, TempDir uses the // default directory for temporary files (see os.TempDir). // Multiple programs calling TempDir simultaneously // will not choose the same directory. It is the caller's responsibility // to remove the directory when no longer needed. -func TempDir(dir, prefix string) (name string, err error) { +func TempDir(dir, pattern string) (name string, err error) { if dir == "" { dir = os.TempDir() } + prefix, suffix := prefixAndSuffix(pattern) + nconflict := 0 for i := 0; i < 10000; i++ { - try := filepath.Join(dir, prefix+nextRandom()) + try := filepath.Join(dir, prefix+nextRandom()+suffix) err = os.Mkdir(try, 0700) if os.IsExist(err) { if nconflict++; nconflict > 10 { diff --git a/src/io/ioutil/tempfile_test.go b/src/io/ioutil/tempfile_test.go index 0758890b69..698ebabee9 100644 --- a/src/io/ioutil/tempfile_test.go +++ b/src/io/ioutil/tempfile_test.go @@ -54,18 +54,47 @@ func TestTempDir(t *testing.T) { t.Errorf("TempDir(`/_not_exists_`, `foo`) = %v, %v", name, err) } - dir := os.TempDir() - name, err = TempDir(dir, "ioutil_test") - if name == "" || err != nil { - t.Errorf("TempDir(dir, `ioutil_test`) = %v, %v", name, err) + tests := []struct { + pattern string + wantPrefix, wantSuffix string + }{ + {"ioutil_test", "ioutil_test", ""}, + {"ioutil_test*", "ioutil_test", ""}, + {"ioutil_test*xyz", "ioutil_test", "xyz"}, } - if name != "" { - os.Remove(name) - re := regexp.MustCompile("^" + regexp.QuoteMeta(filepath.Join(dir, "ioutil_test")) + "[0-9]+$") + + dir := os.TempDir() + + runTestTempDir := func(t *testing.T, pattern, wantRePat string) { + name, err := TempDir(dir, pattern) + if name == "" || err != nil { + t.Fatalf("TempDir(dir, `ioutil_test`) = %v, %v", name, err) + } + defer os.Remove(name) + + re := regexp.MustCompile(wantRePat) if !re.MatchString(name) { - t.Errorf("TempDir(`"+dir+"`, `ioutil_test`) created bad name %s", name) + t.Errorf("TempDir(%q, %q) created bad name\n\t%q\ndid not match pattern\n\t%q", dir, pattern, name, wantRePat) } } + + for _, tt := range tests { + t.Run(tt.pattern, func(t *testing.T) { + wantRePat := "^" + regexp.QuoteMeta(filepath.Join(dir, tt.wantPrefix)) + "[0-9]+" + regexp.QuoteMeta(tt.wantSuffix) + "$" + runTestTempDir(t, tt.pattern, wantRePat) + }) + } + + // Separately testing "*xyz" (which has no prefix). That is when constructing the + // pattern to assert on, as in the previous loop, using filepath.Join for an empty + // prefix filepath.Join(dir, ""), produces the pattern: + // ^[0-9]+xyz$ + // yet we just want to match + // "^/[0-9]+xyz" + t.Run("*xyz", func(t *testing.T) { + wantRePat := "^" + regexp.QuoteMeta(filepath.Join(dir)) + regexp.QuoteMeta(string(filepath.Separator)) + "[0-9]+xyz$" + runTestTempDir(t, "*xyz", wantRePat) + }) } // test that we return a nice error message if the dir argument to TempDir doesn't -- 2.48.1