github.com/aloncn/graphics-go@v0.0.1/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  	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  	// We set GOMAXPROCS=1 to prevent test flakiness.
   159  	// There are two sources of flakiness:
   160  	// 1. Some tests rely on particular execution order.
   161  	//    If the order is different, race does not happen at all.
   162  	// 2. Ironically, ThreadSanitizer runtime contains a logical race condition
   163  	//    that can lead to false negatives if racy accesses happen literally at the same time.
   164  	// Tests used to work reliably in the good old days of GOMAXPROCS=1.
   165  	// So let's set it for now. A more reliable solution is to explicitly annotate tests
   166  	// with required execution order by means of a special "invisible" synchronization primitive
   167  	// (that's what is done for C++ ThreadSanitizer tests). This is issue #14119.
   168  	cmd.Env = append(cmd.Env,
   169  		"GOMAXPROCS=1",
   170  		"GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0",
   171  	)
   172  	return cmd.CombinedOutput()
   173  }
   174  
   175  func TestIssue8102(t *testing.T) {
   176  	// If this compiles with -race, the test passes.
   177  	type S struct {
   178  		x interface{}
   179  		i int
   180  	}
   181  	c := make(chan int)
   182  	a := [2]*int{}
   183  	for ; ; c <- *a[S{}.i] {
   184  		if t != nil {
   185  			break
   186  		}
   187  	}
   188  }
   189  
   190  func TestIssue9137(t *testing.T) {
   191  	a := []string{"a"}
   192  	i := 0
   193  	a[i], a[len(a)-1], a = a[len(a)-1], "", a[:len(a)-1]
   194  	if len(a) != 0 || a[:1][0] != "" {
   195  		t.Errorf("mangled a: %q %q", a, a[:1])
   196  	}
   197  }