From 610a20c4d5fdef42e462ffde30fae41f2ba832c4 Mon Sep 17 00:00:00 2001 From: Dan Kegel Date: Sun, 16 Jan 2022 11:42:56 -0800 Subject: [PATCH] testing.go: hoist runTests() out of Run() to match upstream a bit better Only trivial functional changes: - gets rid of mistaken extra "no tests" warning (whoops) - matches upstream's exit code better In preparation for switching to fancy test filtering. --- src/testing/testing.go | 66 ++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/src/testing/testing.go b/src/testing/testing.go index 599ca258..a38db998 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -47,6 +47,7 @@ type common struct { output bytes.Buffer w io.Writer // either &output, or at top level, os.Stdout indent string + 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. cleanups []func() // optional functions to be called at the end of the test @@ -278,6 +279,7 @@ func tRunner(t *T, fn func(t *T)) { t.duration += time.Since(t.start) // TODO: capture cleanup time, too. t.report() // Report after all subtests have finished. + t.ran = true } // Run runs f as a subtest of t called name. It waits until the subtest is finished @@ -304,6 +306,10 @@ type M struct { Benchmarks []InternalBenchmark deps testDeps + + // value to pass to os.Exit, the outer test func main + // harness calls os.Exit with this code. See #34129. + exitCode int } type testDeps interface { @@ -319,39 +325,55 @@ func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenc } } -// Run the test suite. -func (m *M) Run() int { +// Run runs the tests. It returns an exit code to pass to os.Exit. +func (m *M) Run() (code int) { + defer func() { + code = m.exitCode + }() if !flag.Parsed() { flag.Parse() } - failures := 0 + testRan, testOk := runTests(m.deps.MatchString, m.Tests) + if !testRan && *matchBenchmarks == "" { + fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") + } + if !testOk || !runBenchmarks(m.deps.MatchString, m.Benchmarks) { + fmt.Println("FAIL") + m.exitCode = 1 + } else { + if flagVerbose { + fmt.Println("PASS") + } + m.exitCode = 0 + } + return +} + +func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ran, ok bool) { + ok = true if flagRunRegexp != "" { var filtered []InternalTest // pre-test the regexp; we don't want to bother logging one failure for every test name if the regexp is broken - if _, err := m.deps.MatchString(flagRunRegexp, "some-test-name"); err != nil { + if _, err := matchString(flagRunRegexp, "some-test-name"); err != nil { fmt.Println("testing: invalid regexp for -test.run:", err.Error()) - failures++ + return false, false } // filter the list of tests before we try to run them - for _, test := range m.Tests { + for _, test := range tests { // ignore the error; we already tested that the regexp compiles fine above - if match, _ := m.deps.MatchString(flagRunRegexp, test.Name); match { + if match, _ := matchString(flagRunRegexp, test.Name); match { filtered = append(filtered, test) } } - m.Tests = filtered + tests = filtered } - if len(m.Tests) == 0 && len(m.Benchmarks) == 0 { - fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") - } - - for _, test := range m.Tests { + for _, test := range tests { t := &T{ common: common{ name: test.Name, @@ -361,22 +383,10 @@ func (m *M) Run() int { tRunner(t, test.F) - if t.failed { - failures++ - } + ok = ok && !t.Failed() + ran = ran || t.ran } - - if len(m.Tests) == 0 && *matchBenchmarks == "" { - fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") - } - if failures > 0 || !runBenchmarks(m.deps.MatchString, m.Benchmarks) { - fmt.Println("FAIL") - } else { - if flagVerbose { - fmt.Println("PASS") - } - } - return failures + return ran, ok } func (t *T) report() {