aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/testing/testing.go
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-12-23 09:57:37 -0800
committerIan Lance Taylor <iant@golang.org>2020-12-30 15:13:24 -0800
commitcfcbb4227fb20191e04eb8d7766ae6202f526afd (patch)
treee2effea96f6f204451779f044415c2385e45042b /libgo/go/testing/testing.go
parent0696141107d61483f38482b941549959a0d7f613 (diff)
downloadgcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.zip
gcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.tar.gz
gcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.tar.bz2
libgo: update to Go1.16beta1 release
This does not yet include support for the //go:embed directive added in this release. * Makefile.am (check-runtime): Don't create check-runtime-dir. (mostlyclean-local): Don't remove check-runtime-dir. (check-go-tool, check-vet): Copy in go.mod and modules.txt. (check-cgo-test, check-carchive-test): Add go.mod file. * Makefile.in: Regenerate. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/280172
Diffstat (limited to 'libgo/go/testing/testing.go')
-rw-r--r--libgo/go/testing/testing.go195
1 files changed, 139 insertions, 56 deletions
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go
index 80282fc..795ee32 100644
--- a/libgo/go/testing/testing.go
+++ b/libgo/go/testing/testing.go
@@ -242,7 +242,6 @@ import (
"fmt"
"internal/race"
"io"
- "io/ioutil"
"os"
"runtime"
"runtime/debug"
@@ -294,6 +293,7 @@ func Init() {
blockProfileRate = flag.Int("test.blockprofilerate", 1, "set blocking profile `rate` (see runtime.SetBlockProfileRate)")
mutexProfile = flag.String("test.mutexprofile", "", "write a mutex contention profile to the named file after execution")
mutexProfileFraction = flag.Int("test.mutexprofilefraction", 1, "if >= 0, calls runtime.SetMutexProfileFraction()")
+ panicOnExit0 = flag.Bool("test.paniconexit0", false, "panic on call to os.Exit(0)")
traceFile = flag.String("test.trace", "", "write an execution trace to `file`")
timeout = flag.Duration("test.timeout", 0, "panic test binary after duration `d` (default 0, timeout disabled)")
cpuListStr = flag.String("test.cpu", "", "comma-separated `list` of cpu counts to run each test with")
@@ -320,6 +320,7 @@ var (
blockProfileRate *int
mutexProfile *string
mutexProfileFraction *int
+ panicOnExit0 *bool
traceFile *string
timeout *time.Duration
cpuListStr *string
@@ -382,17 +383,18 @@ const maxStackLen = 50
// common holds the elements common between T and B and
// captures common methods such as Errorf.
type common struct {
- mu sync.RWMutex // guards this group of fields
- output []byte // Output generated by test or benchmark.
- w io.Writer // For flushToParent.
- ran bool // Test or benchmark (or one of its subtests) was executed.
- failed bool // Test or benchmark has failed.
- skipped bool // Test of benchmark has been skipped.
- done bool // Test is finished and all subtests have completed.
- helpers map[string]struct{} // functions to be skipped when writing file/line info
- cleanup func() // optional function to be called at the end of the test
- cleanupName string // Name of the cleanup function.
- cleanupPc []uintptr // The stack trace at the point where Cleanup was called.
+ mu sync.RWMutex // guards this group of fields
+ output []byte // Output generated by test or benchmark.
+ w io.Writer // For flushToParent.
+ ran bool // Test or benchmark (or one of its subtests) was executed.
+ failed bool // Test or benchmark has failed.
+ skipped bool // Test of benchmark has been skipped.
+ done bool // Test is finished and all subtests have completed.
+ helperPCs map[uintptr]struct{} // functions to be skipped when writing file/line info
+ helperNames map[string]struct{} // helperPCs converted to function names
+ cleanups []func() // optional functions to be called at the end of the test
+ cleanupName string // Name of the cleanup function.
+ cleanupPc []uintptr // The stack trace at the point where Cleanup was called.
chatty *chattyPrinter // A copy of chattyPrinter, if the chatty flag is set.
bench bool // Whether the current test is a benchmark.
@@ -411,10 +413,10 @@ type common struct {
signal chan bool // To signal a test is done.
sub []*T // Queue of subtests to be run in parallel.
- tempDirOnce sync.Once
- tempDir string
- tempDirErr error
- tempDirSeq int32
+ tempDirMu sync.Mutex
+ tempDir string
+ tempDirErr error
+ tempDirSeq int32
}
// Short reports whether the -test.short flag is set.
@@ -507,7 +509,7 @@ func (c *common) frameSkip(skip int) runtime.Frame {
}
return prevFrame
}
- if _, ok := c.helpers[frame.Function]; !ok {
+ if _, ok := c.helperNames[frame.Function]; !ok {
// Found a frame that wasn't inside a helper function.
return frame
}
@@ -519,6 +521,14 @@ func (c *common) frameSkip(skip int) runtime.Frame {
// and inserts the final newline if needed and indentation spaces for formatting.
// This function must be called with c.mu held.
func (c *common) decorate(s string, skip int) string {
+ // If more helper PCs have been added since we last did the conversion
+ if c.helperNames == nil {
+ c.helperNames = make(map[string]struct{})
+ for pc := range c.helperPCs {
+ c.helperNames[pcToName(pc)] = struct{}{}
+ }
+ }
+
frame := c.frameSkip(skip)
file := frame.File
line := frame.Line
@@ -887,38 +897,50 @@ func (c *common) Skipped() bool {
func (c *common) Helper() {
c.mu.Lock()
defer c.mu.Unlock()
- if c.helpers == nil {
- c.helpers = make(map[string]struct{})
+ if c.helperPCs == nil {
+ c.helperPCs = make(map[uintptr]struct{})
+ }
+ // repeating code from callerName here to save walking a stack frame
+ var pc [1]uintptr
+ n := runtime.Callers(2, pc[:]) // skip runtime.Callers + Helper
+ if n == 0 {
+ panic("testing: zero callers found")
+ }
+ if _, found := c.helperPCs[pc[0]]; !found {
+ c.helperPCs[pc[0]] = struct{}{}
+ c.helperNames = nil // map will be recreated next time it is needed
}
- c.helpers[callerName(1)] = struct{}{}
}
// Cleanup registers a function to be called when the test and all its
// subtests complete. Cleanup functions will be called in last added,
// first called order.
func (c *common) Cleanup(f func()) {
- c.mu.Lock()
- defer c.mu.Unlock()
- oldCleanup := c.cleanup
- oldCleanupPc := c.cleanupPc
- c.cleanup = func() {
- if oldCleanup != nil {
- defer func() {
- c.mu.Lock()
- c.cleanupPc = oldCleanupPc
- c.mu.Unlock()
- oldCleanup()
- }()
- }
+ var pc [maxStackLen]uintptr
+ // Skip two extra frames to account for this function and runtime.Callers itself.
+ n := runtime.Callers(2, pc[:])
+ cleanupPc := pc[:n]
+
+ fn := func() {
+ defer func() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.cleanupName = ""
+ c.cleanupPc = nil
+ }()
+
+ name := callerName(0)
c.mu.Lock()
- c.cleanupName = callerName(0)
+ c.cleanupName = name
+ c.cleanupPc = cleanupPc
c.mu.Unlock()
+
f()
}
- var pc [maxStackLen]uintptr
- // Skip two extra frames to account for this function and runtime.Callers itself.
- n := runtime.Callers(2, pc[:])
- c.cleanupPc = pc[:n]
+
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.cleanups = append(c.cleanups, fn)
}
var tempDirReplacer struct {
@@ -934,17 +956,29 @@ var tempDirReplacer struct {
func (c *common) TempDir() string {
// Use a single parent directory for all the temporary directories
// 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()
- // ioutil.TempDir doesn't like path separators in its pattern,
+ // os.MkdirTemp doesn't like path separators in its pattern,
// so mangle the name to accommodate subtests.
tempDirReplacer.Do(func() {
tempDirReplacer.r = strings.NewReplacer("/", "_", "\\", "_", ":", "_")
})
pattern := tempDirReplacer.r.Replace(c.Name())
- c.tempDir, c.tempDirErr = ioutil.TempDir("", pattern)
+ c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern)
if c.tempDirErr == nil {
c.Cleanup(func() {
if err := os.RemoveAll(c.tempDir); err != nil {
@@ -952,7 +986,9 @@ func (c *common) TempDir() string {
}
})
}
- })
+ }
+ c.tempDirMu.Unlock()
+
if c.tempDirErr != nil {
c.Fatalf("TempDir: %v", c.tempDirErr)
}
@@ -976,34 +1012,53 @@ const (
// If catchPanic is true, this will catch panics, and return the recovered
// value if any.
func (c *common) runCleanup(ph panicHandling) (panicVal interface{}) {
- c.mu.Lock()
- cleanup := c.cleanup
- c.cleanup = nil
- c.mu.Unlock()
- if cleanup == nil {
- return nil
- }
-
if ph == recoverAndReturnPanic {
defer func() {
panicVal = recover()
}()
}
- cleanup()
- return nil
+ // Make sure that if a cleanup function panics,
+ // we still run the remaining cleanup functions.
+ defer func() {
+ c.mu.Lock()
+ recur := len(c.cleanups) > 0
+ c.mu.Unlock()
+ if recur {
+ c.runCleanup(normalPanic)
+ }
+ }()
+
+ for {
+ var cleanup func()
+ c.mu.Lock()
+ if len(c.cleanups) > 0 {
+ last := len(c.cleanups) - 1
+ cleanup = c.cleanups[last]
+ c.cleanups = c.cleanups[:last]
+ }
+ c.mu.Unlock()
+ if cleanup == nil {
+ return nil
+ }
+ cleanup()
+ }
}
// callerName gives the function name (qualified with a package path)
// for the caller after skip frames (where 0 means the current function).
func callerName(skip int) string {
- // Make room for the skip PC.
- var pc [2]uintptr
+ var pc [1]uintptr
n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName
if n == 0 {
panic("testing: zero callers found")
}
- frames := runtime.CallersFrames(pc[:n])
+ return pcToName(pc[0])
+}
+
+func pcToName(pc uintptr) string {
+ pcs := []uintptr{pc}
+ frames := runtime.CallersFrames(pcs)
frame, _ := frames.Next()
return frame.Function
}
@@ -1077,6 +1132,7 @@ func tRunner(t *T, fn func(t *T)) {
// If the test panicked, print any test output before dying.
err := recover()
signal := true
+
if !t.finished && err == nil {
err = errNilPanicOrGoexit
for p := t.parent; p != nil; p = p.parent {
@@ -1088,6 +1144,21 @@ func tRunner(t *T, fn func(t *T)) {
}
}
}
+ // Use a deferred call to ensure that we report that the test is
+ // complete even if a cleanup function calls t.FailNow. See issue 41355.
+ didPanic := false
+ defer func() {
+ if didPanic {
+ return
+ }
+ if err != nil {
+ panic(err)
+ }
+ // Only report that the test is complete if it doesn't panic,
+ // as otherwise the test binary can exit before the panic is
+ // reported to the user. See issue 41479.
+ t.signal <- signal
+ }()
doPanic := func(err interface{}) {
t.Fail()
@@ -1105,6 +1176,7 @@ func tRunner(t *T, fn func(t *T)) {
fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r)
}
}
+ didPanic = true
panic(err)
}
if err != nil {
@@ -1146,7 +1218,6 @@ func tRunner(t *T, fn func(t *T)) {
if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 {
t.setRan()
}
- t.signal <- signal
}()
defer func() {
if len(t.sub) == 0 {
@@ -1287,6 +1358,7 @@ func (f matchStringOnly) WriteProfileTo(string, io.Writer, int) error { return e
func (f matchStringOnly) ImportPath() string { return "" }
func (f matchStringOnly) StartTestLog(io.Writer) {}
func (f matchStringOnly) StopTestLog() error { return errMain }
+func (f matchStringOnly) SetPanicOnExit0(bool) {}
// Main is an internal function, part of the implementation of the "go test" command.
// It was exported because it is cross-package and predates "internal" packages.
@@ -1322,6 +1394,7 @@ type M struct {
type testDeps interface {
ImportPath() string
MatchString(pat, str string) (bool, error)
+ SetPanicOnExit0(bool)
StartCPUProfile(io.Writer) error
StopCPUProfile()
StartTestLog(io.Writer)
@@ -1547,6 +1620,9 @@ func (m *M) before() {
m.deps.StartTestLog(f)
testlogFile = f
}
+ if *panicOnExit0 {
+ m.deps.SetPanicOnExit0(true)
+ }
}
// after runs after all testing.
@@ -1554,6 +1630,13 @@ func (m *M) after() {
m.afterOnce.Do(func() {
m.writeProfiles()
})
+
+ // Restore PanicOnExit0 after every run, because we set it to true before
+ // every run. Otherwise, if m.Run is called multiple times the behavior of
+ // os.Exit(0) will not be restored after the second run.
+ if *panicOnExit0 {
+ m.deps.SetPanicOnExit0(false)
+ }
}
func (m *M) writeProfiles() {