• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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