blob: 7e0ee866a630b3178a7f78b5bb4d201ff3cab57e [file] [log] [blame]
Colin Cross7bb052a2015-02-03 12:59:37 -08001// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build race
6
7// This program is used to verify the race detector
8// by running the tests and parsing their output.
9// It does not check stack correctness, completeness or anything else:
10// it merely verifies that if a test is expected to be racy
11// then the race is detected.
12package race_test
13
14import (
15 "bufio"
16 "bytes"
17 "fmt"
18 "io"
19 "log"
20 "os"
21 "os/exec"
22 "path/filepath"
23 "strings"
24 "testing"
25)
26
27var (
28 passedTests = 0
29 totalTests = 0
30 falsePos = 0
31 falseNeg = 0
32 failingPos = 0
33 failingNeg = 0
34 failed = false
35)
36
37const (
38 visibleLen = 40
39 testPrefix = "=== RUN Test"
40)
41
42func TestRace(t *testing.T) {
43 testOutput, err := runTests()
44 if err != nil {
45 t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
46 }
47 reader := bufio.NewReader(bytes.NewReader(testOutput))
48
49 funcName := ""
50 var tsanLog []string
51 for {
52 s, err := nextLine(reader)
53 if err != nil {
54 fmt.Printf("%s\n", processLog(funcName, tsanLog))
55 break
56 }
57 if strings.HasPrefix(s, testPrefix) {
58 fmt.Printf("%s\n", processLog(funcName, tsanLog))
59 tsanLog = make([]string, 0, 100)
60 funcName = s[len(testPrefix):]
61 } else {
62 tsanLog = append(tsanLog, s)
63 }
64 }
65
66 fmt.Printf("\nPassed %d of %d tests (%.02f%%, %d+, %d-)\n",
67 passedTests, totalTests, 100*float64(passedTests)/float64(totalTests), falsePos, falseNeg)
68 fmt.Printf("%d expected failures (%d has not fail)\n", failingPos+failingNeg, failingNeg)
69 if failed {
70 t.Fail()
71 }
72}
73
74// nextLine is a wrapper around bufio.Reader.ReadString.
75// It reads a line up to the next '\n' character. Error
76// is non-nil if there are no lines left, and nil
77// otherwise.
78func nextLine(r *bufio.Reader) (string, error) {
79 s, err := r.ReadString('\n')
80 if err != nil {
81 if err != io.EOF {
82 log.Fatalf("nextLine: expected EOF, received %v", err)
83 }
84 return s, err
85 }
86 return s[:len(s)-1], nil
87}
88
89// processLog verifies whether the given ThreadSanitizer's log
90// contains a race report, checks this information against
91// the name of the testcase and returns the result of this
92// comparison.
93func processLog(testName string, tsanLog []string) string {
94 if !strings.HasPrefix(testName, "Race") && !strings.HasPrefix(testName, "NoRace") {
95 return ""
96 }
97 gotRace := false
98 for _, s := range tsanLog {
99 if strings.Contains(s, "DATA RACE") {
100 gotRace = true
101 break
102 }
103 }
104
105 failing := strings.Contains(testName, "Failing")
106 expRace := !strings.HasPrefix(testName, "No")
107 for len(testName) < visibleLen {
108 testName += " "
109 }
110 if expRace == gotRace {
111 passedTests++
112 totalTests++
113 if failing {
114 failed = true
115 failingNeg++
116 }
117 return fmt.Sprintf("%s .", testName)
118 }
119 pos := ""
120 if expRace {
121 falseNeg++
122 } else {
123 falsePos++
124 pos = "+"
125 }
126 if failing {
127 failingPos++
128 } else {
129 failed = true
130 }
131 totalTests++
132 return fmt.Sprintf("%s %s%s", testName, "FAILED", pos)
133}
134
135// runTests assures that the package and its dependencies is
136// built with instrumentation enabled and returns the output of 'go test'
137// which includes possible data race reports from ThreadSanitizer.
138func runTests() ([]byte, error) {
139 tests, err := filepath.Glob("./testdata/*_test.go")
140 if err != nil {
141 return nil, err
142 }
143 args := []string{"test", "-race", "-v"}
144 args = append(args, tests...)
145 cmd := exec.Command("go", args...)
146 // The following flags turn off heuristics that suppress seemingly identical reports.
147 // It is required because the tests contain a lot of data races on the same addresses
148 // (the tests are simple and the memory is constantly reused).
149 for _, env := range os.Environ() {
150 if strings.HasPrefix(env, "GOMAXPROCS=") || strings.HasPrefix(env, "GODEBUG=") {
151 continue
152 }
153 cmd.Env = append(cmd.Env, env)
154 }
155 cmd.Env = append(cmd.Env, `GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0"`)
156 return cmd.CombinedOutput()
157}
158
159func TestIssue8102(t *testing.T) {
160 // If this compiles with -race, the test passes.
161 type S struct {
162 x interface{}
163 i int
164 }
165 c := make(chan int)
166 a := [2]*int{}
167 for ; ; c <- *a[S{}.i] {
168 if t != nil {
169 break
170 }
171 }
172}