"strconv"
"strings"
"sync"
+ "sync/atomic"
"time"
"cmd/go/internal/base"
testC bool // -c flag
testCoverPkgs []*load.Package // -coverpkg flag
testCoverProfile string // -coverprofile flag
+ testFailFast bool // -failfast flag
testFuzz string // -fuzz flag
testJSON bool // -json flag
testList string // -list flag
testHelp bool // -help option passed to test via -args
- testKillTimeout = 100 * 365 * 24 * time.Hour // backup alarm; defaults to about a century if no timeout is set
- testWaitDelay time.Duration // how long to wait for output to close after a test binary exits; zero means unlimited
- testCacheExpire time.Time // ignore cached test results before this time
+ testKillTimeout = 100 * 365 * 24 * time.Hour // backup alarm; defaults to about a century if no timeout is set
+ testWaitDelay time.Duration // how long to wait for output to close after a test binary exits; zero means unlimited
+ testCacheExpire time.Time // ignore cached test results before this time
+ testShouldFailFast atomic.Bool // signals pending tests to fail fast
testBlockProfile, testCPUProfile, testMemProfile, testMutexProfile, testTrace string // profiling flag that limits test to one package
// Wait for previous test to get started and print its first json line.
select {
case <-r.prev:
+ // If should fail fast then release next test and exit.
+ if testShouldFailFast.Load() {
+ close(r.next)
+ return nil
+ }
case <-base.Interrupted:
// We can't wait for the previous test action to complete: we don't start
// new actions after an interrupt, so if that action wasn't already running
fmt.Fprintf(cmd.Stdout, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun)
r.c.saveOutput(a)
} else {
+ if testFailFast {
+ testShouldFailFast.Store(true)
+ }
+
base.SetExitStatus(1)
if cancelSignaled {
fmt.Fprintf(cmd.Stdout, "*** Test killed with %v: ran too long (%v).\n", base.SignalTrace, testKillTimeout)
cf.Int("count", 0, "")
cf.String("cpu", "", "")
cf.StringVar(&testCPUProfile, "cpuprofile", "", "")
- cf.Bool("failfast", false, "")
+ cf.BoolVar(&testFailFast, "failfast", false, "")
cf.StringVar(&testFuzz, "fuzz", "", "")
cf.Bool("fullpath", false, "")
cf.StringVar(&testList, "list", "", "")
! go test ./failfast_test.go -run='TestFatal[CD]' -failfast=false
stdout -count=2 'FAIL - '
+# cross package failfast
+! go test -p 1 -failfast ./a ./b ./c
+stdout -count=1 'FAIL - '
+stdout -count=1 'FAIL - TestFailingPkgA'
+
+-- go.mod --
+module m
+
+go 1.21.0
-- failfast_test.go --
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
func TestFatalD(t *testing.T) {
t.Fatalf("FAIL - %s", t.Name())
}
+-- a/a_test.go --
+package a
+
+import "testing"
+
+func TestFailingPkgA(t *testing.T) {
+ t.Errorf("FAIL - %s", t.Name())
+}
+-- b/b_test.go --
+package b
+
+import "testing"
+
+func TestFailingPkgB(t *testing.T) {
+ t.Errorf("FAIL - %s", t.Name())
+}
+-- c/c_test.go --
+package c
+
+import "testing"
+
+func TestFailingPkgC(t *testing.T) {
+ t.Errorf("FAIL - %s", t.Name())
+}