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 [-test.paniconexit0]]
    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 must be invoked with -test.v. Additionally passing
    22  // -test.paniconexit0 will cause test2json to exit with a non-zero
    23  // status if one of the tests being run calls os.Exit(0).
    24  //
    25  // Note that test2json is only intended for converting a single test
    26  // binary's output. To convert the output of a "go test" command,
    27  // use "go test -json" instead of invoking test2json directly.
    28  //
    29  // Output Format
    30  //
    31  // The JSON stream is a newline-separated sequence of TestEvent objects
    32  // corresponding to the Go struct:
    33  //
    34  //	type TestEvent struct {
    35  //		Time    time.Time // encodes as an RFC3339-format string
    36  //		Action  string
    37  //		Package string
    38  //		Test    string
    39  //		Elapsed float64 // seconds
    40  //		Output  string
    41  //	}
    42  //
    43  // The Time field holds the time the event happened.
    44  // It is conventionally omitted for cached test results.
    45  //
    46  // The Action field is one of a fixed set of action descriptions:
    47  //
    48  //	run    - the test has started running
    49  //	pause  - the test has been paused
    50  //	cont   - the test has continued running
    51  //	pass   - the test passed
    52  //	bench  - the benchmark printed log output but did not fail
    53  //	fail   - the test or benchmark failed
    54  //	output - the test printed output
    55  //	skip   - the test was skipped or the package contained no tests
    56  //
    57  // The Package field, if present, specifies the package being tested.
    58  // When the go command runs parallel tests in -json mode, events from
    59  // different tests are interlaced; the Package field allows readers to
    60  // separate them.
    61  //
    62  // The Test field, if present, specifies the test, example, or benchmark
    63  // function that caused the event. Events for the overall package test
    64  // do not set Test.
    65  //
    66  // The Elapsed field is set for "pass" and "fail" events. It gives the time
    67  // elapsed for the specific test or the overall package test that passed or failed.
    68  //
    69  // The Output field is set for Action == "output" and is a portion of the test's output
    70  // (standard output and standard error merged together). The output is
    71  // unmodified except that invalid UTF-8 output from a test is coerced
    72  // into valid UTF-8 by use of replacement characters. With that one exception,
    73  // the concatenation of the Output fields of all output events is the exact
    74  // output of the test execution.
    75  //
    76  // When a benchmark runs, it typically produces a single line of output
    77  // giving timing results. That line is reported in an event with Action == "output"
    78  // and no Test field. If a benchmark logs output or reports a failure
    79  // (for example, by using b.Log or b.Error), that extra output is reported
    80  // as a sequence of events with Test set to the benchmark name, terminated
    81  // by a final event with Action == "bench" or "fail".
    82  // Benchmarks have no events with Action == "run", "pause", or "cont".
    83  //
    84  package main
    85  
    86  import (
    87  	"flag"
    88  	"fmt"
    89  	exec "internal/execabs"
    90  	"io"
    91  	"os"
    92  
    93  	"cmd/internal/test2json"
    94  )
    95  
    96  var (
    97  	flagP = flag.String("p", "", "report `pkg` as the package being tested in each event")
    98  	flagT = flag.Bool("t", false, "include timestamps in events")
    99  )
   100  
   101  func usage() {
   102  	fmt.Fprintf(os.Stderr, "usage: go tool test2json [-p pkg] [-t] [./pkg.test -test.v]\n")
   103  	os.Exit(2)
   104  }
   105  
   106  func main() {
   107  	flag.Usage = usage
   108  	flag.Parse()
   109  
   110  	var mode test2json.Mode
   111  	if *flagT {
   112  		mode |= test2json.Timestamp
   113  	}
   114  	c := test2json.NewConverter(os.Stdout, *flagP, mode)
   115  	defer c.Close()
   116  
   117  	if flag.NArg() == 0 {
   118  		io.Copy(c, os.Stdin)
   119  	} else {
   120  		args := flag.Args()
   121  		cmd := exec.Command(args[0], args[1:]...)
   122  		w := &countWriter{0, c}
   123  		cmd.Stdout = w
   124  		cmd.Stderr = w
   125  		err := cmd.Run()
   126  		if err != nil {
   127  			if w.n > 0 {
   128  				// Assume command printed why it failed.
   129  			} else {
   130  				fmt.Fprintf(c, "test2json: %v\n", err)
   131  			}
   132  		}
   133  		c.Exited(err)
   134  		if err != nil {
   135  			c.Close()
   136  			os.Exit(1)
   137  		}
   138  	}
   139  }
   140  
   141  type countWriter struct {
   142  	n int64
   143  	w io.Writer
   144  }
   145  
   146  func (w *countWriter) Write(b []byte) (int, error) {
   147  	w.n += int64(len(b))
   148  	return w.w.Write(b)
   149  }
   150  

View as plain text