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 }