// Copyright 2012 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.
package runtime_test
import (
"bytes"
"flag"
"fmt"
"internal/testenv"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"testing"
"time"
)
var toRemove []string
func TestMain(m *testing.M) {
status := m.Run()
for _, file := range toRemove {
os.RemoveAll(file)
}
os.Exit(status)
}
var testprog struct {
sync.Mutex
dir string
target map[string]buildexe
}
type buildexe struct {
exe string
err error
}
func runTestProg(t *testing.T, binary, name string, env ...string) string {
if *flagQuick {
t.Skip("-quick")
}
testenv.MustHaveGoBuild(t)
exe, err := buildTestProg(t, binary)
if err != nil {
t.Fatal(err)
}
return runBuiltTestProg(t, exe, name, env...)
}
func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
if *flagQuick {
t.Skip("-quick")
}
testenv.MustHaveGoBuild(t)
cmd := testenv.CleanCmdEnv(exec.Command(exe, name))
cmd.Env = append(cmd.Env, env...)
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()
}
func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
if *flagQuick {
t.Skip("-quick")
}
testprog.Lock()
defer testprog.Unlock()
if testprog.dir == "" {
dir, err := os.MkdirTemp("", "go-build")
if err != nil {
t.Fatalf("failed to create temp directory: %v", err)
}
testprog.dir = dir
toRemove = append(toRemove, dir)
}
if testprog.target == nil {
testprog.target = make(map[string]buildexe)
}
name := binary
if len(flags) > 0 {
name += "_" + strings.Join(flags, "_")
}
target, ok := testprog.target[name]
if ok {
return target.exe, target.err
}
exe := filepath.Join(testprog.dir, name+".exe")
cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
cmd.Dir = "testdata/" + binary
out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
if err != nil {
target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)