Skip to content

Commit 7f68870

Browse files
authored
DX177 - Fix flakeguard parser panic (#1683)
* Improve panic attribution logic in Flakeguard test runner * Show "Failed Tests On The First Run" in console
1 parent af19bb9 commit 7f68870

File tree

4 files changed

+180
-113
lines changed

4 files changed

+180
-113
lines changed

tools/flakeguard/cmd/run.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ var RunTestsCmd = &cobra.Command{
184184
flushSummaryAndExit(0)
185185
}
186186

187+
fmt.Fprint(&summaryBuffer, "\nFailed Tests On The First Run:\n\n")
188+
reports.PrintTestResultsTable(&summaryBuffer, failedTests, false, false, true, false)
189+
fmt.Fprintln(&summaryBuffer)
190+
187191
rerunResults, rerunJsonOutputPaths, err := testRunner.RerunFailedTests(failedTests, rerunFailedCount)
188192
if err != nil {
189193
log.Fatal().Err(err).Msg("Error rerunning failed tests")
@@ -203,8 +207,8 @@ var RunTestsCmd = &cobra.Command{
203207
flushSummaryAndExit(ErrorExitCode)
204208
}
205209

206-
fmt.Fprint(&summaryBuffer, "\nFailed Tests That Were Rerun:\n\n")
207-
reports.PrintTestResultsTable(&summaryBuffer, rerunResults, false, false, true)
210+
fmt.Fprint(&summaryBuffer, "\nFailed Tests After Rerun:\n\n")
211+
reports.PrintTestResultsTable(&summaryBuffer, rerunResults, false, false, true, true)
208212
fmt.Fprintln(&summaryBuffer)
209213

210214
// Save the rerun test report to file

tools/flakeguard/reports/presentation.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,18 @@ func generateTestResultsTable(
7878
func generateShortTestResultsTable(
7979
results []TestResult,
8080
markdown bool,
81+
showEverPassed bool,
8182
filter func(TestResult) bool,
8283
) [][]string {
8384
p := message.NewPrinter(language.English)
8485

85-
headers := []string{"Name", "Ever Passed", "Runs", "Successes", "Code Owners", "Path"}
86+
// Build headers dynamically
87+
var headers []string
88+
headers = append(headers, "Name")
89+
if showEverPassed {
90+
headers = append(headers, "Ever Passed")
91+
}
92+
headers = append(headers, "Runs", "Successes", "Code Owners", "Path")
8693

8794
// Optionally format the headers for Markdown
8895
if markdown {
@@ -112,14 +119,17 @@ func generateShortTestResultsTable(
112119
owners = strings.Join(r.CodeOwners, ", ")
113120
}
114121

115-
row := []string{
116-
r.TestName, // "Name"
117-
passed, // "Passed"
118-
p.Sprintf("%d", r.Runs), // "Runs"
119-
p.Sprintf("%d", r.Successes), // "Passes"
120-
owners, // "Code Owners"
121-
r.TestPath, // "Path"
122+
// Build row dynamically
123+
row := []string{r.TestName}
124+
if showEverPassed {
125+
row = append(row, passed)
122126
}
127+
row = append(row,
128+
p.Sprintf("%d", r.Runs),
129+
p.Sprintf("%d", r.Successes),
130+
owners,
131+
r.TestPath,
132+
)
123133

124134
table = append(table, row)
125135
}
@@ -143,13 +153,14 @@ func PrintTestResultsTable(
143153
results []TestResult,
144154
markdown bool,
145155
collapsible bool,
146-
shortTable bool) {
156+
shortTable bool,
157+
showEverPassed bool) {
147158
filter := func(result TestResult) bool {
148159
return true // Include all tests
149160
}
150161
var table [][]string
151162
if shortTable {
152-
table = generateShortTestResultsTable(results, markdown, filter)
163+
table = generateShortTestResultsTable(results, markdown, showEverPassed, filter)
153164
} else {
154165
table = generateTestResultsTable(results, markdown, filter)
155166
}

tools/flakeguard/runner/runner.go

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,11 @@ func (r *Runner) parseTestResults(jsonOutputPaths []string, runPrefix string, ru
381381

382382
if (panicDetectionMode || raceDetectionMode) && entryLine.Action == "fail" { // End of panic or race output
383383
if panicDetectionMode {
384-
panicTest, timeout, err := attributePanicToTest(entryLine.Package, detectedEntries)
384+
var outputs []string
385+
for _, entry := range detectedEntries {
386+
outputs = append(outputs, entry.Output)
387+
}
388+
panicTest, timeout, err := attributePanicToTest(outputs)
385389
if err != nil {
386390
return nil, err
387391
}
@@ -614,23 +618,23 @@ func (r *Runner) transformTestOutputFiles(filePaths []string) ([]string, error)
614618
}
615619

616620
// attributePanicToTest properly attributes panics to the test that caused them.
617-
func attributePanicToTest(panicPackage string, panicEntries []entry) (test string, timeout bool, err error) {
618-
regexSanitizePanicPackage := filepath.Base(panicPackage)
619-
panicAttributionRe := regexp.MustCompile(fmt.Sprintf(`%s\.(Test[^\.\(]+)`, regexSanitizePanicPackage))
620-
timeoutAttributionRe := regexp.MustCompile(`(Test.*?)\W+\(.*\)`)
621-
entriesOutputs := []string{}
622-
for _, entry := range panicEntries {
623-
entriesOutputs = append(entriesOutputs, entry.Output)
624-
if matches := panicAttributionRe.FindStringSubmatch(entry.Output); len(matches) > 1 {
621+
func attributePanicToTest(outputs []string) (test string, timeout bool, err error) {
622+
// Regex to extract a valid test function name.
623+
testNameRe := regexp.MustCompile(`(?:.*\.)?(Test[A-Z]\w+)(?:\.[^(]+)?\s*\(`)
624+
// Regex to detect timeout messages (accepting "timeout", "timedout", or "timed out", case-insensitive).
625+
timeoutRe := regexp.MustCompile(`(?i)(timeout|timedout|timed\s*out)`)
626+
for _, o := range outputs {
627+
outputs = append(outputs, o)
628+
if matches := testNameRe.FindStringSubmatch(o); len(matches) > 1 {
625629
testName := strings.TrimSpace(matches[1])
630+
if timeoutRe.MatchString(o) {
631+
return testName, true, nil
632+
}
626633
return testName, false, nil
627634
}
628-
if matches := timeoutAttributionRe.FindStringSubmatch(entry.Output); len(matches) > 1 {
629-
testName := strings.TrimSpace(matches[1])
630-
return testName, true, nil
631-
}
632635
}
633-
return "", false, fmt.Errorf("failed to attribute panic to test, using regex %s on these strings:\n%s", panicAttributionRe.String(), strings.Join(entriesOutputs, ""))
636+
return "", false, fmt.Errorf("failed to attribute panic to test, using regex %s on these strings:\n%s",
637+
testNameRe.String(), strings.Join(outputs, ""))
634638
}
635639

636640
// attributeRaceToTest properly attributes races to the test that caused them.

0 commit comments

Comments
 (0)