design/2981: remove non-streaming mode
During discussion in https://github.com/golang/go/issues/12826
adg@, minux@ and nodir@ decided that always streaming would simplify
-json flag design.
Also this change makes `testing.Result` internal, which decreases
modifications to the public api.
Change-Id: I40444bb9b34db53ecf2d6ad606578a874c4bd473
Reviewed-on: https://go-review.googlesource.com/15603
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/design/2981-go-test-json.md b/design/2981-go-test-json.md
index 3639079..71efb8f 100644
--- a/design/2981-go-test-json.md
+++ b/design/2981-go-test-json.md
@@ -4,7 +4,7 @@
_With initial input by Russ Cox, Caleb Spare, Andrew Gerrand and Minux Ma._
-Last updated: 2015-10-06
+Last updated: 2015-10-07
Discussion at https://golang.org/issue/2981.
@@ -20,15 +20,12 @@
## Abstract
Add `-json` flag to `go test`.
-When it is passed, `go test` stdout is valid indented JSON format.
-
-Add `-stream` flag to `go test` to explicitly specify that multiple JSON
-objects in stdout is OK.
+When specified, `go test` stdout is JSON.
## Background
There is a clear need in parsing test and benchmark results by third party
-tools, see feedback in https://github.com/golang/go/issues/2981.
+tools, see feedback in https://golang.org/issue/2981.
Currently `go test` output format is suited for humans, but not computers.
Also a change to the current format may break existing programs that parse
`go test` output.
@@ -39,43 +36,31 @@
third party tools interpreting `go test` output can stream results too.
`-json` flag was originally proposed by Russ Cox in
-https://github.com/golang/go/issues/2981 in 2012.
+https://golang.org/issue/2981 in 2012.
This proposal differs from the original:
* supports streaming
* `go test` JSON output contains unrecognized test binary output.
-* no changes to `testing.InternalTest` and `testing.InternalBenchmark`.
+* minimal changes in `testing` package.
## Proposal
I propose the following user-visible changes:
-* `go test`: add `-json` and `-stream` flags.
- * `-json`: all `go test` stdout is valid indented JSON containing all
- test binary artifacts. Format below.
- * `-json -stream`: all `go test` stdout is a series of valid indented JSON
- objects, delimited by newline.
- They are printed on each test binary output.
-
- The format is the same as without `-stream`, but not all JSON object
- properties are present.
- If synthesized to one JSON object, the output is same as without
- streaming.
+* add `-json` flag to `go test`
+ * `-json`: all `go test` stdout is indented JSON objects containing
+ test binary artifacts, separated by newline.
+ Format below.
* `-json -v`: verbose messages are printed to stderr,
so stdout contains only JSON.
* `-json -n`: not supported
* `-json -x`: not supported
- * `-stream`: print all test binary output.
- If not specified, streaming is enabled under certain undocumented
- conditions as it works now
- (see [testStreamOutput variable in test.go][testStreamOutput]).
-* `testing` package
+* In `testing` package
* Add `type TestResult` and `type TestState`.
* Add `func Test(f func(*T)) TestResult`
to be consistent with `func Benchmark(f func(*B)) BenchmarkResult`.
This is not required for JSON output.
* Add `Name`, `Output` and `Procs` fields to `BenchmarkResult`.
- * Add `type Result` for JSON output.
Type definitions and details below.
@@ -112,20 +97,14 @@
// existing fields
}
-// Result contains test/benchmark results and other test binary artifacts.
+// result is used for test binary JSON output format.
+// It is not added to the public API.
//
-// Every time a test/benchmark completes, the test binary emits one Result
+// Each time a test/benchmark completes, the test binary emits one result
// in unindented JSON format to stdout, surrounded by '\n'.
-type Result struct {
- Tests []TestResult `json:",omitempty"`
- Benchmarks []BenchmarkResult `json:",omitempty"`
-
- // Stdout is the test binary stdout that was not recognized by `go test`.
- // It is always empty when emitted by a test binary.
- Stdout string `json:",omitempty"`
- // Stderr is the test binary stderr caught by by `go test`.
- // It is always empty when emitted by a test binary.
- Stderr string `json:",omitempty"`
+type result struct {
+ Test *TestResult `json:",omitempty"`
+ Benchmark *BenchmarkResult `json:",omitempty"`
}
```
@@ -134,37 +113,31 @@
```json
{
- "Tests": [
- {
- "Name": "TestFoo",
- "State": "PASS",
- "T": 1000000
- }
- ]
+ "Test": {
+ "Name": "TestFoo",
+ "State": "PASS",
+ "T": 1000000
+ }
}
Random string written directly to os.Stdout.
{
- "Tests": [
- {
- "Name": "TestBar",
- "State": "PASS",
- "T": 1000000,
- "Output": "some test output\n"
- }
- ]
+ "Test": {
+ "Name": "TestBar",
+ "State": "PASS",
+ "T": 1000000,
+ "Output": "some test output\n"
+ }
}
{
- "Benchmarks": [
- {
- "Name": "BenchmarkBar",
- "State": "PASS",
- "T": 1000000,
- "N": 1000,
- "Bytes": 0,
- "MemAllocs": 0,
- "MemBytes": 0
- }
- ]
+ "Benchmark": {
+ "Name": "BenchmarkBar",
+ "State": "PASS",
+ "T": 1000000,
+ "N": 1000,
+ "Bytes": 0,
+ "MemAllocs": 0,
+ "MemBytes": 0
+ }
}
```
@@ -173,157 +146,61 @@
`go test` JSON output format:
```go
-// TestResult contains output of all test binaries.
+// TestResult contains one output line of a test binary.
type TestResult struct {
- About *struct {
- Version string `json: ",omitempty"`
- OS string `json: ",omitempty"`
- Arch string `json: ",omitempty"`
- Hostname string `json: ",omitempty"`
- StartTime *time.Time `json: ",omitempty"`
- EndTime *time.Time `json: ",omitempty"`
- } `json: ",omitempty"`
+ Package string // package of the test binary.
- // Results is a mapping packageName -> results of the package test binary.
- //
- // Results[P].Stdout contains unrecognized stdout of the P test binary.
- // Results[P].Stderr contains all stderr of the P test binary.
- //
- // In non-streaming mode, it is the synthesized output of all test binaries.
- // Multiple testing.Result structs generated by the same test binary are
- // combined.
- Results map[string]testing.Result `json: ",omitempty"`
+ Test *TestResult `json:",omitempty"`
+ Benchmark *BenchmarkResult `json:",omitempty"`
+ Stdout string `json:",omitempty"` // Unrecognized stdout of the test binary.
+ Stderr string `json:",omitempty"` // Stderr output line of the test binary.
}
```
-Example output: `go test -json`
+Example `go test -json` output
+
```json
{
- "About": {
- "Version": "go1.5",
- "OS": "darwin",
- "Arch": "amd64",
- "Hostname": "nodir-macbookpro",
- "Start": "2015-10-06T15:33:03.925363433-07:00",
- "End": "2015-10-06T15:33:04.925363433-07:00"
- },
- "Results": {
- "example.com/foobar": {
- "Tests": [
- {
- "Name": "TestFoo",
- "Pass": true,
- "T": 1000000
- },
- {
- "Name": "TestBar",
- "Pass": true,
- "T": 1000000,
- "Output": "some test output\n"
- }
- ],
- "Benchmarks": [
- {
- "Name": "BenchmarkBar",
- "Procs": 8,
- "Pass": true,
- "T": 1000000,
- "N": 1000,
- "Bytes": 0,
- "MemAllocs": 0,
- "MemBytes": 0
- }
- ],
- "Stdout": "Random string written directly to os.Stdout.\n"
- }
- }
-}
-```
-
-Example output: `go test -json -stream`
-
-```json
-{
- "About": {
- "Version": "go1.5",
- "OS": "darwin",
- "Arch": "amd64",
- "Hostname": "nodir-macbookpro",
- "Start": "2015-10-06T15:33:03.925363433-07:00",
- }
+ "Package": "example.com/foobar",
+ "Test": {
+ "Name": "TestFoo",
+ "State": "PASS",
+ "T": 1000000
+ }
}
{
- "Results": {
- "example.com/foobar": {
- "Tests": [
- {
- "Name": "TestFoo",
- "Pass": true,
- "T": 1000000
- }
- ]
- }
- }
+ "Package": "example.com/foobar",
+ "Stdout": "Random string written directly to os.Stdout.\n"
}
{
- "Results": {
- "example.com/foobar": {
- "Stdout": "Random string written directly to os.Stdout.\n"
- }
- }
+ "Package": "example.com/foobar",
+ "Test": {
+ "Name": "TestBar",
+ "State": "PASS",
+ "T": 1000000,
+ "Output": "some test output\n"
+ }
}
{
- "Results": {
- "example.com/foobar": {
- "Package": "example.com/foobar",
- "Tests": [
- {
- "Name": "TestBar",
- "Pass": true,
- "T": 1000000,
- "Output": "some test output\n"
- }
- ]
- }
- }
-}
-{
- "Results": {
- "example.com/foobar": {
- "Package": "example.com/foobar",
- "Benchmarks": [
- {
- "Name": "BenchmarkBar",
- "Procs": 8,
- "T": 1000000,
- "N": 1000,
- "Bytes": 0,
- "MemAllocs": 0,
- "MemBytes": 0
- }
- ]
- }
- }
-}
-{
- "About": {
- "End": "2015-10-06T15:33:04.925363433-07:00"
- }
+ "Package": "example.com/foobar",
+ "Benchmark": {
+ "Name": "BenchmarkBar",
+ "Procs": 8,
+ "T": 1000000,
+ "N": 1000,
+ "Bytes": 0,
+ "MemAllocs": 0,
+ "MemBytes": 0
+ }
}
```
## Rationale
-* Assuming streaming is important, `-stream` flag has to be explicit because
- a third party tool may prefer `go test -json` to always return one JSON
- object, so the tool doesn't have to combine multiple objects itself.
-
- There was no need for explicit `-stream` flag before because it didn't
- matter for humans.
-* Test binary surrounds `testing.Result` JSON with `\n` to handle situation
+* A test binary surrounds `testing.result` JSON with `\n` to handle situation
when a string without a trailing `\n` is printed directly to `os.Stdout`.
-* Test binary always streams results so we don't loose them if the binary
+* A test binary always streams results so we don't loose them if the binary
panics.
Alternatives:
@@ -331,13 +208,11 @@
* Add `-format` and `-benchformat` flags proposed in
https://github.com/golang/go/issues/12826. This is simpler to implement
by moving the burden of output parsing to third party programs.
-* Drop streaming support in `go test -json`, remove `-stream` flag.
- Always print one JSON object in `go test -json` stdout.
Trade offs:
* I propose to make `-json` mutually exclusive with `-n` and `-x` flags.
- This is a trade off for `TestResult` type simplicity in `cmd/go/test.go`.
+ This is a trade off for `go test` output format simplicity.
Supporting `-json` with `-n`/`-x` flags would require a new field in
`TestResult` that would contain commands that have been run. Note that
we cannot print commands to stdout because stdout must be valid JSON.
@@ -355,21 +230,9 @@
I propose not to add `BuildOutput` now because `-n` affects `go test` too.
For example, `go test -n` prints a command to run the test binary, which
should not be a part of `BuildOutput` (because it is not build).
-* With `Stdout` and `Stderr` fields separated in `testing.Result`, it is
- impossible to determine the order of the test binary output.
- This is a trade off for `testing.Result` simplicity.
-
- Combining `Stdout` and `Stderr` would make it impossible to distinguish
- stdout and stderr.
-
- If a third party tool needs to know the stdout/stderr order, it can leverage
- the `-exec` flag.
-
-
-Disadvantages:
-
-* Adding seemingly unnecessary types to the public `testing` package, that are
- possibly useless for users.
+* `go test` always streams and does not aggregate results into one JSON
+ object.
+ This is a trade off for `go test -json` output format simplicity.
## Compatibility
@@ -386,21 +249,15 @@
1. Add `type TestResult`, `type TestStatus` and
`func Test(f func(*T)) TestResult` to package `testing`.
Modify `testing.tRunner` to create `TestResult`.
-1. Add `-test.json` flag and `type Result` to package `testing`.
+1. Add `-test.json` flag to the `testing` package.
Modify `testing.(*T).report` and `testing.RunBenchmarks` functions
to print JSON if `-test.json` is specified.
- If `-test.verbose` was passed, print verbose messages to stderr.
-1. Add `-json` and `-stream` flags to `go test`.
- If `-json` is passed, pass `-test.json` to test binaries.
+ If `-test.verbose` is specified, print verbose messages to stderr.
+1. Add `-json` flag to `go test`.
+ If specified, pass `-test.json` to test binaries.
- For each line in a test binary output, try to parse it as `testing.Result`
- in JSON format.
- Accumulate one `testing.Result` per package.
-
- If `-stream` was specified, override `testStreamOutput` variable value.
- Print JSON output on each test binary output line.
- If not streaming, print one JSON with all artifacts on completion of all
- test binaries.
+ For each line in a test binary output, try to parse it as
+ `"cmd/go".TestResult` in JSON format, and print a JSON object.
The goal is to get agreement on this proposal and to complete the work
before the 1.6 freeze date.