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 15package race_test 16 17import ( 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 29func TestMain(m *testing.M) { 30 bazel_testing.TestMain(m, bazel_testing.Args{ 31 Main: ` 32-- BUILD.bazel -- 33load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") 34 35go_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 46go_binary( 47 name = "racy_cmd", 48 srcs = ["main.go"], 49 embed = [":racy"], 50) 51 52go_binary( 53 name = "racy_cmd_race_mode", 54 srcs = ["main.go"], 55 embed = [":racy"], 56 race = "on", 57) 58 59go_test( 60 name = "racy_test", 61 srcs = ["racy_test.go"], 62 embed = [":racy"], 63) 64 65go_test( 66 name = "racy_test_race_mode", 67 srcs = ["racy_test.go"], 68 embed = [":racy"], 69 race = "on", 70) 71 72go_binary( 73 name = "pure_bin", 74 srcs = ["pure_bin.go"], 75 pure = "on", 76) 77 78go_binary( 79 name = "pure_race_bin", 80 srcs = ["pure_bin.go"], 81 pure = "on", 82 race = "on", 83) 84 85go_library( 86 name = "coverrace", 87 srcs = ["coverrace.go"], 88 importpath = "example.com/coverrace", 89) 90 91go_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 100package main 101 102const RaceEnabled = false 103 104-- race_on.go -- 105// +build race 106 107package main 108 109const RaceEnabled = true 110 111-- racy.go -- 112package main 113 114import ( 115 "flag" 116 "fmt" 117 "os" 118) 119 120var wantRace = flag.Bool("wantrace", false, "") 121 122func 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 -- 140package main 141 142import "flag" 143 144func main() { 145 flag.Parse() 146 Race() 147} 148 149-- racy_test.go -- 150package main 151 152import "testing" 153 154func 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. 163package main 164 165func main() {} 166 167-- coverrace.go -- 168package coverrace 169// copied from https://hermanschaaf.com/running-the-go-race-detector-with-cover/ 170func 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 -- 185package coverrace 186// copied from https://hermanschaaf.com/running-the-go-race-detector-with-cover/ 187 188import "testing" 189 190func 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 200func 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 featureFlag: true, 250 }, 251 } { 252 t.Run(test.desc, func(t *testing.T) { 253 // TODO(#2518): fix coverage tests on Windows 254 if test.cmd == "coverage" && runtime.GOOS == "windows" { 255 t.Skip("TODO(#2518): fix and enable coverage tests on Windows") 256 } 257 args := []string{test.cmd} 258 if test.featureFlag { 259 args = append(args, "--@io_bazel_rules_go//go/config:race") 260 } 261 args = append(args, test.target) 262 if test.cmd == "test" { 263 args = append(args, fmt.Sprintf("--test_arg=-wantrace=%v", test.wantRace)) 264 } else if test.cmd == "run" { 265 args = append(args, "--", fmt.Sprintf("-wantrace=%v", test.wantRace)) 266 } 267 cmd := bazel_testing.BazelCmd(args...) 268 stderr := &bytes.Buffer{} 269 cmd.Stderr = stderr 270 t.Logf("running: bazel %s", strings.Join(args, " ")) 271 if err := cmd.Run(); err != nil { 272 var xerr *exec.ExitError 273 if !errors.As(err, &xerr) { 274 t.Fatalf("unexpected error: %v", err) 275 } 276 if xerr.ExitCode() == bazel_testing.BUILD_FAILURE { 277 if !test.wantBuildFail { 278 t.Fatalf("unexpected build failure: %v\nstderr:\n%s", err, stderr.Bytes()) 279 } 280 return 281 } else if xerr.ExitCode() == bazel_testing.TESTS_FAILED { 282 if bytes.Contains(stderr.Bytes(), []byte("!!!")) { 283 t.Fatalf("error running %s:\n%s", strings.Join(cmd.Args, " "), stderr.Bytes()) 284 } else if !test.wantRace { 285 t.Fatalf("error running %s without race enabled\n%s", strings.Join(cmd.Args, " "), stderr.Bytes()) 286 } 287 } else if test.wantRace { 288 if !bytes.Contains(stderr.Bytes(), []byte("WARNING: DATA RACE")) { 289 t.Fatalf("wanted data race; command failed with: %v\nstderr:\n%s", err, stderr.Bytes()) 290 } 291 return 292 } else { 293 t.Fatalf("unexpected error: %v\nstderr:\n%s", err, stderr.Bytes()) 294 } 295 } else if test.wantRace { 296 t.Fatalf("command %s with race enabled did not fail", strings.Join(cmd.Args, " ")) 297 } else if test.wantBuildFail { 298 t.Fatalf("target %s did not fail to build", test.target) 299 } 300 }) 301 } 302} 303