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