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
|
package testenv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"internal/cfg"
|
"internal/cfg"
|
||||||
@ -22,6 +23,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Builder reports the name of the builder running this test
|
// 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)
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build windows || plan9 || (js && wasm)
|
//go:build windows || plan9 || (js && wasm)
|
||||||
|
|
||||||
package runtime_test
|
package testenv
|
||||||
|
|
||||||
import "os"
|
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.
|
// 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"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var toRemove []string
|
var toRemove []string
|
||||||
@ -71,43 +69,8 @@ func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
|
|||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
|
cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
|
||||||
}
|
}
|
||||||
var b bytes.Buffer
|
out, _ := testenv.RunWithTimeout(t, cmd)
|
||||||
cmd.Stdout = &b
|
return string(out)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var serializeBuild = make(chan bool, 2)
|
var serializeBuild = make(chan bool, 2)
|
||||||
|
@ -21,16 +21,12 @@ import (
|
|||||||
"unsafe"
|
"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() {
|
func init() {
|
||||||
if runtime.Sigisblocked(int(syscall.SIGQUIT)) {
|
if runtime.Sigisblocked(int(syscall.SIGQUIT)) {
|
||||||
// We can't use SIGQUIT to kill subprocesses because
|
// We can't use SIGQUIT to kill subprocesses because
|
||||||
// it's blocked. Use SIGKILL instead. See issue
|
// it's blocked. Use SIGKILL instead. See issue
|
||||||
// #19196 for an example of when this happens.
|
// #19196 for an example of when this happens.
|
||||||
sigquit = syscall.SIGKILL
|
testenv.Sigquit = syscall.SIGKILL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user