cmd/testwrapper: stream output results

Previously it would wait for all tests to run before printing anything,
instead stream the results over a channel so that they can be emitted
immediately.

Updates #8493

Signed-off-by: Maisem Ali <maisem@tailscale.com>
pull/8492/head^2
Maisem Ali 11 months ago committed by Maisem Ali
parent 2e4e7d6b9d
commit 1ca5dcce15

@ -23,7 +23,6 @@ import (
"time"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"tailscale.com/cmd/testwrapper/flakytest"
)
@ -34,6 +33,8 @@ type testAttempt struct {
outcome string // "pass", "fail", "skip"
logs bytes.Buffer
isMarkedFlaky bool // set if the test is marked as flaky
pkgFinished bool
}
type testName struct {
@ -60,7 +61,12 @@ type goTestOutput struct {
var debug = os.Getenv("TS_TESTWRAPPER_DEBUG") != ""
func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []string) []*testAttempt {
// runTests runs the tests in pt and sends the results on ch. It sends a
// testAttempt for each test and a final testAttempt per pkg with pkgFinished
// set to true.
// It calls close(ch) when it's done.
func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []string, ch chan<- *testAttempt) {
defer close(ch)
args := []string{"test", "-json", pt.pattern}
args = append(args, otherArgs...)
if len(pt.tests) > 0 {
@ -92,7 +98,6 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
jd := json.NewDecoder(r)
resultMap := make(map[testName]*testAttempt)
var out []*testAttempt
for {
var goOutput goTestOutput
if err := jd.Decode(&goOutput); err != nil {
@ -102,6 +107,16 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
panic(err)
}
if goOutput.Test == "" {
switch goOutput.Action {
case "fail", "pass", "skip":
ch <- &testAttempt{
name: testName{
pkg: goOutput.Package,
},
outcome: goOutput.Action,
pkgFinished: true,
}
}
continue
}
name := testName{
@ -124,7 +139,7 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
}
case "skip", "pass", "fail":
resultMap[name].outcome = goOutput.Action
out = append(out, resultMap[name])
ch <- resultMap[name]
case "output":
if strings.TrimSpace(goOutput.Output) == flakytest.FlakyTestLogMessage {
resultMap[name].isMarkedFlaky = true
@ -134,7 +149,6 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
}
}
<-done
return out
}
func main() {
@ -186,13 +200,22 @@ func main() {
attempt: 1,
},
}
printPkgStatus := func(pkgName string, failed bool) {
if failed {
fmt.Println("FAIL\t", pkgName)
} else {
fmt.Println("ok\t", pkgName)
printPkgOutcome := func(pkg, outcome string, attempt int) {
if outcome == "skip" {
fmt.Printf("?\t%s [skipped/no tests] \n", pkg)
return
}
if outcome == "pass" {
outcome = "ok"
}
if outcome == "fail" {
outcome = "FAIL"
}
if attempt > 1 {
fmt.Printf("%s\t%s [attempt=%d]\n", outcome, pkg, attempt)
return
}
fmt.Printf("%s\t%s\n", outcome, pkg)
}
for len(toRun) > 0 {
@ -210,25 +233,12 @@ func main() {
failed := false
toRetry := make(map[string][]string) // pkg -> tests to retry
for _, pt := range thisRun.tests {
output := runTests(ctx, thisRun.attempt, pt, otherArgs)
slices.SortFunc(output, func(i, j *testAttempt) bool {
if c := strings.Compare(i.name.pkg, j.name.pkg); c < 0 {
return true
} else if c > 0 {
return false
}
return strings.Compare(i.name.name, j.name.name) <= 0
})
lastPkg := ""
lastPkgFailed := false
for _, tr := range output {
if lastPkg == "" {
lastPkg = tr.name.pkg
} else if lastPkg != tr.name.pkg {
printPkgStatus(lastPkg, lastPkgFailed)
lastPkg = tr.name.pkg
lastPkgFailed = false
ch := make(chan *testAttempt)
go runTests(ctx, thisRun.attempt, pt, otherArgs, ch)
for tr := range ch {
if tr.pkgFinished {
printPkgOutcome(tr.name.pkg, tr.outcome, thisRun.attempt)
continue
}
if *v || tr.outcome == "fail" {
io.Copy(os.Stdout, &tr.logs)
@ -236,14 +246,12 @@ func main() {
if tr.outcome != "fail" {
continue
}
lastPkgFailed = true
if tr.isMarkedFlaky {
toRetry[tr.name.pkg] = append(toRetry[tr.name.pkg], tr.name.name)
} else {
failed = true
}
}
printPkgStatus(lastPkg, lastPkgFailed)
}
if failed {
fmt.Println("\n\nNot retrying flaky tests because non-flaky tests failed.")

Loading…
Cancel
Save