Commit 43f954e0 authored by Russ Cox's avatar Russ Cox

testing: mark tests and benchmarks failed if a race occurs during execution

Before:

$ go test -race -v -run TestRace
=== RUN   TestRace
==================
WARNING: DATA RACE
Write at 0x00c420076420 by goroutine 7:
  _/Users/rsc/go/src/cmd/go/testdata/src/testrace.TestRace.func1()
      /Users/rsc/go/src/cmd/go/testdata/src/testrace/race_test.go:10 +0x3b

Previous write at 0x00c420076420 by goroutine 6:
  _/Users/rsc/go/src/cmd/go/testdata/src/testrace.TestRace()
      /Users/rsc/go/src/cmd/go/testdata/src/testrace/race_test.go:13 +0xcc
  testing.tRunner()
      /Users/rsc/go/src/testing/testing.go:656 +0x104

Goroutine 7 (running) created at:
  _/Users/rsc/go/src/cmd/go/testdata/src/testrace.TestRace()
      /Users/rsc/go/src/cmd/go/testdata/src/testrace/race_test.go:12 +0xbb
  testing.tRunner()
      /Users/rsc/go/src/testing/testing.go:656 +0x104

Goroutine 6 (running) created at:
  testing.(*T).Run()
      /Users/rsc/go/src/testing/testing.go:693 +0x536
  testing.runTests.func1()
      /Users/rsc/go/src/testing/testing.go:877 +0xaa
  testing.tRunner()
      /Users/rsc/go/src/testing/testing.go:656 +0x104
  testing.runTests()
      /Users/rsc/go/src/testing/testing.go:883 +0x4ac
  testing.(*M).Run()
      /Users/rsc/go/src/testing/testing.go:818 +0x1c3
  main.main()
      _/Users/rsc/go/src/cmd/go/testdata/src/testrace/_test/_testmain.go:42 +0x20f
==================
--- PASS: TestRace (0.00s)
PASS
Found 1 data race(s)
FAIL	_/Users/rsc/go/src/cmd/go/testdata/src/testrace	1.026s
$

After:

$ go test -race -v -run TestRace
=== RUN   TestRace
==================
WARNING: DATA RACE
Write at 0x00c420076420 by goroutine 7:
  _/Users/rsc/go/src/cmd/go/testdata/src/testrace.TestRace.func1()
      /Users/rsc/go/src/cmd/go/testdata/src/testrace/race_test.go:10 +0x3b

Previous write at 0x00c420076420 by goroutine 6:
  _/Users/rsc/go/src/cmd/go/testdata/src/testrace.TestRace()
      /Users/rsc/go/src/cmd/go/testdata/src/testrace/race_test.go:13 +0xcc
  testing.tRunner()
      /Users/rsc/go/src/testing/testing.go:656 +0x104

Goroutine 7 (running) created at:
  _/Users/rsc/go/src/cmd/go/testdata/src/testrace.TestRace()
      /Users/rsc/go/src/cmd/go/testdata/src/testrace/race_test.go:12 +0xbb
  testing.tRunner()
      /Users/rsc/go/src/testing/testing.go:656 +0x104

Goroutine 6 (running) created at:
  testing.(*T).Run()
      /Users/rsc/go/src/testing/testing.go:693 +0x536
  testing.runTests.func1()
      /Users/rsc/go/src/testing/testing.go:877 +0xaa
  testing.tRunner()
      /Users/rsc/go/src/testing/testing.go:656 +0x104
  testing.runTests()
      /Users/rsc/go/src/testing/testing.go:883 +0x4ac
  testing.(*M).Run()
      /Users/rsc/go/src/testing/testing.go:818 +0x1c3
  main.main()
      _/Users/rsc/go/src/cmd/go/testdata/src/testrace/_test/_testmain.go:42 +0x20f
==================
--- FAIL: TestRace (0.00s)
	testing.go:609: race detected during execution of test
FAIL
FAIL	_/Users/rsc/go/src/cmd/go/testdata/src/testrace	0.022s
$

Fixes #15972.

