|  | // 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`}, | 
|  | } |