mirror of
https://github.com/golang/go
synced 2024-11-23 20:10:08 -07:00
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: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
f3862742b6
commit
43f954e098
@ -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) {
|
func TestGoTestImportErrorStack(t *testing.T) {
|
||||||
const out = `package testdep/p1 (test)
|
const out = `package testdep/p1 (test)
|
||||||
imports testdep/p2
|
imports testdep/p2
|
||||||
|
29
src/cmd/go/testdata/src/testrace/race_test.go
vendored
Normal file
29
src/cmd/go/testdata/src/testrace/race_test.go
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
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"},
|
"runtime/trace": {"L0"},
|
||||||
"text/tabwriter": {"L2"},
|
"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/iotest": {"L2", "log"},
|
||||||
"testing/quick": {"L2", "flag", "fmt", "reflect"},
|
"testing/quick": {"L2", "flag", "fmt", "reflect"},
|
||||||
"internal/testenv": {"L2", "OS", "flag", "testing", "syscall"},
|
"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 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) {
|
func WriteRange(addr unsafe.Pointer, len int) {
|
||||||
runtime.RaceWriteRange(addr, len)
|
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 RaceSemacquire(s *uint32)
|
||||||
func RaceSemrelease(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
|
// private interface for the runtime
|
||||||
const raceenabled = true
|
const raceenabled = true
|
||||||
|
|
||||||
|
@ -184,8 +184,8 @@ func TestFail(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`, `
|
`, `
|
||||||
==================
|
==================
|
||||||
PASS
|
--- FAIL: TestFail \(0.00s\)
|
||||||
Found 1 data race\(s\)
|
.*testing.go:.*: race detected during execution of test
|
||||||
FAIL`},
|
FAIL`},
|
||||||
|
|
||||||
{"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", `
|
{"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.
|
// (that's what is done for C++ ThreadSanitizer tests). This is issue #14119.
|
||||||
cmd.Env = append(cmd.Env,
|
cmd.Env = append(cmd.Env,
|
||||||
"GOMAXPROCS=1",
|
"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) {
|
func TestIssue8102(t *testing.T) {
|
||||||
|
@ -7,6 +7,7 @@ package testing
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/race"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
@ -131,6 +132,7 @@ func (b *B) runN(n int) {
|
|||||||
// Try to get a comparable environment for each run
|
// Try to get a comparable environment for each run
|
||||||
// by clearing garbage from previous runs.
|
// by clearing garbage from previous runs.
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
|
b.raceErrors = -race.Errors()
|
||||||
b.N = n
|
b.N = n
|
||||||
b.parallelism = 1
|
b.parallelism = 1
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
@ -139,6 +141,10 @@ func (b *B) runN(n int) {
|
|||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
b.previousN = n
|
b.previousN = n
|
||||||
b.previousDuration = b.duration
|
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 {
|
func min(x, y int) int {
|
||||||
|
@ -207,6 +207,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"internal/race"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -257,16 +258,17 @@ var (
|
|||||||
// common holds the elements common between T and B and
|
// common holds the elements common between T and B and
|
||||||
// captures common methods such as Errorf.
|
// captures common methods such as Errorf.
|
||||||
type common struct {
|
type common struct {
|
||||||
mu sync.RWMutex // guards output, failed, and done.
|
mu sync.RWMutex // guards output, failed, and done.
|
||||||
output []byte // Output generated by test or benchmark.
|
output []byte // Output generated by test or benchmark.
|
||||||
w io.Writer // For flushToParent.
|
w io.Writer // For flushToParent.
|
||||||
chatty bool // A copy of the chatty flag.
|
chatty bool // A copy of the chatty flag.
|
||||||
ran bool // Test or benchmark (or one of its subtests) was executed.
|
ran bool // Test or benchmark (or one of its subtests) was executed.
|
||||||
failed bool // Test or benchmark has failed.
|
failed bool // Test or benchmark has failed.
|
||||||
skipped bool // Test of benchmark has been skipped.
|
skipped bool // Test of benchmark has been skipped.
|
||||||
finished bool // Test function has completed.
|
finished bool // Test function has completed.
|
||||||
done bool // Test is finished and all subtests have completed.
|
done bool // Test is finished and all subtests have completed.
|
||||||
hasSub bool
|
hasSub bool
|
||||||
|
raceErrors int // number of races detected during test
|
||||||
|
|
||||||
parent *common
|
parent *common
|
||||||
level int // Nesting depth of test or benchmark.
|
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.
|
// Add to the list of tests to be released by the parent.
|
||||||
t.parent.sub = append(t.parent.sub, t)
|
t.parent.sub = append(t.parent.sub, t)
|
||||||
|
t.raceErrors += race.Errors()
|
||||||
|
|
||||||
t.signal <- true // Release calling test.
|
t.signal <- true // Release calling test.
|
||||||
<-t.parent.barrier // Wait for the parent test to complete.
|
<-t.parent.barrier // Wait for the parent test to complete.
|
||||||
t.context.waitParallel()
|
t.context.waitParallel()
|
||||||
t.start = time.Now()
|
t.start = time.Now()
|
||||||
|
t.raceErrors += -race.Errors()
|
||||||
}
|
}
|
||||||
|
|
||||||
// An internal type but exported because it is cross-package; part of the implementation
|
// 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 call to runtime.Goexit, record the duration and send
|
||||||
// a signal saying that the test is done.
|
// a signal saying that the test is done.
|
||||||
defer func() {
|
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)
|
t.duration += time.Now().Sub(t.start)
|
||||||
// If the test panicked, print any test output before dying.
|
// If the test panicked, print any test output before dying.
|
||||||
err := recover()
|
err := recover()
|
||||||
@ -643,6 +652,7 @@ func tRunner(t *T, fn func(t *T)) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
t.start = time.Now()
|
t.start = time.Now()
|
||||||
|
t.raceErrors = -race.Errors()
|
||||||
fn(t)
|
fn(t)
|
||||||
t.finished = true
|
t.finished = true
|
||||||
}
|
}
|
||||||
@ -810,7 +820,7 @@ func (m *M) Run() int {
|
|||||||
if !testRan && !exampleRan && *matchBenchmarks == "" {
|
if !testRan && !exampleRan && *matchBenchmarks == "" {
|
||||||
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
|
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")
|
fmt.Println("FAIL")
|
||||||
m.after()
|
m.after()
|
||||||
return 1
|
return 1
|
||||||
|
Loading…
Reference in New Issue
Block a user