Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
Merge pull request #9 from tschottdorf/show-pkg
Browse files Browse the repository at this point in the history
Rewrite to take test2json input
  • Loading branch information
tbg authored Sep 20, 2018
2 parents 68e190a + 4e48730 commit b1ed8e6
Show file tree
Hide file tree
Showing 6 changed files with 2,331 additions and 27 deletions.
119 changes: 118 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ package main

import (
"bufio"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"regexp"
"sort"
"strings"
"time"
)
Expand All @@ -24,13 +28,15 @@ type Test struct {
Status string
Race bool
Suite bool
Package string
}

var (
input = os.Stdin
output = os.Stdout

additionalTestName = ""
useJSON = false

run = regexp.MustCompile("^=== RUN\\s+([a-zA-Z_]\\S*)")
end = regexp.MustCompile("^(\\s*)--- (PASS|SKIP|FAIL):\\s+([a-zA-Z_]\\S*) \\((-?[\\.\\ds]+)\\)")
Expand All @@ -39,6 +45,7 @@ var (
)

func init() {
flag.BoolVar(&useJSON, "json", false, "Parse input from JSON (as emitted from go tool test2json)")
flag.StringVar(&additionalTestName, "name", "", "Add prefix to test name")
}

Expand Down Expand Up @@ -78,6 +85,14 @@ func outputTest(w io.Writer, test *Test) {
now, testName, escapeLines(test.Details))
case "PASS":
// ignore
case "UNKNOWN":
// This can happen when a data race is detected, in which case the test binary
// exits apruptly assuming GORACE="halt_on_error=1" is specified.
// CockroachDB CI does this at the time of writing:
// https://github.com/cockroachdb/cockroach/pull/14590
fmt.Fprintf(w, "##teamcity[testIgnored timestamp='%s' name='%s' message='"+
"Test framework exited prematurely. Likely another test panicked or encountered a data race']\n",
now, testName)
default:
fmt.Fprintf(w, "##teamcity[testFailed timestamp='%s' name='%s' message='Test ended in panic.' details='%s']\n",
now, testName, escapeLines(test.Details))
Expand Down Expand Up @@ -195,5 +210,107 @@ func main() {

reader := bufio.NewReader(input)

processReader(reader, output)
if useJSON {
processJSON(reader, output)
} else {
processReader(reader, output)
}
}

// TestEvent is a message as emitted by `go tool test2json`.
type TestEvent struct {
Time time.Time // encodes as an RFC3339-format string
Action string
Package string
Test string
Elapsed float64 // seconds
Output string
}

func processJSON(r *bufio.Reader, w io.Writer) {
openTests := map[string]*Test{}
output := func(name string) {
test := openTests[name]
delete(openTests, name)
outputTest(w, test)
}

defer func() {
sorted := make([]*Test, 0, len(openTests))
for _, test := range openTests {
sorted = append(sorted, test)
}
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].Name < sorted[j].Name
})
for _, test := range sorted {
test.Output += "(test not terminated explicitly)\n"
test.Status = "UNKNOWN"
outputTest(w, test)
}
}()

dec := json.NewDecoder(r)
dec.DisallowUnknownFields()

for dec.More() {
var event TestEvent
if err := dec.Decode(&event); err != nil {
buffered, err := ioutil.ReadAll(dec.Buffered())
if err != nil {
log.Fatal(err)
}
fmt.Fprint(w, string(buffered))
line, err := r.ReadString('\n')
dec = json.NewDecoder(r)
if err != nil {
if err == io.EOF {
continue
}
log.Fatal(err)
}
fmt.Fprint(w, string(line))
continue
}

if openTests[event.Test] == nil {
if event.Test == "" {
// We're about to start a new test, but this line doesn't correspond to one.
// It's probably a package-level info (coverage etc).
fmt.Fprint(w, event.Output)
continue
}
openTests[event.Test] = &Test{}
}

test := openTests[event.Test]
if test.Name == "" {
test.Name = event.Test
}
test.Output += event.Output
test.Race = test.Race || race.MatchString(event.Output)
test.Duration += time.Duration(event.Elapsed * 1E9)
if test.Package == "" {
test.Package = event.Package
}

switch event.Action {
case "run":
case "pause":
case "cont":
case "bench":
case "output":
case "skip":
test.Status = "SKIP"
output(event.Test)
case "pass":
test.Status = "PASS"
output(event.Test)
case "fail":
test.Status = "FAIL"
output(event.Test)
default:
log.Fatalf("unknown event type: %+v", event)
}
}
}
72 changes: 46 additions & 26 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,72 @@ package main
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)

var (
inDir = "testdata/input/"
outDir = "testdata/output/"
inDir = "testdata/input"
outDir = "testdata/output"

timestampRegexp = regexp.MustCompile(`timestamp='.*?'`)
timestampReplacement = `timestamp='2017-01-02T04:05:06.789'`
)

func TestProcessReader(t *testing.T) {
files, err := ioutil.ReadDir(inDir)
if err != nil {
t.Error(err)
}
for _, file := range files {
t.Run(file.Name(), func(t *testing.T) {
inpath := inDir + file.Name()
f, err := os.Open(inpath)
if err != nil {
t.Error(err)
for _, json := range []bool{false, true} {
t.Run(fmt.Sprintf("json=%t", json), func(t *testing.T) {
sep := ""
if json {
sep = ".json"
}
in := bufio.NewReader(f)

out := &bytes.Buffer{}
processReader(in, out)
actual := out.String()
actual = timestampRegexp.ReplaceAllString(actual, timestampReplacement)

outpath := outDir + file.Name()
t.Logf("input: %s", inpath)
t.Logf("output: %s", outpath)
expectedBytes, err := ioutil.ReadFile(outpath)
files, err := ioutil.ReadDir(inDir + sep)
if err != nil {
t.Error(err)
}
expected := string(expectedBytes)
expected = timestampRegexp.ReplaceAllString(expected, timestampReplacement)
for _, file := range files {
t.Run(file.Name(), func(t *testing.T) {
inpath := filepath.Join(inDir+sep, file.Name())
f, err := os.Open(inpath)
if err != nil {
t.Error(err)
}
in := bufio.NewReader(f)

out := &bytes.Buffer{}
if json {
processJSON(in, out)
} else {
processReader(in, out)
}
actual := out.String()
actual = timestampRegexp.ReplaceAllString(actual, timestampReplacement)

outpath := filepath.Join(outDir+sep, file.Name())
t.Logf("input: %s", inpath)
t.Logf("output: %s", outpath)
expectedBytes, err := ioutil.ReadFile(outpath)
if err != nil {
t.Error(err)
}
expected := string(expectedBytes)
expected = timestampRegexp.ReplaceAllString(expected, timestampReplacement)

const rewriteOutput = true
if rewriteOutput {
_ = ioutil.WriteFile(outpath, []byte(actual), 0644)
}

if strings.Compare(expected, actual) != 0 {
t.Errorf("expected:\n\n%s\nbut got:\n\n%s\n", expected, actual)
if strings.Compare(expected, actual) != 0 {
t.Errorf("expected:\n\n%s\nbut got:\n\n%s\n", expected, actual)
}
})
}
})
}
Expand Down
Loading

0 comments on commit b1ed8e6

Please sign in to comment.