]> Cypherpunks repositories - gostls13.git/commitdiff
testing: testing: add (*T).Deadline method for test timeout
authorBryan C. Mills <bcmills@google.com>
Wed, 23 Oct 2019 14:54:09 +0000 (10:54 -0400)
committerBryan C. Mills <bcmills@google.com>
Fri, 21 Feb 2020 17:46:33 +0000 (17:46 +0000)
Fixes #28135

Change-Id: I62818595eaf4a59d8b5c26cd6848c08fec795ad1
Reviewed-on: https://go-review.googlesource.com/c/go/+/202758
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
api/next.txt
doc/go1.15.html
src/cmd/go/testdata/script/test_deadline.txt [new file with mode: 0644]
src/testing/testing.go

index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ecc3c4f0b69cbeb0322d114eda8ca3d41d3cfce3 100644 (file)
@@ -0,0 +1 @@
+pkg testing, method (*T) Deadline() (time.Time, bool)
index 6c9952bafccf406e12b888c06e42c69b1293260c..a3a089e07e664aee05b2bf84288556f90987b0b9 100644 (file)
@@ -60,6 +60,15 @@ TODO
 TODO
 </p>
 
+<dl id="testing"><dt><a href="/pkg/testing/">testing</a></dt>
+  <dd>
+    <p><!-- golang.org/issue/28135 -->
+      The <code>testing.T</code> type now has a <code>Deadline</code> method
+      that reports the time at which the test binary will have exceeded its
+      timeout.
+    </p>
+</dl><!-- testing -->
+
 <h3 id="minor_library_changes">Minor changes to the library</h3>
 
 <p>
diff --git a/src/cmd/go/testdata/script/test_deadline.txt b/src/cmd/go/testdata/script/test_deadline.txt
new file mode 100644 (file)
index 0000000..5a19f65
--- /dev/null
@@ -0,0 +1,50 @@
+[short] skip
+
+go test -timeout=0 -run=TestNoDeadline
+go test -timeout=1m -run=TestDeadlineWithinMinute
+go test -timeout=1m -run=TestSubtestDeadlineWithinMinute
+
+-- deadline_test.go --
+package testing_test
+
+import (
+       "testing"
+       "time"
+)
+
+func TestNoDeadline(t *testing.T) {
+       d, ok := t.Deadline()
+       if ok || !d.IsZero() {
+               t.Fatalf("t.Deadline() = %v, %v; want 0, false", d, ok)
+       }
+}
+
+func TestDeadlineWithinMinute(t *testing.T) {
+       now := time.Now()
+       d, ok := t.Deadline()
+       if !ok || d.IsZero() {
+               t.Fatalf("t.Deadline() = %v, %v; want nonzero deadline", d, ok)
+       }
+       if !d.After(now) {
+               t.Fatalf("t.Deadline() = %v; want after start of test (%v)", d, now)
+       }
+       if d.Sub(now) > time.Minute {
+               t.Fatalf("t.Deadline() = %v; want within one minute of start of test (%v)", d, now)
+       }
+}
+
+func TestSubtestDeadlineWithinMinute(t *testing.T) {
+       t.Run("sub", func(t *testing.T) {
+               now := time.Now()
+               d, ok := t.Deadline()
+               if !ok || d.IsZero() {
+                       t.Fatalf("t.Deadline() = %v, %v; want nonzero deadline", d, ok)
+               }
+               if !d.After(now) {
+                       t.Fatalf("t.Deadline() = %v; want after start of test (%v)", d, now)
+               }
+               if d.Sub(now) > time.Minute {
+                       t.Fatalf("t.Deadline() = %v; want within one minute of start of test (%v)", d, now)
+               }
+       })
+}
index 8a0c7b3021ab2711122c9891d15e03a57454082f..4b424e6abbd5d2509a7d15d651b0b4618726044f 100644 (file)
@@ -1049,10 +1049,20 @@ func (t *T) Run(name string, f func(t *T)) bool {
        return !t.failed
 }
 
+// Deadline reports the time at which the test binary will have
+// exceeded the timeout specified by the -timeout flag.
+//
+// The ok result is false if the -timeout flag indicates “no timeout” (0).
+func (t *T) Deadline() (deadline time.Time, ok bool) {
+       deadline = t.context.deadline
+       return deadline, !deadline.IsZero()
+}
+
 // testContext holds all fields that are common to all tests. This includes
 // synchronization primitives to run at most *parallel tests.
 type testContext struct {
-       match *matcher
+       match    *matcher
+       deadline time.Time
 
        mu sync.Mutex
 
@@ -1195,9 +1205,9 @@ func (m *M) Run() int {
 
        m.before()
        defer m.after()
-       m.startAlarm()
+       deadline := m.startAlarm()
        haveExamples = len(m.examples) > 0
-       testRan, testOk := runTests(m.deps.MatchString, m.tests)
+       testRan, testOk := runTests(m.deps.MatchString, m.tests, deadline)
        exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
        m.stopAlarm()
        if !testRan && !exampleRan && *matchBenchmarks == "" {
@@ -1255,14 +1265,18 @@ func listTests(matchString func(pat, str string) (bool, error), tests []Internal
 // RunTests is an internal function but exported because it is cross-package;
 // it is part of the implementation of the "go test" command.
 func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) {
-       ran, ok := runTests(matchString, tests)
+       var deadline time.Time
+       if *timeout > 0 {
+               deadline = time.Now().Add(*timeout)
+       }
+       ran, ok := runTests(matchString, tests, deadline)
        if !ran && !haveExamples {
                fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
        }
        return ok
 }
 
-func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ran, ok bool) {
+func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest, deadline time.Time) (ran, ok bool) {
        ok = true
        for _, procs := range cpuList {
                runtime.GOMAXPROCS(procs)
@@ -1271,6 +1285,7 @@ func runTests(matchString func(pat, str string) (bool, error), tests []InternalT
                                break
                        }
                        ctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run"))
+                       ctx.deadline = deadline
                        t := &T{
                                common: common{
                                        signal:  make(chan bool),
@@ -1452,14 +1467,18 @@ func toOutputDir(path string) string {
 }
 
 // startAlarm starts an alarm if requested.
-func (m *M) startAlarm() {
-       if *timeout > 0 {
-               m.timer = time.AfterFunc(*timeout, func() {
-                       m.after()
-                       debug.SetTraceback("all")
-                       panic(fmt.Sprintf("test timed out after %v", *timeout))
-               })
+func (m *M) startAlarm() time.Time {
+       if *timeout <= 0 {
+               return time.Time{}
        }
+
+       deadline := time.Now().Add(*timeout)
+       m.timer = time.AfterFunc(*timeout, func() {
+               m.after()
+               debug.SetTraceback("all")
+               panic(fmt.Sprintf("test timed out after %v", *timeout))
+       })
+       return deadline
 }
 
 // stopAlarm turns off the alarm.