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  }