mirror of
https://github.com/golang/go
synced 2024-11-23 20:30:04 -07:00
testenv: abstract run-with-timeout into testenv
This lifts the logic to run a subcommand with a timeout in a test from the runtime's runTestProg into testenv. The implementation is unchanged in this CL. We'll improve it in a future CL. Currently, tests that run subcommands usually just timeout with no useful output if the subcommand runs for too long. This is a step toward improving this. For #37405. Change-Id: I2298770db516e216379c4c438e05d23cbbdda51d Reviewed-on: https://go-review.googlesource.com/c/go/+/370701 Trust: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Bryan Mills <bcmills@google.com>
This commit is contained in:
parent
9c6e8f63c0
commit
6b89773722
@ -11,6 +11,7 @@
|
||||
package testenv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"internal/cfg"
|
||||
@ -22,6 +23,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Builder reports the name of the builder running this test
|
||||
@ -306,3 +308,52 @@ func SkipIfShortAndSlow(t testing.TB) {
|
||||
t.Skipf("skipping test in -short mode on %s", runtime.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
// RunWithTimeout runs cmd and returns its combined output. If the
|
||||
// subprocess exits with a non-zero status, it will log that status
|
||||
// and return a non-nil error, but this is not considered fatal.
|
||||
func RunWithTimeout(t testing.TB, cmd *exec.Cmd) ([]byte, error) {
|
||||
args := cmd.Args
|
||||
if args == nil {
|
||||
args = []string{cmd.Path}
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
cmd.Stdout = &b
|
||||
cmd.Stderr = &b
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatalf("starting %s: %v", args, err)
|
||||
}
|
||||
|
||||
// If the process doesn't complete within 1 minute,
|
||||
// assume it is hanging and kill it to get a stack trace.
|
||||
p := cmd.Process
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
scale := 1
|
||||
// This GOARCH/GOOS test is copied from cmd/dist/test.go.
|
||||
// TODO(iant): Have cmd/dist update the environment variable.
|
||||
if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
|
||||
scale = 2
|
||||
}
|
||||
if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
|
||||
if sc, err := strconv.Atoi(s); err == nil {
|
||||
scale = sc
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(time.Duration(scale) * time.Minute):
|
||||
p.Signal(Sigquit)
|
||||
}
|
||||
}()
|
||||
|
||||
err := cmd.Wait()
|
||||
if err != nil {
|
||||
t.Logf("%s exit status: %v", args, err)
|
||||
}
|
||||
close(done)
|
||||
|
||||
return b.Bytes(), err
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows || plan9 || (js && wasm)
|
||||
|
||||
package runtime_test
|
||||
package testenv
|
||||
|
||||
import "os"
|
||||
|
||||
// sigquit is the signal to send to kill a hanging testdata program.
|
||||
// Sigquit is the signal to send to kill a hanging subprocess.
|
||||
// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill.
|
||||
var sigquit = os.Kill
|
||||
var Sigquit = os.Kill
|
13
src/internal/testenv/testenv_unix.go
Normal file
13
src/internal/testenv/testenv_unix.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
|
||||
package testenv
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Sigquit is the signal to send to kill a hanging subprocess.
|
||||
// Send SIGQUIT to get a stack trace.
|
||||
var Sigquit = syscall.SIGQUIT
|
@ -15,11 +15,9 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var toRemove []string
|
||||
@ -71,43 +69,8 @@ func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
|
||||
if testing.Short() {
|
||||
cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
|
||||
}
|
||||
var b bytes.Buffer
|
||||
cmd.Stdout = &b
|
||||
cmd.Stderr = &b
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatalf("starting %s %s: %v", exe, name, err)
|
||||
}
|
||||
|
||||
// If the process doesn't complete within 1 minute,
|
||||
// assume it is hanging and kill it to get a stack trace.
|
||||
p := cmd.Process
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
scale := 1
|
||||
// This GOARCH/GOOS test is copied from cmd/dist/test.go.
|
||||
// TODO(iant): Have cmd/dist update the environment variable.
|
||||
if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
|
||||
scale = 2
|
||||
}
|
||||
if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
|
||||
if sc, err := strconv.Atoi(s); err == nil {
|
||||
scale = sc
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(time.Duration(scale) * time.Minute):
|
||||
p.Signal(sigquit)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
t.Logf("%s %s exit status: %v", exe, name, err)
|
||||
}
|
||||
close(done)
|
||||
|
||||
return b.String()
|
||||
out, _ := testenv.RunWithTimeout(t, cmd)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
var serializeBuild = make(chan bool, 2)
|
||||
|
@ -21,16 +21,12 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// sigquit is the signal to send to kill a hanging testdata program.
|
||||
// Send SIGQUIT to get a stack trace.
|
||||
var sigquit = syscall.SIGQUIT
|
||||
|
||||
func init() {
|
||||
if runtime.Sigisblocked(int(syscall.SIGQUIT)) {
|
||||
// We can't use SIGQUIT to kill subprocesses because
|
||||
// it's blocked. Use SIGKILL instead. See issue
|
||||
// #19196 for an example of when this happens.
|
||||
sigquit = syscall.SIGKILL
|
||||
testenv.Sigquit = syscall.SIGKILL
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user