diff options
Diffstat (limited to 'src/cmd/test2json')
-rw-r--r-- | src/cmd/test2json/main.go | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/cmd/test2json/main.go b/src/cmd/test2json/main.go new file mode 100644 index 0000000..fdf681a --- /dev/null +++ b/src/cmd/test2json/main.go @@ -0,0 +1,149 @@ +// Copyright 2017 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. + +// Test2json converts go test output to a machine-readable JSON stream. +// +// Usage: +// +// go tool test2json [-p pkg] [-t] [./pkg.test -test.v [-test.paniconexit0]] +// +// Test2json runs the given test command and converts its output to JSON; +// with no command specified, test2json expects test output on standard input. +// It writes a corresponding stream of JSON events to standard output. +// There is no unnecessary input or output buffering, so that +// the JSON stream can be read for “live updates” of test status. +// +// The -p flag sets the package reported in each test event. +// +// The -t flag requests that time stamps be added to each test event. +// +// The test must be invoked with -test.v. Additionally passing +// -test.paniconexit0 will cause test2json to exit with a non-zero +// status if one of the tests being run calls os.Exit(0). +// +// Note that test2json is only intended for converting a single test +// binary's output. To convert the output of a "go test" command, +// use "go test -json" instead of invoking test2json directly. +// +// Output Format +// +// The JSON stream is a newline-separated sequence of TestEvent objects +// corresponding to the Go struct: +// +// type TestEvent struct { +// Time time.Time // encodes as an RFC3339-format string +// Action string +// Package string +// Test string +// Elapsed float64 // seconds +// Output string +// } +// +// The Time field holds the time the event happened. +// It is conventionally omitted for cached test results. +// +// The Action field is one of a fixed set of action descriptions: +// +// run - the test has started running +// pause - the test has been paused +// cont - the test has continued running +// pass - the test passed +// bench - the benchmark printed log output but did not fail +// fail - the test or benchmark failed +// output - the test printed output +// skip - the test was skipped or the package contained no tests +// +// The Package field, if present, specifies the package being tested. +// When the go command runs parallel tests in -json mode, events from +// different tests are interlaced; the Package field allows readers to +// separate them. +// +// The Test field, if present, specifies the test, example, or benchmark +// function that caused the event. Events for the overall package test +// do not set Test. +// +// The Elapsed field is set for "pass" and "fail" events. It gives the time +// elapsed for the specific test or the overall package test that passed or failed. +// +// The Output field is set for Action == "output" and is a portion of the test's output +// (standard output and standard error merged together). The output is +// unmodified except that invalid UTF-8 output from a test is coerced +// into valid UTF-8 by use of replacement characters. With that one exception, +// the concatenation of the Output fields of all output events is the exact +// output of the test execution. +// +// When a benchmark runs, it typically produces a single line of output +// giving timing results. That line is reported in an event with Action == "output" +// and no Test field. If a benchmark logs output or reports a failure +// (for example, by using b.Log or b.Error), that extra output is reported +// as a sequence of events with Test set to the benchmark name, terminated +// by a final event with Action == "bench" or "fail". +// Benchmarks have no events with Action == "run", "pause", or "cont". +// +package main + +import ( + "flag" + "fmt" + exec "internal/execabs" + "io" + "os" + + "cmd/internal/test2json" +) + +var ( + flagP = flag.String("p", "", "report `pkg` as the package being tested in each event") + flagT = flag.Bool("t", false, "include timestamps in events") +) + +func usage() { + fmt.Fprintf(os.Stderr, "usage: go tool test2json [-p pkg] [-t] [./pkg.test -test.v]\n") + os.Exit(2) +} + +func main() { + flag.Usage = usage + flag.Parse() + + var mode test2json.Mode + if *flagT { + mode |= test2json.Timestamp + } + c := test2json.NewConverter(os.Stdout, *flagP, mode) + defer c.Close() + + if flag.NArg() == 0 { + io.Copy(c, os.Stdin) + } else { + args := flag.Args() + cmd := exec.Command(args[0], args[1:]...) + w := &countWriter{0, c} + cmd.Stdout = w + cmd.Stderr = w + err := cmd.Run() + if err != nil { + if w.n > 0 { + // Assume command printed why it failed. + } else { + fmt.Fprintf(c, "test2json: %v\n", err) + } + } + c.Exited(err) + if err != nil { + c.Close() + os.Exit(1) + } + } +} + +type countWriter struct { + n int64 + w io.Writer +} + +func (w *countWriter) Write(b []byte) (int, error) { + w.n += int64(len(b)) + return w.w.Write(b) +} |