Files
kubernetes/vendor/gotest.tools/gotestsum/main.go
2019-07-31 17:43:02 -04:00

215 lines
4.9 KiB
Go

package main
import (
"context"
"fmt"
"io"
"os"
"os/exec"
"strings"
"github.com/fatih/color"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/pflag"
"gotest.tools/gotestsum/testjson"
)
var version = "master"
func main() {
name := os.Args[0]
flags, opts := setupFlags(name)
switch err := flags.Parse(os.Args[1:]); {
case err == pflag.ErrHelp:
os.Exit(0)
case err != nil:
log.Error(err.Error())
flags.Usage()
os.Exit(1)
}
opts.args = flags.Args()
setupLogging(opts)
if opts.version {
fmt.Fprintf(os.Stdout, "gotestsum version %s\n", version)
os.Exit(0)
}
switch err := run(opts).(type) {
case nil:
case *exec.ExitError:
// go test should already report the error to stderr so just exit with
// the same status code
os.Exit(ExitCodeWithDefault(err))
default:
fmt.Fprintln(os.Stderr, name+": Error: "+err.Error())
os.Exit(3)
}
}
func setupFlags(name string) (*pflag.FlagSet, *options) {
opts := &options{noSummary: newNoSummaryValue()}
flags := pflag.NewFlagSet(name, pflag.ContinueOnError)
flags.SetInterspersed(false)
flags.Usage = func() {
fmt.Fprintf(os.Stderr, `Usage:
%s [flags] [--] [go test flags]
Flags:
`, name)
flags.PrintDefaults()
fmt.Fprint(os.Stderr, `
Formats:
dots print a character for each test
short print a line for each package
short-verbose print a line for each test and package
standard-quiet default go test format
standard-verbose default go test -v format
`)
}
flags.BoolVar(&opts.debug, "debug", false, "enabled debug")
flags.StringVarP(&opts.format, "format", "f",
lookEnvWithDefault("GOTESTSUM_FORMAT", "short"),
"print format of test input")
flags.BoolVar(&opts.rawCommand, "raw-command", false,
"don't prepend 'go test -json' to the 'go test' command")
flags.StringVar(&opts.jsonFile, "jsonfile",
lookEnvWithDefault("GOTESTSUM_JSONFILE", ""),
"write all TestEvents to file")
flags.StringVar(&opts.junitFile, "junitfile",
lookEnvWithDefault("GOTESTSUM_JUNITFILE", ""),
"write a JUnit XML file")
flags.BoolVar(&opts.noColor, "no-color", false, "disable color output")
flags.Var(opts.noSummary, "no-summary",
fmt.Sprintf("do not print summary of: %s", testjson.SummarizeAll.String()))
flags.BoolVar(&opts.version, "version", false, "show version and exit")
return flags, opts
}
func lookEnvWithDefault(key, defValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defValue
}
type options struct {
args []string
format string
debug bool
rawCommand bool
jsonFile string
junitFile string
noColor bool
noSummary *noSummaryValue
version bool
}
func setupLogging(opts *options) {
if opts.debug {
log.SetLevel(log.DebugLevel)
}
if opts.noColor {
color.NoColor = true
}
}
// TODO: add flag --max-failures
func run(opts *options) error {
ctx := context.Background()
goTestProc, err := startGoTest(ctx, goTestCmdArgs(opts))
if err != nil {
return errors.Wrapf(err, "failed to run %s %s",
goTestProc.cmd.Path,
strings.Join(goTestProc.cmd.Args, " "))
}
defer goTestProc.cancel()
out := os.Stdout
handler, err := newEventHandler(opts, out, os.Stderr)
if err != nil {
return err
}
defer handler.Close() // nolint: errcheck
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
Stdout: goTestProc.stdout,
Stderr: goTestProc.stderr,
Handler: handler,
})
if err != nil {
return err
}
if err := testjson.PrintSummary(out, exec, opts.noSummary.value); err != nil {
return err
}
if err := writeJUnitFile(opts.junitFile, exec); err != nil {
return err
}
return goTestProc.cmd.Wait()
}
func goTestCmdArgs(opts *options) []string {
args := opts.args
defaultArgs := []string{"go", "test"}
switch {
case opts.rawCommand:
return args
case len(args) == 0:
return append(defaultArgs, "-json", pathFromEnv("./..."))
case !hasJSONArg(args):
defaultArgs = append(defaultArgs, "-json")
}
if testPath := pathFromEnv(""); testPath != "" {
args = append(args, testPath)
}
return append(defaultArgs, args...)
}
func pathFromEnv(defaultPath string) string {
return lookEnvWithDefault("TEST_DIRECTORY", defaultPath)
}
func hasJSONArg(args []string) bool {
for _, arg := range args {
if arg == "-json" || arg == "--json" {
return true
}
}
return false
}
type proc struct {
cmd *exec.Cmd
stdout io.Reader
stderr io.Reader
cancel func()
}
func startGoTest(ctx context.Context, args []string) (proc, error) {
if len(args) == 0 {
return proc{}, errors.New("missing command to run")
}
ctx, cancel := context.WithCancel(ctx)
p := proc{
cmd: exec.CommandContext(ctx, args[0], args[1:]...),
cancel: cancel,
}
log.Debugf("exec: %s", p.cmd.Args)
var err error
p.stdout, err = p.cmd.StdoutPipe()
if err != nil {
return p, err
}
p.stderr, err = p.cmd.StderrPipe()
if err != nil {
return p, err
}
err = p.cmd.Start()
if err == nil {
log.Debugf("go test pid: %d", p.cmd.Process.Pid)
}
return p, err
}