| // 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 ( |
| "io/ioutil" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "regexp" |
| "strings" |
| "testing" |
| ) |
| |
| func TestOutput(t *testing.T) { |
| for _, test := range tests { |
| 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("go", test.run, "-race", "-gcflags=-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, "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 |
| 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 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 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 |
| go func() { |
| x = 42 |
| done <- true |
| }() |
| x = 43 |
| <-done |
| } |
| `, ` |
| ================== |
| PASS |
| Found 1 data race\(s\) |
| FAIL`}, |
| } |