]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: fail fast across packages
authorAlexander Yastrebov <yastrebov.alex@gmail.com>
Sat, 17 Feb 2024 04:35:24 +0000 (04:35 +0000)
committerGopher Robot <gobot@golang.org>
Wed, 21 Feb 2024 19:25:24 +0000 (19:25 +0000)
Fixes #33038

Change-Id: I0b70c450be1c1cc59ddc1f3fddad227deccc7e14
GitHub-Last-Rev: 302ebd648afb4a5fc9ca0ae10cda5c58f2d64120
GitHub-Pull-Request: golang/go#62714
Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest-race,gotip-windows-amd64-race,gotip-windows-amd64-longtest
Reviewed-on: https://go-review.googlesource.com/c/go/+/529198
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
src/cmd/go/internal/test/test.go
src/cmd/go/internal/test/testflag.go
src/cmd/go/testdata/script/test_fail_fast.txt

index b2b5d340278a288d0fb2a004b14effd26be6e773..c3c50b3e0d94d0b5f067b837021c5ab6cb11b6f4 100644 (file)
@@ -21,6 +21,7 @@ import (
        "strconv"
        "strings"
        "sync"
+       "sync/atomic"
        "time"
 
        "cmd/go/internal/base"
@@ -540,6 +541,7 @@ var (
        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
@@ -589,9 +591,10 @@ var (
 
        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
 
@@ -1355,6 +1358,11 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
        // 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
@@ -1631,6 +1639,10 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
                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)
index 425378889d9af06da941763ab25c90ca4f81142c..4686e550fdc54d2186c68a8145ea1977bcab4981 100644 (file)
@@ -48,7 +48,7 @@ func init() {
        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", "", "")
index 132ea709eb6ea05421297f6e75fed1c6750c8bc9..1f169d6da8717ba39242edc6f13c3dd2433e7be1 100644 (file)
@@ -48,6 +48,15 @@ stdout -count=1 'FAIL - '
 ! 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
@@ -111,3 +120,27 @@ func TestFatalC(t *testing.T) {
 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())
+}