Source file src/cmd/test2json/main.go
1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Test2json converts go test output to a machine-readable JSON stream. 6 // 7 // Usage: 8 // 9 // go tool test2json [-p pkg] [-t] [./pkg.test -test.v=test2json] 10 // 11 // Test2json runs the given test command and converts its output to JSON; 12 // with no command specified, test2json expects test output on standard input. 13 // It writes a corresponding stream of JSON events to standard output. 14 // There is no unnecessary input or output buffering, so that 15 // the JSON stream can be read for “live updates” of test status. 16 // 17 // The -p flag sets the package reported in each test event. 18 // 19 // The -t flag requests that time stamps be added to each test event. 20 // 21 // The test should be invoked with -test.v=test2json. Using only -test.v 22 // (or -test.v=true) is permissible but produces lower fidelity results. 23 // 24 // Note that "go test -json" takes care of invoking test2json correctly, 25 // so "go tool test2json" is only needed when a test binary is being run 26 // separately from "go test". Use "go test -json" whenever possible. 27 // 28 // Note also that test2json is only intended for converting a single test 29 // binary's output. To convert the output of a "go test" command that 30 // runs multiple packages, again use "go test -json". 31 // 32 // # Output Format 33 // 34 // The JSON stream is a newline-separated sequence of TestEvent objects 35 // corresponding to the Go struct: 36 // 37 // type TestEvent struct { 38 // Time time.Time // encodes as an RFC3339-format string 39 // Action string 40 // Package string 41 // Test string 42 // Elapsed float64 // seconds 43 // Output string 44 // OutputType string 45 // FailedBuild string 46 // } 47 // 48 // The Time field holds the time the event happened. 49 // It is conventionally omitted for cached test results. 50 // 51 // The Action field is one of a fixed set of action descriptions: 52 // 53 // start - the test binary is about to be executed 54 // run - the test has started running 55 // pause - the test has been paused 56 // cont - the test has continued running 57 // pass - the test passed 58 // bench - the benchmark printed log output but did not fail 59 // fail - the test or benchmark failed 60 // output - the test printed output 61 // skip - the test was skipped or the package contained no tests 62 // 63 // Every JSON stream begins with a "start" event. 64 // 65 // The Package field, if present, specifies the package being tested. 66 // When the go command runs parallel tests in -json mode, events from 67 // different tests are interlaced; the Package field allows readers to 68 // separate them. 69 // 70 // The Test field, if present, specifies the test, example, or benchmark 71 // function that caused the event. Events for the overall package test 72 // do not set Test. 73 // 74 // The Elapsed field is set for "pass" and "fail" events. It gives the time 75 // elapsed for the specific test or the overall package test that passed or failed. 76 // 77 // The Output field is set for Action == "output" and is a portion of the test's output 78 // (standard output and standard error merged together). The output is 79 // unmodified except that invalid UTF-8 output from a test is coerced 80 // into valid UTF-8 by use of replacement characters. With that one exception, 81 // the concatenation of the Output fields of all output events is the exact 82 // output of the test execution. 83 // 84 // The FailedBuild field is set for Action == "fail" if the test failure was 85 // caused by a build failure. It contains the package ID of the package that 86 // failed to build. This matches the ImportPath field of the "go list" output, 87 // as well as the BuildEvent.ImportPath field as emitted by "go build -json". 88 // 89 // The OutputType field *may* be set for Action == "output" and indicates the 90 // type of output. OutputType will be one of the following: 91 // 92 // (blank) - regular output 93 // frame - test framing, such as "=== RUN ..." or "--- FAIL: ..." 94 // error - an error produced by Error(f) or Fatal(f) 95 // error-continue - continuation of a multi-line error 96 // 97 // When a benchmark runs, it typically produces a single line of output 98 // giving timing results. That line is reported in an event with Action == "output" 99 // and no Test field. If a benchmark logs output or reports a failure 100 // (for example, by using b.Log or b.Error), that extra output is reported 101 // as a sequence of events with Test set to the benchmark name, terminated 102 // by a final event with Action == "bench" or "fail". 103 // Benchmarks have no events with Action == "pause". 104 package main 105 106 import ( 107 "flag" 108 "fmt" 109 "io" 110 "os" 111 "os/exec" 112 "os/signal" 113 114 "cmd/internal/telemetry/counter" 115 "cmd/internal/test2json" 116 ) 117 118 var ( 119 flagP = flag.String("p", "", "report `pkg` as the package being tested in each event") 120 flagT = flag.Bool("t", false, "include timestamps in events") 121 ) 122 123 func usage() { 124 fmt.Fprintf(os.Stderr, "usage: go tool test2json [-p pkg] [-t] [./pkg.test -test.v]\n") 125 os.Exit(2) 126 } 127 128 // ignoreSignals ignore the interrupt signals. 129 func ignoreSignals() { 130 signal.Ignore(signalsToIgnore...) 131 } 132 133 func main() { 134 counter.Open() 135 136 flag.Usage = usage 137 flag.Parse() 138 counter.Inc("test2json/invocations") 139 counter.CountFlags("test2json/flag:", *flag.CommandLine) 140 141 var mode test2json.Mode 142 if *flagT { 143 mode |= test2json.Timestamp 144 } 145 c := test2json.NewConverter(os.Stdout, *flagP, mode) 146 defer c.Close() 147 148 if flag.NArg() == 0 { 149 io.Copy(c, os.Stdin) 150 } else { 151 args := flag.Args() 152 cmd := exec.Command(args[0], args[1:]...) 153 w := &countWriter{0, c} 154 cmd.Stdout = w 155 cmd.Stderr = w 156 ignoreSignals() 157 err := cmd.Run() 158 if err != nil { 159 if w.n > 0 { 160 // Assume command printed why it failed. 161 } else { 162 fmt.Fprintf(c, "test2json: %v\n", err) 163 } 164 } 165 c.Exited(err) 166 if err != nil { 167 c.Close() 168 os.Exit(1) 169 } 170 } 171 } 172 173 type countWriter struct { 174 n int64 175 w io.Writer 176 } 177 178 func (w *countWriter) Write(b []byte) (int, error) { 179 w.n += int64(len(b)) 180 return w.w.Write(b) 181 } 182