Home | History | Annotate | Download | only in race
      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