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