Change-Id: Idb15b8ab81d65637bb535c7e275595ca4a6e450e
Reviewed-on: https://go-review.googlesource.com/32615
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent f3862742
......@@ -2828,6 +2828,26 @@ func TestGoTestRaceInstallCgo(t *testing.T) {
}
}
func TestGoTestRaceFailures(t *testing.T) {
if !canRace {
t.Skip("skipping because race detector not supported")
}
tg := testgo(t)
defer tg.cleanup()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.run("test", "testrace")
tg.runFail("test", "-race", "testrace")
tg.grepStdout("FAIL: TestRace", "TestRace did not fail")
tg.grepBothNot("PASS", "something passed")
tg.runFail("test", "-race", "testrace", "-run", "XXX", "-bench", ".")
tg.grepStdout("FAIL: BenchmarkRace", "BenchmarkRace did not fail")
tg.grepBothNot("PASS", "something passed")
}
func TestGoTestImportErrorStack(t *testing.T) {
const out = `package testdep/p1 (test)
imports testdep/p2
......
package testrace
import "testing"
func TestRace(t *testing.T) {
for i := 0; i < 10; i++ {
c := make(chan int)
x := 1
go func() {
x = 2
c <- 1
}()
x = 3
<-c
}
}
func BenchmarkRace(b *testing.B) {
for i := 0; i < b.N; i++ {
c := make(chan int)
x := 1
go func() {
x = 2
c <- 1
}()
x = 3
<-c
}
}
......@@ -178,7 +178,7 @@ var pkgDeps = map[string][]string{
"runtime/trace": {"L0"},
"text/tabwriter": {"L2"},
"testing": {"L2", "flag", "fmt", "os", "runtime/debug", "runtime/pprof", "runtime/trace", "time"},
"testing": {"L2", "flag", "fmt", "internal/race", "os", "runtime/debug", "runtime/pprof", "runtime/trace", "time"},
"testing/iotest": {"L2", "log"},
"testing/quick": {"L2", "flag", "fmt", "reflect"},
"internal/testenv": {"L2", "OS", "flag", "testing", "syscall"},
......
......@@ -38,3 +38,5 @@ func ReadRange(addr unsafe.Pointer, len int) {
func WriteRange(addr unsafe.Pointer, len int) {
}
func Errors() int { return 0 }
......@@ -48,3 +48,7 @@ func ReadRange(addr unsafe.Pointer, len int) {
func WriteRange(addr unsafe.Pointer, len int) {
runtime.RaceWriteRange(addr, len)
}
func Errors() int {
return runtime.RaceErrors()
}
......@@ -20,6 +20,12 @@ func RaceWriteRange(addr unsafe.Pointer, len int)
func RaceSemacquire(s *uint32)
func RaceSemrelease(s *uint32)
func RaceErrors() int {
var n uint64
racecall(&__tsan_report_count, uintptr(unsafe.Pointer(&n)), 0, 0, 0)
return int(n)
}
// private interface for the runtime
const raceenabled = true
......
......@@ -184,8 +184,8 @@ func TestFail(t *testing.T) {
}
`, `
==================
PASS
Found 1 data race\(s\)
--- FAIL: TestFail \(0.00s\)
.*testing.go:.*: race detected during execution of test
FAIL`},
{"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", `
......
......@@ -173,9 +173,11 @@ func runTests(t *testing.T) ([]byte, error) {
// (that's what is done for C++ ThreadSanitizer tests). This is issue #14119.
cmd.Env = append(cmd.Env,
"GOMAXPROCS=1",
"GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0",
"GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0",
)
return cmd.CombinedOutput()
// There are races: we expect tests to fail and the exit code to be non-zero.
out, _ := cmd.CombinedOutput()
return out, nil
}
func TestIssue8102(t *testing.T) {
......
......@@ -7,6 +7,7 @@ package testing
import (
"flag"
"fmt"
"internal/race"
"os"
"runtime"
"sync"
......@@ -131,6 +132,7 @@ func (b *B) runN(n int) {
// Try to get a comparable environment for each run
// by clearing garbage from previous runs.
runtime.GC()
b.raceErrors = -race.Errors()
b.N = n
b.parallelism = 1
b.ResetTimer()
......@@ -139,6 +141,10 @@ func (b *B) runN(n int) {
b.StopTimer()
b.previousN = n
b.previousDuration = b.duration
b.raceErrors += race.Errors()
if b.raceErrors > 0 {
b.Errorf("race detected during execution of benchmark")
}
}
func min(x, y int) int {
......
......@@ -207,6 +207,7 @@ import (
"errors"
"flag"
"fmt"
"internal/race"
"io"
"os"
"runtime"
......@@ -257,16 +258,17 @@ var (
// common holds the elements common between T and B and
// captures common methods such as Errorf.
type common struct {
mu sync.RWMutex // guards output, failed, and done.
output []byte // Output generated by test or benchmark.
w io.Writer // For flushToParent.
chatty bool // A copy of the chatty flag.
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.
finished bool // Test function has completed.
done bool // Test is finished and all subtests have completed.
hasSub bool
mu sync.RWMutex // guards output, failed, and done.
output []byte // Output generated by test or benchmark.
w io.Writer // For flushToParent.
chatty bool // A copy of the chatty flag.
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.
finished bool // Test function has completed.
done bool // Test is finished and all subtests have completed.
hasSub bool
raceErrors int // number of races detected during test
parent *common
level int // Nesting depth of test or benchmark.
......@@ -580,11 +582,13 @@ func (t *T) Parallel() {
// Add to the list of tests to be released by the parent.
t.parent.sub = append(t.parent.sub, t)
t.raceErrors += race.Errors()
t.signal <- true // Release calling test.
<-t.parent.barrier // Wait for the parent test to complete.
t.context.waitParallel()
t.start = time.Now()
t.raceErrors += -race.Errors()
}
// An internal type but exported because it is cross-package; part of the implementation
......@@ -600,6 +604,11 @@ func tRunner(t *T, fn func(t *T)) {
// a call to runtime.Goexit, record the duration and send
// a signal saying that the test is done.
defer func() {
t.raceErrors += race.Errors()
if t.raceErrors > 0 {
t.Errorf("race detected during execution of test")
}
t.duration += time.Now().Sub(t.start)
// If the test panicked, print any test output before dying.
err := recover()
......@@ -643,6 +652,7 @@ func tRunner(t *T, fn func(t *T)) {
}()
t.start = time.Now()
t.raceErrors = -race.Errors()
fn(t)
t.finished = true
}
......@@ -810,7 +820,7 @@ func (m *M) Run() int {
if !testRan && !exampleRan && *matchBenchmarks == "" {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
}
if !testOk || !exampleOk || !runBenchmarks(m.deps.MatchString, m.benchmarks) {
if !testOk || !exampleOk || !runBenchmarks(m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
fmt.Println("FAIL")
m.after()
return 1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment