Skip to content

Commit 126cfaf

Browse files
authored
TT-1995 Integrate go test transformer into Flakeguard (#1667)
* Integrate go test transformer into Flakeguard * Fix * Update test
1 parent 6af8024 commit 126cfaf

File tree

5 files changed

+100
-73
lines changed

5 files changed

+100
-73
lines changed

tools/flakeguard/cmd/run.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var RunTestsCmd = &cobra.Command{
4141
useShuffle, _ := cmd.Flags().GetBool("shuffle")
4242
shuffleSeed, _ := cmd.Flags().GetString("shuffle-seed")
4343
omitOutputsOnSuccess, _ := cmd.Flags().GetBool("omit-test-outputs-on-success")
44+
ignoreParentFailuresOnSubtests, _ := cmd.Flags().GetBool("ignore-parent-failures-on-subtests")
4445

4546
outputDir := filepath.Dir(outputPath)
4647
initialDirSize, err := getDirSize(outputDir)
@@ -63,7 +64,6 @@ var RunTestsCmd = &cobra.Command{
6364
// Determine test packages
6465
var testPackages []string
6566
if len(testCmdStrings) == 0 {
66-
// No custom command -> parse packages
6767
if testPackagesJson != "" {
6868
if err := json.Unmarshal([]byte(testPackagesJson), &testPackages); err != nil {
6969
log.Error().Err(err).Msg("Error decoding test packages JSON")
@@ -79,18 +79,19 @@ var RunTestsCmd = &cobra.Command{
7979

8080
// Initialize the runner
8181
testRunner := runner.Runner{
82-
ProjectPath: projectPath,
83-
Verbose: true,
84-
RunCount: runCount,
85-
Timeout: timeout,
86-
Tags: tags,
87-
UseRace: useRace,
88-
SkipTests: skipTests,
89-
SelectTests: selectTests,
90-
UseShuffle: useShuffle,
91-
ShuffleSeed: shuffleSeed,
92-
OmitOutputsOnSuccess: omitOutputsOnSuccess,
93-
MaxPassRatio: maxPassRatio,
82+
ProjectPath: projectPath,
83+
Verbose: true,
84+
RunCount: runCount,
85+
Timeout: timeout,
86+
Tags: tags,
87+
UseRace: useRace,
88+
SkipTests: skipTests,
89+
SelectTests: selectTests,
90+
UseShuffle: useShuffle,
91+
ShuffleSeed: shuffleSeed,
92+
OmitOutputsOnSuccess: omitOutputsOnSuccess,
93+
MaxPassRatio: maxPassRatio,
94+
IgnoreParentFailuresOnSubtests: ignoreParentFailuresOnSubtests,
9495
}
9596

9697
// Run the tests
@@ -105,7 +106,6 @@ var RunTestsCmd = &cobra.Command{
105106
os.Exit(ErrorExitCode)
106107
}
107108
} else {
108-
// Otherwise, use the normal go test approach
109109
testReport, err = testRunner.RunTestPackages(testPackages)
110110
if err != nil {
111111
log.Fatal().Err(err).Msg("Error running test packages")
@@ -180,6 +180,7 @@ func init() {
180180
RunTestsCmd.Flags().StringSlice("select-tests", nil, "Comma-separated list of test names to specifically run")
181181
RunTestsCmd.Flags().Float64("max-pass-ratio", 1.0, "The maximum pass ratio threshold for a test to be considered flaky. Any tests below this pass rate will be considered flaky.")
182182
RunTestsCmd.Flags().Bool("omit-test-outputs-on-success", true, "Omit test outputs and package outputs for tests that pass")
183+
RunTestsCmd.Flags().Bool("ignore-parent-failures-on-subtests", false, "Ignore failures in parent tests when only subtests fail")
183184
}
184185

185186
func checkDependencies(projectPath string) error {

tools/flakeguard/go-test-transform/pkg/example/example_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,7 @@ func TestDeepNesting(t *testing.T) {
199199
t.Run("Level3", func(t *testing.T) {
200200
t.Run("Level4", func(t *testing.T) {
201201
t.Run("Level5", func(t *testing.T) {
202-
if !simulateFlaky() {
203-
t.Error("Deep nested test failed")
204-
}
202+
t.Error("Deep nested test failed")
205203
})
206204
})
207205
})

tools/flakeguard/go-test-transform/pkg/transformer/transformer.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type TestNode struct {
3737
}
3838

3939
// TransformJSON transforms go test -json output according to the options
40-
func TransformJSON(input io.Reader, output io.Writer, opts *Options) (int, error) {
40+
func TransformJSON(input io.Reader, output io.Writer, opts *Options) error {
4141
// Create scanner for JSON input
4242
scanner := bufio.NewScanner(input)
4343
scanner.Buffer(make([]byte, 1024*1024), 10*1024*1024) // 10MB max buffer
@@ -47,13 +47,13 @@ func TransformJSON(input io.Reader, output io.Writer, opts *Options) (int, error
4747
for scanner.Scan() {
4848
var event TestEvent
4949
if err := json.Unmarshal(scanner.Bytes(), &event); err != nil {
50-
return 1, fmt.Errorf("failed to parse JSON: %v", err)
50+
return fmt.Errorf("failed to parse JSON: %v", err)
5151
}
5252
events = append(events, event)
5353
}
5454

5555
if err := scanner.Err(); err != nil {
56-
return 1, fmt.Errorf("error reading input: %v", err)
56+
return fmt.Errorf("error reading input: %v", err)
5757
}
5858

5959
// Build test tree
@@ -69,22 +69,18 @@ func TransformJSON(input io.Reader, output io.Writer, opts *Options) (int, error
6969
identifyTestsToIgnore(testTree, opts)
7070

7171
// Transform events
72-
transformedEvents, anyRemainingFailures := transformEvents(events, testTree)
72+
transformedEvents, _ := transformEvents(events, testTree)
7373

7474
// Output transformed events
7575
for _, event := range transformedEvents {
7676
eventJSON, err := json.Marshal(event)
7777
if err != nil {
78-
return 1, fmt.Errorf("failed to marshal JSON: %v", err)
78+
return fmt.Errorf("failed to marshal JSON: %v", err)
7979
}
8080
fmt.Fprintln(output, string(eventJSON))
8181
}
8282

83-
// Return appropriate exit code
84-
if anyRemainingFailures {
85-
return 1, nil
86-
}
87-
return 0, nil
83+
return nil
8884
}
8985

9086
// buildTestTree builds a tree of tests from the events

tools/flakeguard/go-test-transform/pkg/transformer/transformer_test.go

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func createEvent(action, pkg, test string, elapsed float64, output string) testE
2626
}
2727

2828
// transformAndVerify is a helper function to transform events and verify the results
29-
func transformAndVerify(t *testing.T, events []testEvent, opts *Options, expectedExitCode int, expectedActions map[string]string) {
29+
func transformAndVerify(t *testing.T, events []testEvent, opts *Options, expectedActions map[string]string) {
3030
// Convert test events to JSON
3131
var input bytes.Buffer
3232
for _, event := range events {
@@ -39,16 +39,11 @@ func transformAndVerify(t *testing.T, events []testEvent, opts *Options, expecte
3939

4040
// Transform the events
4141
var output bytes.Buffer
42-
exitCode, err := TransformJSON(&input, &output, opts)
42+
err := TransformJSON(&input, &output, opts)
4343
if err != nil {
4444
t.Fatalf("Failed to transform JSON: %v", err)
4545
}
4646

47-
// Verify exit code
48-
if exitCode != expectedExitCode {
49-
t.Errorf("Expected exit code %d, got %d", expectedExitCode, exitCode)
50-
}
51-
5247
// Parse the output
5348
scanner := bufio.NewScanner(&output)
5449
var resultEvents []TestEvent
@@ -105,7 +100,7 @@ func TestIgnoreAllSubtests(t *testing.T) {
105100
"example/": "pass",
106101
}
107102

108-
transformAndVerify(t, events, opts, 1, expectedActions)
103+
transformAndVerify(t, events, opts, expectedActions)
109104
}
110105

111106
// TestNestedSubtests tests handling nested subtests
@@ -139,7 +134,7 @@ func TestNestedSubtests(t *testing.T) {
139134
"example/": "pass",
140135
}
141136

142-
transformAndVerify(t, events, opts, 1, expectedActions)
137+
transformAndVerify(t, events, opts, expectedActions)
143138
}
144139

145140
// TestParallelTestsEventOrdering tests handling of parallel tests
@@ -171,7 +166,7 @@ func TestParallelTestsEventOrdering(t *testing.T) {
171166
"example/": "pass",
172167
}
173168

174-
transformAndVerify(t, events, opts, 1, expectedActions)
169+
transformAndVerify(t, events, opts, expectedActions)
175170
}
176171

177172
// TestOutputTransformation tests transformation of output text
@@ -191,16 +186,12 @@ func TestEmptyInput(t *testing.T) {
191186
opts := DefaultOptions()
192187

193188
var input, output bytes.Buffer
194-
exitCode, err := TransformJSON(&input, &output, opts)
189+
err := TransformJSON(&input, &output, opts)
195190

196191
if err != nil {
197192
t.Errorf("Expected no error for empty input, got: %v", err)
198193
}
199194

200-
if exitCode != 0 {
201-
t.Errorf("Expected exit code 0 for empty input, got: %d", exitCode)
202-
}
203-
204195
if output.Len() != 0 {
205196
t.Errorf("Expected empty output for empty input, got: %s", output.String())
206197
}
@@ -213,7 +204,7 @@ func TestMalformedJSON(t *testing.T) {
213204
input := strings.NewReader("This is not JSON\n{\"Action\":\"fail\"")
214205
var output bytes.Buffer
215206

216-
_, err := TransformJSON(input, &output, opts)
207+
err := TransformJSON(input, &output, opts)
217208

218209
if err == nil {
219210
t.Error("Expected error for malformed JSON, got nil")
@@ -257,7 +248,7 @@ func TestParentWithDirectFailureAndFailingSubtest(t *testing.T) {
257248
"example/": "pass",
258249
}
259250

260-
transformAndVerify(t, events, opts, 1, expectedActions)
251+
transformAndVerify(t, events, opts, expectedActions)
261252
}
262253

263254
func TestParentWithOnlyDirectFailure(t *testing.T) {
@@ -290,7 +281,7 @@ func TestParentWithOnlyDirectFailure(t *testing.T) {
290281
"example/": "pass",
291282
}
292283

293-
transformAndVerify(t, events, opts, 1, expectedActions)
284+
transformAndVerify(t, events, opts, expectedActions)
294285
}
295286

296287
// TestMultiLevelNestedFailures tests multiple levels of nested failures
@@ -333,7 +324,7 @@ func TestMultiLevelNestedFailures(t *testing.T) {
333324
"example/": "pass",
334325
}
335326

336-
transformAndVerify(t, events, opts, 1, expectedActions)
327+
transformAndVerify(t, events, opts, expectedActions)
337328
}
338329

339330
// TestOnlyDirectFailures tests a scenario where there are no failing subtests, only direct failures
@@ -377,7 +368,7 @@ func TestOnlyDirectFailures(t *testing.T) {
377368
}
378369

379370
// Exit code should be 1 because there are still failing tests
380-
transformAndVerify(t, events, opts, 1, expectedActions)
371+
transformAndVerify(t, events, opts, expectedActions)
381372
}
382373

383374
// TestLogMessagesNotDirectFailures tests that log messages are not treated as direct failures
@@ -417,7 +408,7 @@ func TestLogMessagesNotDirectFailures(t *testing.T) {
417408
"example/": "pass",
418409
}
419410

420-
transformAndVerify(t, events, opts, 1, expectedActions)
411+
transformAndVerify(t, events, opts, expectedActions)
421412
}
422413

423414
// TestNestedLogsAllPassing tests that log messages in passing tests are handled correctly
@@ -453,7 +444,7 @@ func TestNestedLogsAllPassing(t *testing.T) {
453444
"example/": "pass",
454445
}
455446

456-
transformAndVerify(t, events, opts, 0, expectedActions)
447+
transformAndVerify(t, events, opts, expectedActions)
457448
}
458449

459450
// TestSkippedTests tests handling of skipped tests
@@ -483,7 +474,7 @@ func TestSkippedTests(t *testing.T) {
483474
"example/TestSkippedTests": "pass",
484475
}
485476

486-
transformAndVerify(t, events, opts, 0, expectedActions)
477+
transformAndVerify(t, events, opts, expectedActions)
487478
}
488479

489480
// TestDeepNesting tests handling of deeply nested test hierarchies
@@ -529,7 +520,7 @@ func TestDeepNesting(t *testing.T) {
529520
"example/": "pass",
530521
}
531522

532-
transformAndVerify(t, events, opts, 1, expectedActions)
523+
transformAndVerify(t, events, opts, expectedActions)
533524
}
534525

535526
// TestConcurrentTests tests handling of concurrent test execution
@@ -580,7 +571,7 @@ func TestConcurrentTests(t *testing.T) {
580571
"example/": "pass",
581572
}
582573

583-
transformAndVerify(t, events, opts, 1, expectedActions)
574+
transformAndVerify(t, events, opts, expectedActions)
584575
}
585576

586577
// TestTableDrivenTests tests handling of table-driven tests
@@ -628,7 +619,7 @@ func TestTableDrivenTests(t *testing.T) {
628619
"example/": "pass",
629620
}
630621

631-
transformAndVerify(t, events, opts, 1, expectedActions)
622+
transformAndVerify(t, events, opts, expectedActions)
632623
}
633624

634625
// TestSubtestReuse tests handling of tests that reuse the same subtest function
@@ -677,7 +668,7 @@ func TestSubtestReuse(t *testing.T) {
677668
"example/": "pass",
678669
}
679670

680-
transformAndVerify(t, events, opts, 1, expectedActions)
671+
transformAndVerify(t, events, opts, expectedActions)
681672
}
682673

683674
// TestSpecialCharactersInTestNames tests handling of tests with special characters in names
@@ -720,7 +711,7 @@ func TestSpecialCharactersInTestNames(t *testing.T) {
720711
"example/": "pass",
721712
}
722713

723-
transformAndVerify(t, events, opts, 1, expectedActions)
714+
transformAndVerify(t, events, opts, expectedActions)
724715
}
725716

726717
func TestSubTestNameWithSlashes(t *testing.T) {
@@ -751,7 +742,7 @@ func TestSubTestNameWithSlashes(t *testing.T) {
751742
"example/": "pass",
752743
}
753744

754-
transformAndVerify(t, events, opts, 0, expectedActions)
745+
transformAndVerify(t, events, opts, expectedActions)
755746
}
756747

757748
func TestFuzzTestWithCorpus(t *testing.T) {
@@ -796,5 +787,5 @@ func TestFuzzTestWithCorpus(t *testing.T) {
796787
}
797788

798789
// transformAndVerify should yield exit code 0 since everything passes
799-
transformAndVerify(t, events, opts, 0, expectedActions)
790+
transformAndVerify(t, events, opts, expectedActions)
800791
}

0 commit comments

Comments
 (0)