testing: make TempDir idempotent for both Cleanup and Benchmark

Ensures that calling TempDir() in either of Cleanup or Benchmark
doesn't cause test failures which were previously caused by the
created directory having been deleted after the first run, yet
we prevented the recreation of the directory due to our selection
of concurrency primitive sync.Once. This change recreates the
temporary directory if it doesn't exist, regardless of how
many times Cleanup and Benchmark are invoked.

Fixes #41062

Change-Id: I925d9f7207d7c369a193d1e17da7a59a586244a7
Reviewed-on: https://go-review.googlesource.com/c/go/+/251297
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Changkun Ou 2020-08-28 12:13:37 +02:00 committed by Emmanuel Odeke
parent 93810ac1f4
commit 721819e7ba
2 changed files with 52 additions and 6 deletions

View file

@ -413,7 +413,7 @@ type common struct {
signal chan bool // To signal a test is done. signal chan bool // To signal a test is done.
sub []*T // Queue of subtests to be run in parallel. sub []*T // Queue of subtests to be run in parallel.
tempDirOnce sync.Once tempDirMu sync.Mutex
tempDir string tempDir string
tempDirErr error tempDirErr error
tempDirSeq int32 tempDirSeq int32
@ -903,7 +903,19 @@ var tempDirReplacer struct {
func (c *common) TempDir() string { func (c *common) TempDir() string {
// Use a single parent directory for all the temporary directories // Use a single parent directory for all the temporary directories
// created by a test, each numbered sequentially. // created by a test, each numbered sequentially.
c.tempDirOnce.Do(func() { c.tempDirMu.Lock()
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() c.Helper()
// ioutil.TempDir doesn't like path separators in its pattern, // ioutil.TempDir doesn't like path separators in its pattern,
@ -921,7 +933,9 @@ func (c *common) TempDir() string {
} }
}) })
} }
}) }
c.tempDirMu.Unlock()
if c.tempDirErr != nil { if c.tempDirErr != nil {
c.Fatalf("TempDir: %v", c.tempDirErr) c.Fatalf("TempDir: %v", c.tempDirErr)
} }

View file

@ -19,6 +19,38 @@ func TestMain(m *testing.M) {
os.Exit(m.Run()) os.Exit(m.Run())
} }
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) { func TestTempDir(t *testing.T) {
testTempDir(t) testTempDir(t)
t.Run("InSubtest", testTempDir) t.Run("InSubtest", testTempDir)