| // 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" | 
 | 	"io" | 
 | 	"os" | 
 | 	"os/exec" | 
 |  | 
 | 	"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) | 
 | } |