// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build race package race_test import ( "internal/testenv" "io/ioutil" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "testing" ) func TestOutput(t *testing.T) { pkgdir, err := ioutil.TempDir("", "go-build-race-output") if err != nil { t.Fatal(err) } defer os.RemoveAll(pkgdir) out, err := exec.Command(testenv.GoToolPath(t), "install", "-race", "-pkgdir="+pkgdir, "-gcflags=all=-l", "testing").CombinedOutput() if err != nil { t.Fatalf("go install -race: %v\n%s", err, out) } for _, test := range tests { if test.goos != "" && test.goos != runtime.GOOS { t.Logf("test %v runs only on %v, skipping: ", test.name, test.goos) continue } dir, err := ioutil.TempDir("", "go-build") if err != nil { t.Fatalf("failed to create temp directory: %v", err) } defer os.RemoveAll(dir) source := "main.go" if test.run == "test" { source = "main_test.go" } src := filepath.Join(dir, source) f, err := os.Create(src) if err != nil { t.Fatalf("failed to create file: %v", err) } _, err = f.WriteString(test.source) if err != nil { f.Close() t.Fatalf("failed to write: %v", err) } if err := f.Close(); err != nil { t.Fatalf("failed to close file: %v", err) } // Pass -l to the compiler to test stack traces. cmd := exec.Command(testenv.GoToolPath(t), test.run, "-race", "-pkgdir="+pkgdir, "-gcflags=all=-l", src) // GODEBUG spoils program output, GOMAXPROCS makes it flaky. for _, env := range os.Environ() { if strings.HasPrefix(env, "GODEBUG=") || strings.HasPrefix(env, "GOMAXPROCS=") || strings.HasPrefix(env, "GORACE=") { continue } cmd.Env = append(cmd.Env, env) } cmd.Env = append(cmd.Env, "GOMAXPROCS=1", // see comment in race_test.go "GORACE="+test.gorace, ) got, _ := cmd.CombinedOutput() if !regexp.MustCompile(test.re).MatchString(string(got)) { t.Fatalf("failed test case %v, expect:\n%v\ngot:\n%s", test.name, test.re, got) } } } var tests = []struct { name string run string goos string gorace string source string re string }{ {"simple", "run", "", "atexit_sleep_ms=0", ` package main import "time" func main() { done := make(chan bool) x := 0 startRacer(&x, done) store(&x, 43) <-done } func store(x *int, v int) { *x = v } func startRacer(x *int, done chan bool) { go racer(x, done) } func racer(x *int, done chan bool) { time.Sleep(10*time.Millisecond) store(x, 42) done <- true } `, `================== WARNING: DATA RACE Write at 0x[0-9,a-f]+ by goroutine [0-9]: main\.store\(\) .+/main\.go:12 \+0x[0-9,a-f]+ main\.racer\(\) .+/main\.go:19 \+0x[0-9,a-f]+ Previous write at 0x[0-9,a-f]+ by main goroutine: main\.store\(\) .+/main\.go:12 \+0x[0-9,a-f]+ main\.main\(\) .+/main\.go:8 \+0x[0-9,a-f]+ Goroutine [0-9] \(running\) created at: main\.startRacer\(\) .+/main\.go:15 \+0x[0-9,a-f]+ main\.main\(\) .+/main\.go:7 \+0x[0-9,a-f]+ ================== Found 1 data race\(s\) exit status 66 `}, {"exitcode", "run", "", "atexit_sleep_ms=0 exitcode=13", ` package main func main() { done := make(chan bool) x := 0 go func() { x = 42 done <- true }() x = 43 <-done } `, `exit status 13`}, {"strip_path_prefix", "run", "", "atexit_sleep_ms=0 strip_path_prefix=/main.", ` package main func main() { done := make(chan bool) x := 0 go func() { x = 42 done <- true }() x = 43 <-done } `, ` go:7 \+0x[0-9,a-f]+ `}, {"halt_on_error", "run", "", "atexit_sleep_ms=0 halt_on_error=1", ` package main func main() { done := make(chan bool) x := 0 go func() { x = 42 done <- true }() x = 43 <-done } `, ` ================== exit status 66 `}, {"test_fails_on_race", "test", "", "atexit_sleep_ms=0", ` package main_test import "testing" func TestFail(t *testing.T) { done := make(chan bool) x := 0 _ = x go func() { x = 42 done <- true }() x = 43 <-done t.Log(t.Failed()) } `, ` ================== --- FAIL: TestFail \(0...s\) .*main_test.go:14: true .*testing.go:.*: race detected during execution of test FAIL`}, {"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", ` package main func main() { done := make(chan string) data := make([]byte, 10) go func() { done <- string(data) }() data[0] = 1 <-done } `, ` runtime\.slicebytetostring\(\) .*/runtime/string\.go:.* main\.main\.func1\(\) .*/main.go:7`}, // Test for https://golang.org/issue/17190 {"external_cgo_thread", "run", "linux", "atexit_sleep_ms=0", ` package main /* #include <pthread.h> typedef struct cb { int foo; } cb; extern void goCallback(); static inline void *threadFunc(void *p) { goCallback(); return 0; } static inline void startThread(cb* c) { pthread_t th; pthread_create(&th, 0, threadFunc, 0); } */ import "C" import "time" var racy int //export goCallback func goCallback() { racy++ } func main() { var c C.cb C.startThread(&c) time.Sleep(time.Second) racy++ } `, `================== WARNING: DATA RACE Read at 0x[0-9,a-f]+ by main goroutine: main\.main\(\) .*/main\.go:34 \+0x[0-9,a-f]+ Previous write at 0x[0-9,a-f]+ by goroutine [0-9]: main\.goCallback\(\) .*/main\.go:27 \+0x[0-9,a-f]+ main._cgoexpwrap_[0-9a-z]+_goCallback\(\) .*_cgo_gotypes\.go:[0-9]+ \+0x[0-9,a-f]+ Goroutine [0-9] \(running\) created at: runtime\.newextram\(\) .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+ ==================`}, {"second_test_passes", "test", "", "atexit_sleep_ms=0", ` package main_test import "testing" func TestFail(t *testing.T) { done := make(chan bool) x := 0 _ = x go func() { x = 42 done <- true }() x = 43 <-done } func TestPass(t *testing.T) { } `, ` ================== --- FAIL: TestFail \(0...s\) .*testing.go:.*: race detected during execution of test FAIL`}, }