cmd/testwrapper: emit logs of failed tests on timeout

It would just fail the entire pkg, but would not print any
logs. It was already tracking all the logs, so have it emit
them when the pkg fails/times out.

Updates #9231

Signed-off-by: Maisem Ali <maisem@tailscale.com>
pull/9020/head
Maisem Ali 1 year ago committed by Maisem Ali
parent 52a19b5970
commit 0f397baf77

@ -29,7 +29,8 @@ import (
const maxAttempts = 3 const maxAttempts = 3
type testAttempt struct { type testAttempt struct {
name testName pkg string // "tailscale.com/types/key"
testName string // "TestFoo"
outcome string // "pass", "fail", "skip" outcome string // "pass", "fail", "skip"
logs bytes.Buffer logs bytes.Buffer
isMarkedFlaky bool // set if the test is marked as flaky isMarkedFlaky bool // set if the test is marked as flaky
@ -37,11 +38,6 @@ type testAttempt struct {
pkgFinished bool pkgFinished bool
} }
type testName struct {
pkg string // "tailscale.com/types/key"
name string // "TestFoo"
}
type packageTests struct { type packageTests struct {
// pattern is the package pattern to run. // pattern is the package pattern to run.
// Must be a single pattern, not a list of patterns. // Must be a single pattern, not a list of patterns.
@ -98,7 +94,7 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
}() }()
jd := json.NewDecoder(r) jd := json.NewDecoder(r)
resultMap := make(map[testName]*testAttempt) resultMap := make(map[string]map[string]*testAttempt) // pkg -> test -> testAttempt
for { for {
var goOutput goTestOutput var goOutput goTestOutput
if err := jd.Decode(&goOutput); err != nil { if err := jd.Decode(&goOutput); err != nil {
@ -116,27 +112,34 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
} }
panic(err) panic(err)
} }
pkg := goOutput.Package
pkgTests := resultMap[pkg]
if goOutput.Test == "" { if goOutput.Test == "" {
switch goOutput.Action { switch goOutput.Action {
case "fail", "pass", "skip": case "fail", "pass", "skip":
for _, test := range pkgTests {
if test.outcome == "" {
test.outcome = "fail"
ch <- test
}
}
ch <- &testAttempt{ ch <- &testAttempt{
name: testName{
pkg: goOutput.Package, pkg: goOutput.Package,
},
outcome: goOutput.Action, outcome: goOutput.Action,
pkgFinished: true, pkgFinished: true,
} }
} }
continue continue
} }
name := testName{ if pkgTests == nil {
pkg: goOutput.Package, pkgTests = make(map[string]*testAttempt)
name: goOutput.Test, resultMap[pkg] = pkgTests
} }
testName := goOutput.Test
if test, _, isSubtest := strings.Cut(goOutput.Test, "/"); isSubtest { if test, _, isSubtest := strings.Cut(goOutput.Test, "/"); isSubtest {
name.name = test testName = test
if goOutput.Action == "output" { if goOutput.Action == "output" {
resultMap[name].logs.WriteString(goOutput.Output) resultMap[pkg][testName].logs.WriteString(goOutput.Output)
} }
continue continue
} }
@ -144,17 +147,18 @@ func runTests(ctx context.Context, attempt int, pt *packageTests, otherArgs []st
case "start": case "start":
// ignore // ignore
case "run": case "run":
resultMap[name] = &testAttempt{ pkgTests[testName] = &testAttempt{
name: name, pkg: pkg,
testName: testName,
} }
case "skip", "pass", "fail": case "skip", "pass", "fail":
resultMap[name].outcome = goOutput.Action pkgTests[testName].outcome = goOutput.Action
ch <- resultMap[name] ch <- pkgTests[testName]
case "output": case "output":
if strings.TrimSpace(goOutput.Output) == flakytest.FlakyTestLogMessage { if strings.TrimSpace(goOutput.Output) == flakytest.FlakyTestLogMessage {
resultMap[name].isMarkedFlaky = true pkgTests[testName].isMarkedFlaky = true
} else { } else {
resultMap[name].logs.WriteString(goOutput.Output) pkgTests[testName].logs.WriteString(goOutput.Output)
} }
} }
} }
@ -247,13 +251,13 @@ func main() {
go runTests(ctx, thisRun.attempt, pt, otherArgs, ch) go runTests(ctx, thisRun.attempt, pt, otherArgs, ch)
for tr := range ch { for tr := range ch {
if tr.pkgFinished { if tr.pkgFinished {
if tr.outcome == "fail" && len(toRetry[tr.name.pkg]) == 0 { if tr.outcome == "fail" && len(toRetry[tr.pkg]) == 0 {
// If a package fails and we don't have any tests to // If a package fails and we don't have any tests to
// retry, then we should fail. This typically happens // retry, then we should fail. This typically happens
// when a package times out. // when a package times out.
failed = true failed = true
} }
printPkgOutcome(tr.name.pkg, tr.outcome, thisRun.attempt) printPkgOutcome(tr.pkg, tr.outcome, thisRun.attempt)
continue continue
} }
if *v || tr.outcome == "fail" { if *v || tr.outcome == "fail" {
@ -263,7 +267,7 @@ func main() {
continue continue
} }
if tr.isMarkedFlaky { if tr.isMarkedFlaky {
toRetry[tr.name.pkg] = append(toRetry[tr.name.pkg], tr.name.name) toRetry[tr.pkg] = append(toRetry[tr.pkg], tr.testName)
} else { } else {
failed = true failed = true
} }

Loading…
Cancel
Save