Skip to content

Commit

Permalink
Better docs for testjson package
Browse files Browse the repository at this point in the history
Also make ScanTestOutput easier to use.
  • Loading branch information
dnephin committed May 17, 2020
1 parent 0a6ff94 commit 9b2aef1
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 19 deletions.
6 changes: 1 addition & 5 deletions cmd/tool/slowest/slowest.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package slowest

import (
"bytes"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -104,10 +103,7 @@ func run(opts *options) error {
}
}()

exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
Stdout: in,
Stderr: bytes.NewReader(nil),
})
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{Stdout: in})
if err != nil {
return fmt.Errorf("failed to scan testjson: %v", err)
}
Expand Down
8 changes: 8 additions & 0 deletions do
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,12 @@ _docker-build-dev() {
cat "$idfile"
}

help[godoc]="Run godoc locally to preview package documentation."
godoc() {
local url; url="http://localhost:6060/pkg/$(go list)/"
command -v xdg-open && xdg-open "$url" &
command -v open && open "$url" &
command godoc -http=:6060
}

_plsdo_run "$@"
5 changes: 3 additions & 2 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ type eventHandler struct {
}

func (h *eventHandler) Err(text string) error {
_, err := h.err.Write([]byte(text + "\n"))
return err
_, _ = h.err.Write([]byte(text + "\n"))
// always return nil, no need to stop scanning if the stderr write fails
return nil
}

func (h *eventHandler) Event(event testjson.TestEvent, execution *testjson.Execution) error {
Expand Down
13 changes: 13 additions & 0 deletions testjson/doc.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
/*Package testjson scans test2json output and builds up a summary of the events.
Events are passed to a formatter for output.
Example
This example reads the test2json output from os.Stdin. It builds an
Execution from the output, then it prints the number of tests run.
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{Stdout: os.Stdin})
if err != nil {
return fmt.Errorf("failed to scan testjson: %v", err)
}
fmt.Println("Ran %d tests", exec.Total())
*/
package testjson // import "gotest.tools/gotestsum/testjson"
40 changes: 29 additions & 11 deletions testjson/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,6 @@ func (e *Execution) addError(err string) {
if strings.HasPrefix(err, "# ") {
return
}
// TODO: may need locking, or use a channel
e.errors = append(e.errors, err)
}

Expand All @@ -380,37 +379,51 @@ func (e *Execution) end() {
}
}

// NewExecution returns a new Execution and records the current time as the
// newExecution returns a new Execution and records the current time as the
// time the test execution started.
func NewExecution() *Execution {
func newExecution() *Execution {
return &Execution{
started: clock.Now(),
packages: make(map[string]*Package),
}
}

// ScanConfig used by ScanTestOutput
// ScanConfig used by ScanTestOutput.
type ScanConfig struct {
Stdout io.Reader
Stderr io.Reader
// Stdout is a reader that yields the test2json output stream.
Stdout io.Reader
// Stderr is a reader that yields stderr from the 'go test' process. Often
// it contains build errors, or panics. Stderr may be nil.
Stderr io.Reader
// Handler is a set of callbacks for receiving TestEvents and stderr text.
Handler EventHandler
}

// EventHandler is called by ScanTestOutput for each event and write to stderr.
type EventHandler interface {
// Event is called for every TestEvent, with the current value of Execution.
// It may return an error to stop scanning.
Event(event TestEvent, execution *Execution) error
// Err is called for every line from the Stderr reader and may return an
// error to stop scanning.
Err(text string) error
}

// ScanTestOutput reads lines from stdout and stderr, creates an Execution,
// calls the Handler for each event, and returns the Execution.
// ScanTestOutput reads lines from config.Stdout and config.Stderr, creates an
// Execution, calls the Handler for each event, and returns the Execution.
//
// If config.Handler is nil, a default no-op handler will be used.
func ScanTestOutput(config ScanConfig) (*Execution, error) {
if config.Handler == nil {
config.Handler = noopHandler{}
}
execution := NewExecution()
if config.Stderr == nil {
config.Stderr = new(bytes.Reader)
}
if config.Stdout == nil {
return nil, fmt.Errorf("stdout reader must be non-nil")
}
execution := newExecution()
var group errgroup.Group
group.Go(func() error {
return readStdout(config, execution)
Expand Down Expand Up @@ -449,13 +462,18 @@ func readStderr(config ScanConfig, execution *Execution) error {
scanner := bufio.NewScanner(config.Stderr)
for scanner.Scan() {
line := scanner.Text()
config.Handler.Err(line) // nolint: errcheck
if err := config.Handler.Err(line); err != nil {
return fmt.Errorf("failed to handle stderr: %v", err)
}
if isGoModuleOutput(line) {
continue
}
execution.addError(line)
}
return errors.Wrap(scanner.Err(), "failed to scan test stderr")
if err := scanner.Err(); err != nil {
return fmt.Errorf("failed to scan stderr: %v", err)
}
return nil
}

func isGoModuleOutput(scannerText string) bool {
Expand Down
12 changes: 11 additions & 1 deletion testjson/execution_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package testjson

import (
"bytes"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
)

func TestPackage_Elapsed(t *testing.T) {
Expand All @@ -25,7 +27,7 @@ func TestPackage_Elapsed(t *testing.T) {
}

func TestExecution_Add_PackageCoverage(t *testing.T) {
exec := NewExecution()
exec := newExecution()
exec.add(TestEvent{
Package: "mytestpkg",
Action: ActionOutput,
Expand All @@ -46,3 +48,11 @@ func TestExecution_Add_PackageCoverage(t *testing.T) {
}

var cmpPackage = cmp.AllowUnexported(Package{})

func TestScanTestOutput_MinimalConfig(t *testing.T) {
in := bytes.NewReader(golden.Get(t, "go-test-json.out"))
exec, err := ScanTestOutput(ScanConfig{Stdout: in})
assert.NilError(t, err)
// a weak check to show that all the stdout was scanned
assert.Equal(t, exec.Total(), 46)
}

0 comments on commit 9b2aef1

Please sign in to comment.