1 // 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. 12 package race_test 13 14 import ( 15 "bufio" 16 "bytes" 17 "fmt" 18 "io" 19 "log" 20 "os" 21 "os/exec" 22 "path/filepath" 23 "strings" 24 "testing" 25 ) 26 27 var ( 28 passedTests = 0 29 totalTests = 0 30 falsePos = 0 31 falseNeg = 0 32 failingPos = 0 33 failingNeg = 0 34 failed = false 35 ) 36 37 const ( 38 visibleLen = 40 39 testPrefix = "=== RUN Test" 40 ) 41 42 func 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 if totalTests == 0 { 67 t.Fatalf("failed to parse test output") 68 } 69 fmt.Printf("\nPassed %d of %d tests (%.02f%%, %d+, %d-)\n", 70 passedTests, totalTests, 100*float64(passedTests)/float64(totalTests), falsePos, falseNeg) 71 fmt.Printf("%d expected failures (%d has not fail)\n", failingPos+failingNeg, failingNeg) 72 if failed { 73 t.Fail() 74 } 75 } 76 77 // nextLine is a wrapper around bufio.Reader.ReadString. 78 // It reads a line up to the next '\n' character. Error 79 // is non-nil if there are no lines left, and nil 80 // otherwise. 81 func nextLine(r *bufio.Reader) (string, error) { 82 s, err := r.ReadString('\n') 83 if err != nil { 84 if err != io.EOF { 85 log.Fatalf("nextLine: expected EOF, received %v", err) 86 } 87 return s, err 88 } 89 return s[:len(s)-1], nil 90 } 91 92 // processLog verifies whether the given ThreadSanitizer's log 93 // contains a race report, checks this information against 94 // the name of the testcase and returns the result of this 95 // comparison. 96 func processLog(testName string, tsanLog []string) string { 97 if !strings.HasPrefix(testName, "Race") && !strings.HasPrefix(testName, "NoRace") { 98 return "" 99 } 100 gotRace := false 101 for _, s := range tsanLog { 102 if strings.Contains(s, "DATA RACE") { 103 gotRace = true 104 break 105 } 106 } 107 108 failing := strings.Contains(testName, "Failing") 109 expRace := !strings.HasPrefix(testName, "No") 110 for len(testName) < visibleLen { 111 testName += " " 112 } 113 if expRace == gotRace { 114 passedTests++ 115 totalTests++ 116 if failing { 117 failed = true 118 failingNeg++ 119 } 120 return fmt.Sprintf("%s .", testName) 121 } 122 pos := "" 123 if expRace { 124 falseNeg++ 125 } else { 126 falsePos++ 127 pos = "+" 128 } 129 if failing { 130 failingPos++ 131 } else { 132 failed = true 133 } 134 totalTests++ 135 return fmt.Sprintf("%s %s%s", testName, "FAILED", pos) 136 } 137 138 // runTests assures that the package and its dependencies is 139 // built with instrumentation enabled and returns the output of 'go test' 140 // which includes possible data race reports from ThreadSanitizer. 141 func runTests() ([]byte, error) { 142 tests, err := filepath.Glob("./testdata/*_test.go") 143 if err != nil { 144 return nil, err 145 } 146 args := []string{"test", "-race", "-v"} 147 args = append(args, tests...) 148 cmd := exec.Command("go", args...) 149 // The following flags turn off heuristics that suppress seemingly identical reports. 150 // It is required because the tests contain a lot of data races on the same addresses 151 // (the tests are simple and the memory is constantly reused). 152 for _, env := range os.Environ() { 153 if strings.HasPrefix(env, "GOMAXPROCS=") || strings.HasPrefix(env, "GODEBUG=") { 154 continue 155 } 156 cmd.Env = append(cmd.Env, env) 157 } 158 cmd.Env = append(cmd.Env, `GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0`) 159 return cmd.CombinedOutput() 160 } 161 162 func TestIssue8102(t *testing.T) { 163 // If this compiles with -race, the test passes. 164 type S struct { 165 x interface{} 166 i int 167 } 168 c := make(chan int) 169 a := [2]*int{} 170 for ; ; c <- *a[S{}.i] { 171 if t != nil { 172 break 173 } 174 } 175 } 176 177 func TestIssue9137(t *testing.T) { 178 a := []string{"a"} 179 i := 0 180 a[i], a[len(a)-1], a = a[len(a)-1], "", a[:len(a)-1] 181 if len(a) != 0 || a[:1][0] != "" { 182 t.Errorf("mangled a: %q %q", a, a[:1]) 183 } 184 } 185