diff --git a/docs/go/core/rules.md b/docs/go/core/rules.md index 3dd32e775b..0331f6c1b7 100644 --- a/docs/go/core/rules.md +++ b/docs/go/core/rules.md @@ -334,7 +334,7 @@ This declares a set of source files and related dependencies that can be embedde
go_test(name, cdeps, cgo, clinkopts, copts, cppopts, cxxopts, data, deps, embed, embedsrcs, env, env_inherit, gc_goopts, gc_linkopts, goarch, goos, gotags, importpath, linkmode, msan, pure, - race, rundir, srcs, static, x_defs) + race, register_timeout_handler, rundir, srcs, static, x_defs)This builds a set of tests that can be run with `bazel test`.
on
, off
, or auto
. Not available when cgo is disabled. In most cases, it's better to control this on the command line with --@io_bazel_rules_go//go/config:msan
. See [mode attributes], specifically [msan]. | String | optional | "auto" |
| pure | Controls whether cgo source code and dependencies are compiled and linked, similar to setting CGO_ENABLED
. May be one of on
, off
, or auto
. If auto
, pure mode is enabled when no C/C++ toolchain is configured or when cross-compiling. It's usually better to control this on the command line with --@io_bazel_rules_go//go/config:pure
. See [mode attributes], specifically [pure]. | String | optional | "auto" |
| race | Controls whether code is instrumented for race detection. May be one of on
, off
, or auto
. Not available when cgo is disabled. In most cases, it's better to control this on the command line with --@io_bazel_rules_go//go/config:race
. See [mode attributes], specifically [race]. | String | optional | "auto" |
+| register_timeout_handler | Whether to register a SIGTERM handler to give improved output on test timeouts.go test
so it is easy to write compatible tests..
makes the test behave the normal way for a bazel test, except that the working directory is always that of the test's repository, which is not necessarily the main repository..go
and .s
files are permitted, unless the cgo
attribute is set, in which case, .c .cc .cpp .cxx .h .hh .hpp .hxx .inc .m .mm
files are also permitted. Files may be filtered at build time using Go [build constraints]. | List of labels | optional | [] |
| static | Controls whether a binary is statically linked. May be one of on
, off
, or auto
. Not available on all platforms or in all modes. It's usually better to control this on the command line with --@io_bazel_rules_go//go/config:static
. See [mode attributes], specifically [static]. | String | optional | "auto" |
diff --git a/go/private/rules/test.bzl b/go/private/rules/test.bzl
index 1283776a95..4c63f83f33 100644
--- a/go/private/rules/test.bzl
+++ b/go/private/rules/test.bzl
@@ -112,6 +112,10 @@ def _go_test_impl(ctx):
"l_test=" + external_source.library.importpath,
)
arguments.add("-pkgname", internal_source.library.importpath)
+
+ if not ctx.attr.register_timeout_handler:
+ # Boolean Go flags require =, so we can't just use the attr as an arg value, we need to convert it ourselves.
+ arguments.add("-register_timeout_handler=false")
arguments.add_all(go_srcs, before_each = "-src", format_each = "l=%s")
ctx.actions.run(
inputs = go_srcs,
@@ -415,6 +419,12 @@ _go_test_kwargs = {
See [Cross compilation] for more information.
""",
),
+ "register_timeout_handler": attr.bool(
+ default = True,
+ doc = """Whether to register a SIGTERM handler to give improved output on test timeouts.
+
+ Setting this to False may be useful if your test itself also handles signals.""",
+ ),
"_go_context_data": attr.label(default = "//:go_context_data", cfg = go_transition),
"_testmain_additional_deps": attr.label_list(
providers = [GoLibrary],
diff --git a/go/tools/builders/generate_test_main.go b/go/tools/builders/generate_test_main.go
index c061d5e993..a6279e4889 100644
--- a/go/tools/builders/generate_test_main.go
+++ b/go/tools/builders/generate_test_main.go
@@ -63,15 +63,16 @@ type Example struct {
// Cases holds template data.
type Cases struct {
- Imports []*Import
- Tests []TestCase
- Benchmarks []TestCase
- FuzzTargets []TestCase
- Examples []Example
- TestMain string
- CoverMode string
- CoverFormat string
- Pkgname string
+ Imports []*Import
+ Tests []TestCase
+ Benchmarks []TestCase
+ FuzzTargets []TestCase
+ Examples []Example
+ TestMain string
+ CoverMode string
+ CoverFormat string
+ Pkgname string
+ RegisterTimeoutHandler bool
}
// Version returns whether v is a supported Go version (like "go1.18").
@@ -235,7 +236,9 @@ func main() {
}
}
{{end}}
+ {{if .RegisterTimeoutHandler}}
bzltestutil.RegisterTimeoutHandler()
+ {{end}}
{{if not .TestMain}}
res := m.Run()
{{else}}
@@ -261,6 +264,7 @@ func genTestMain(args []string) error {
coverMode := flags.String("cover_mode", "", "the coverage mode to use")
coverFormat := flags.String("cover_format", "", "the coverage report type to generate (go_cover or lcov)")
pkgname := flags.String("pkgname", "", "package name of test")
+ registerTimeoutHandler := flags.Bool("register_timeout_handler", true, "whether to register a SIGTERM handler for improved timeout reporting")
flags.Var(&imports, "import", "Packages to import")
flags.Var(&sources, "src", "Sources to process for tests")
if err := flags.Parse(args); err != nil {
@@ -269,6 +273,12 @@ func genTestMain(args []string) error {
if err := goenv.checkFlags(); err != nil {
return err
}
+ // Boolean flags must be specified using `--foo=bar`, not `--foo bar`.
+ // Verify that there are no unexpected positional arguments, which may be the case if e.g. someone naively called
+ // args.add("-register_timeout_handler", ctx.attr.register_timeout_handler)
+ if len(flag.Args()) > 0 {
+ return fmt.Errorf("unexpected positional args: %v", flag.Args())
+ }
// Process import args
importMap := map[string]*Import{}
for _, imp := range imports {
@@ -309,9 +319,10 @@ func genTestMain(args []string) error {
}
cases := Cases{
- CoverFormat: *coverFormat,
- CoverMode: *coverMode,
- Pkgname: *pkgname,
+ CoverFormat: *coverFormat,
+ CoverMode: *coverMode,
+ Pkgname: *pkgname,
+ RegisterTimeoutHandler: *registerTimeoutHandler,
}
testFileSet := token.NewFileSet()
diff --git a/tests/core/go_test/BUILD.bazel b/tests/core/go_test/BUILD.bazel
index e44564cca9..3adc05e242 100644
--- a/tests/core/go_test/BUILD.bazel
+++ b/tests/core/go_test/BUILD.bazel
@@ -250,6 +250,16 @@ go_test(
shard_count = 2,
)
+go_test(
+ name = "sigterm_handler_test",
+ srcs = ["sigterm_handler_test.go"],
+ register_timeout_handler = False,
+ target_compatible_with = select({
+ "@platforms//os:windows": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ }),
+)
+
go_bazel_test(
name = "env_inherit_test",
srcs = ["env_inherit_test.go"],
diff --git a/tests/core/go_test/sigterm_handler_test.go b/tests/core/go_test/sigterm_handler_test.go
new file mode 100644
index 0000000000..fd5bc875ea
--- /dev/null
+++ b/tests/core/go_test/sigterm_handler_test.go
@@ -0,0 +1,36 @@
+package sigterm_handler_test
+
+import (
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+ "testing"
+)
+
+func TestRegisterSignalHandler(t *testing.T) {
+ called := false
+ var wg sync.WaitGroup
+
+ wg.Add(1)
+
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, syscall.SIGTERM)
+
+ go func() {
+ switch <-c {
+ case syscall.SIGTERM:
+ called = true
+ wg.Done()
+ }
+ }()
+
+ if err := syscall.Kill(os.Getpid(), syscall.SIGTERM); err != nil {
+ t.Fatalf("Failed to send SIGTERM: %v", err)
+ }
+ wg.Wait()
+
+ if !called {
+ t.Fatal("Our handler has not run")
+ }
+}