github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/runtime/race/race_test.go (about) 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 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. 78 func 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. 93 func 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. 138 func 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 159 func 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 }