Skip to content

Commit

Permalink
testing: implement testing.TempDir
Browse files Browse the repository at this point in the history
  • Loading branch information
dkegel-fastly committed Jan 11, 2022
1 parent 89f0700 commit e9fc27f
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 0 deletions.
70 changes: 70 additions & 0 deletions src/testing/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"io"
"os"
"strings"
"unicode"
"unicode/utf8"
)

// Testing flags.
Expand Down Expand Up @@ -54,6 +56,10 @@ type common struct {
parent *common
level int // Nesting depth of test or benchmark.
name string // Name of test or benchmark.

tempDir string
tempDirErr error
tempDirSeq int32
}

// TB is the interface common to T and B.
Expand Down Expand Up @@ -216,6 +222,70 @@ func (c *common) Cleanup(f func()) {
c.cleanups = append(c.cleanups, f)
}

// TempDir returns a temporary directory for the test to use.
// The directory is automatically removed by Cleanup when the test and
// all its subtests complete.
// Each subsequent call to t.TempDir returns a unique directory;
// if the directory creation fails, TempDir terminates the test by calling Fatal.
func (c *common) TempDir() string {
// Use a single parent directory for all the temporary directories
// created by a test, each numbered sequentially.
var nonExistent bool
if c.tempDir == "" { // Usually the case with js/wasm
nonExistent = true
} else {
_, err := os.Stat(c.tempDir)
nonExistent = os.IsNotExist(err)
if err != nil && !nonExistent {
c.Fatalf("TempDir: %v", err)
}
}

if nonExistent {
c.Helper()

// Drop unusual characters (such as path separators or
// characters interacting with globs) from the directory name to
// avoid surprising os.MkdirTemp behavior.
mapper := func(r rune) rune {
if r < utf8.RuneSelf {
const allowed = "!#$%&()+,-.=@^_{}~ "
if '0' <= r && r <= '9' ||
'a' <= r && r <= 'z' ||
'A' <= r && r <= 'Z' {
return r
}
if strings.ContainsRune(allowed, r) {
return r
}
} else if unicode.IsLetter(r) || unicode.IsNumber(r) {
return r
}
return -1
}
pattern := strings.Map(mapper, c.Name())
c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern)
if c.tempDirErr == nil {
c.Cleanup(func() {
if err := os.RemoveAll(c.tempDir); err != nil {
c.Errorf("TempDir RemoveAll cleanup: %v", err)
}
})
}
}

if c.tempDirErr != nil {
c.Fatalf("TempDir: %v", c.tempDirErr)
}
seq := c.tempDirSeq
c.tempDirSeq++
dir := fmt.Sprintf("%s%c%03d", c.tempDir, os.PathSeparator, seq)
if err := os.Mkdir(dir, 0777); err != nil {
c.Fatalf("TempDir: %v", err)
}
return dir
}

// runCleanup is called at the end of the test.
func (c *common) runCleanup() {
for {
Expand Down
109 changes: 109 additions & 0 deletions src/testing/testing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package testing_test

import (
"os"
"path/filepath"
"testing"
)

func TestTempDirInCleanup(t *testing.T) {
var dir string

t.Run("test", func(t *testing.T) {
t.Cleanup(func() {
dir = t.TempDir()
})
_ = t.TempDir()
})

fi, err := os.Stat(dir)
if fi != nil {
t.Fatalf("Directory %q from user Cleanup still exists", dir)
}
if !os.IsNotExist(err) {
t.Fatalf("Unexpected error: %v", err)
}
}

func TestTempDirInBenchmark(t *testing.T) {
testing.Benchmark(func(b *testing.B) {
if !b.Run("test", func(b *testing.B) {
// Add a loop so that the test won't fail. See issue 38677.
for i := 0; i < b.N; i++ {
_ = b.TempDir()
}
}) {
t.Fatal("Sub test failure in a benchmark")
}
})
}

func TestTempDir(t *testing.T) {
testTempDir(t)
t.Run("InSubtest", testTempDir)
t.Run("test/subtest", testTempDir)
t.Run("test\\subtest", testTempDir)
t.Run("test:subtest", testTempDir)
t.Run("test/..", testTempDir)
t.Run("../test", testTempDir)
t.Run("test[]", testTempDir)
t.Run("test*", testTempDir)
t.Run("äöüéè", testTempDir)
}

func testTempDir(t *testing.T) {
dirCh := make(chan string, 1)
t.Cleanup(func() {
// Verify directory has been removed.
select {
case dir := <-dirCh:
fi, err := os.Stat(dir)
if os.IsNotExist(err) {
// All good
return
}
if err != nil {
t.Fatal(err)
}
t.Errorf("directory %q still exists: %v, isDir=%v", dir, fi, fi.IsDir())
default:
if !t.Failed() {
t.Fatal("never received dir channel")
}
}
})

dir := t.TempDir()
if dir == "" {
t.Fatal("expected dir")
}
dir2 := t.TempDir()
if dir == dir2 {
t.Fatal("subsequent calls to TempDir returned the same directory")
}
if filepath.Dir(dir) != filepath.Dir(dir2) {
t.Fatalf("calls to TempDir do not share a parent; got %q, %q", dir, dir2)
}
dirCh <- dir
fi, err := os.Stat(dir)
if err != nil {
t.Fatal(err)
}
if !fi.IsDir() {
t.Errorf("dir %q is not a dir", dir)
}

glob := filepath.Join(dir, "*.txt")
if _, err := filepath.Glob(glob); err != nil {
t.Error(err)
}

err = os.Remove(dir)
if err != nil {
t.Errorf("unexpected files in TempDir")
}
}

0 comments on commit e9fc27f

Please sign in to comment.