github.com/0xKiwi/rules_go@v0.24.3/tests/core/race/race_test.go (about)

     1  // Copyright 2019 The Bazel Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package race_test
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"os/exec"
    22  	"runtime"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/bazelbuild/rules_go/go/tools/bazel_testing"
    27  )
    28  
    29  func TestMain(m *testing.M) {
    30  	bazel_testing.TestMain(m, bazel_testing.Args{
    31  		Main: `
    32  -- BUILD.bazel --
    33  load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
    34  
    35  go_library(
    36      name = "racy",
    37      srcs = [
    38          "race_off.go",
    39          "race_on.go",
    40          "racy.go",
    41          "empty.s", # verify #2143
    42      ],
    43      importpath = "example.com/racy",
    44  )
    45  
    46  go_binary(
    47      name = "racy_cmd",
    48      srcs = ["main.go"],
    49      embed = [":racy"],
    50  )
    51  
    52  go_binary(
    53      name = "racy_cmd_race_mode",
    54      srcs = ["main.go"],
    55      embed = [":racy"],
    56      race = "on",
    57  )
    58  
    59  go_test(
    60      name = "racy_test",
    61      srcs = ["racy_test.go"],
    62      embed = [":racy"],
    63  )
    64  
    65  go_test(
    66      name = "racy_test_race_mode",
    67      srcs = ["racy_test.go"],
    68      embed = [":racy"],
    69      race = "on",
    70  )
    71  
    72  go_binary(
    73      name = "pure_bin",
    74      srcs = ["pure_bin.go"],
    75      pure = "on",
    76  )
    77  
    78  go_binary(
    79      name = "pure_race_bin",
    80      srcs = ["pure_bin.go"],
    81      pure = "on",
    82      race = "on",
    83  )
    84  
    85  go_library(
    86  		name = "coverrace",
    87  		srcs = ["coverrace.go"],
    88  		importpath = "example.com/coverrace",
    89  )
    90  
    91  go_test(
    92  		name = "coverrace_test",
    93  		srcs = ["coverrace_test.go"],
    94  		embed = [":coverrace"],
    95      race = "on",
    96  )
    97  -- race_off.go --
    98  // +build !race
    99  
   100  package main
   101  
   102  const RaceEnabled = false
   103  
   104  -- race_on.go --
   105  // +build race
   106  
   107  package main
   108  
   109  const RaceEnabled = true
   110  
   111  -- racy.go --
   112  package main
   113  
   114  import (
   115  	"flag"
   116  	"fmt"
   117  	"os"
   118  )
   119  
   120  var wantRace = flag.Bool("wantrace", false, "")
   121  
   122  func Race() {
   123  	if *wantRace != RaceEnabled {
   124  		fmt.Fprintf(os.Stderr, "!!! -wantrace is %v, but RaceEnabled is %v\n", *wantRace, RaceEnabled)
   125  		os.Exit(1)
   126  	}
   127  
   128  	done := make(chan bool)
   129  	m := make(map[string]string)
   130  	m["name"] = "world"
   131  	go func() {
   132  		m["name"] = "data race"
   133  		done <- true
   134  	}()
   135  	fmt.Println("Hello,", m["name"])
   136  	<-done
   137  }
   138  
   139  -- main.go --
   140  package main
   141  
   142  import "flag"
   143  
   144  func main() {
   145  	flag.Parse()
   146  	Race()
   147  }
   148  
   149  -- racy_test.go --
   150  package main
   151  
   152  import "testing"
   153  
   154  func TestRace(t *testing.T) {
   155  	Race()
   156  }
   157  
   158  -- empty.s --
   159  -- pure_bin.go --
   160  // +build !race
   161  
   162  // pure_bin will not build in race mode, since its sources will be excluded.
   163  package main
   164  
   165  func main() {}
   166  
   167  -- coverrace.go --
   168  package coverrace
   169  // copied from https://hermanschaaf.com/running-the-go-race-detector-with-cover/
   170  func add100() int {
   171  	total := 0
   172  	c := make(chan int, 1)
   173  	for i := 0; i < 100; i++ {
   174  		go func(chan int) {
   175  			c <- 1
   176  		}(c)
   177  	}
   178  	for u := 0; u < 100; u++ {
   179  		total += <-c
   180  	}
   181  	return total
   182  }
   183  
   184  -- coverrace_test.go --
   185  package coverrace
   186  // copied from https://hermanschaaf.com/running-the-go-race-detector-with-cover/
   187  
   188  import "testing"
   189  
   190  func TestCoverRace(t *testing.T) {
   191  	got := add100()
   192  	if got != 100 {
   193  		t.Errorf("got %d, want %d", got, 100)
   194  	}
   195  }
   196  `,
   197  	})
   198  }
   199  
   200  func Test(t *testing.T) {
   201  	for _, test := range []struct {
   202  		desc, cmd, target                    string
   203  		featureFlag, wantRace, wantBuildFail bool
   204  	}{
   205  		{
   206  			desc:   "cmd_auto",
   207  			cmd:    "run",
   208  			target: "//:racy_cmd",
   209  		}, {
   210  			desc:     "cmd_attr",
   211  			cmd:      "run",
   212  			target:   "//:racy_cmd_race_mode",
   213  			wantRace: true,
   214  		}, {
   215  			desc:        "cmd_feature",
   216  			cmd:         "run",
   217  			target:      "//:racy_cmd",
   218  			featureFlag: true,
   219  			wantRace:    true,
   220  		}, {
   221  			desc:   "test_auto",
   222  			cmd:    "test",
   223  			target: "//:racy_test",
   224  		}, {
   225  			desc:     "test_attr",
   226  			cmd:      "test",
   227  			target:   "//:racy_test_race_mode",
   228  			wantRace: true,
   229  		}, {
   230  			desc:        "test_feature",
   231  			cmd:         "test",
   232  			target:      "//:racy_test",
   233  			featureFlag: true,
   234  			wantRace:    true,
   235  		}, {
   236  			desc:        "pure_bin",
   237  			cmd:         "build",
   238  			target:      "//:pure_bin",
   239  			featureFlag: true,
   240  		}, {
   241  			desc:          "pure_race_bin",
   242  			cmd:           "build",
   243  			target:        "//:pure_race_bin",
   244  			wantBuildFail: true,
   245  		}, {
   246  			desc:   "cover_race",
   247  			cmd:    "coverage",
   248  			target: "//:coverrace_test",
   249  		},
   250  	} {
   251  		t.Run(test.desc, func(t *testing.T) {
   252  			// TODO(#2518): fix coverage tests on Windows
   253  			if test.cmd == "coverage" && runtime.GOOS == "windows" {
   254  				t.Skip("TODO(#2518): fix and enable coverage tests on Windows")
   255  			}
   256  			args := []string{test.cmd}
   257  			if test.featureFlag {
   258  				args = append(args, "--features=race")
   259  			}
   260  			args = append(args, test.target)
   261  			if test.cmd == "test" {
   262  				args = append(args, fmt.Sprintf("--test_arg=-wantrace=%v", test.wantRace))
   263  			} else if test.cmd == "run" {
   264  				args = append(args, "--", fmt.Sprintf("-wantrace=%v", test.wantRace))
   265  			}
   266  			cmd := bazel_testing.BazelCmd(args...)
   267  			stderr := &bytes.Buffer{}
   268  			cmd.Stderr = stderr
   269  			t.Logf("running: bazel %s", strings.Join(args, " "))
   270  			if err := cmd.Run(); err != nil {
   271  				var xerr *exec.ExitError
   272  				if !errors.As(err, &xerr) {
   273  					t.Fatalf("unexpected error: %v", err)
   274  				}
   275  				if xerr.ExitCode() == bazel_testing.BUILD_FAILURE {
   276  					if !test.wantBuildFail {
   277  						t.Fatalf("unexpected build failure: %v\nstderr:\n%s", err, stderr.Bytes())
   278  					}
   279  					return
   280  				} else if xerr.ExitCode() == bazel_testing.TESTS_FAILED {
   281  					if bytes.Contains(stderr.Bytes(), []byte("!!!")) {
   282  						t.Fatalf("error running %s:\n%s", strings.Join(cmd.Args, " "), stderr.Bytes())
   283  					} else if !test.wantRace {
   284  						t.Fatalf("error running %s without race enabled\n%s", strings.Join(cmd.Args, " "), stderr.Bytes())
   285  					}
   286  				} else if test.wantRace {
   287  					if !bytes.Contains(stderr.Bytes(), []byte("WARNING: DATA RACE")) {
   288  						t.Fatalf("wanted data race; command failed with: %v\nstderr:\n%s", err, stderr.Bytes())
   289  					}
   290  					return
   291  				} else {
   292  					t.Fatalf("unexpected error: %v\nstderr:\n%s", err, stderr.Bytes())
   293  				}
   294  			} else if test.wantRace {
   295  				t.Fatalf("command %s with race enabled did not fail", strings.Join(cmd.Args, " "))
   296  			} else if test.wantBuildFail {
   297  				t.Fatalf("target %s did not fail to build", test.target)
   298  			}
   299  		})
   300  	}
   301  }