@@ -18,15 +18,11 @@ import (
1818 "github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/utils"
1919)
2020
21- // Exit codes for the application
2221const (
23- // FlakyTestsExitCode indicates that Flakeguard ran correctly and was able to identify flaky tests
2422 FlakyTestsExitCode = 1
25- // ErrorExitCode indicates that Flakeguard ran into an error and was not able to complete operation
26- ErrorExitCode = 2
23+ ErrorExitCode = 2
2724)
2825
29- // runConfig holds the configuration parameters for the run command.
3026type runConfig struct {
3127 ProjectPath string
3228 CodeownersPath string
@@ -51,7 +47,6 @@ type runConfig struct {
5147 GoTestCount * int
5248}
5349
54- // summaryAndExit encapsulates the summary buffer and exit logic.
5550type summaryAndExit struct {
5651 buffer bytes.Buffer
5752 code int
@@ -86,7 +81,7 @@ var RunTestsCmd = &cobra.Command{
8681 Use : "run" ,
8782 Short : "Run tests to check if they are flaky" ,
8883 Run : func (cmd * cobra.Command , args []string ) {
89- exitHandler := & summaryAndExit {} // Initialize the exit handler
84+ exitHandler := & summaryAndExit {}
9085
9186 cfg , err := parseAndValidateFlags (cmd )
9287 if err != nil {
@@ -95,25 +90,20 @@ var RunTestsCmd = &cobra.Command{
9590
9691 goProject , err := utils .GetGoProjectName (cfg .ProjectPath )
9792 if err != nil {
98- // Log warning but continue, as it's not critical for core functionality
9993 log .Warn ().Err (err ).Str ("projectPath" , cfg .ProjectPath ).Msg ("Failed to get pretty project path" )
10094 }
10195
102- // Check if project dependencies are correctly set up
10396 if err := checkDependencies (cfg .ProjectPath ); err != nil {
10497 exitHandler .logErrorAndExit (err , "Error checking project dependencies" )
10598 }
10699
107- // Determine test packages
108100 testPackages , err := determineTestPackages (cfg )
109101 if err != nil {
110102 exitHandler .logErrorAndExit (err , "Failed to determine test packages" )
111103 }
112104
113- // Initialize the runner
114105 testRunner := initializeRunner (cfg )
115106
116- // --- Main Test Execution ---
117107 s := spinner .New (spinner .CharSets [14 ], 100 * time .Millisecond )
118108 s .Suffix = " Running tests..."
119109 s .Start ()
@@ -133,32 +123,27 @@ var RunTestsCmd = &cobra.Command{
133123 exitHandler .logErrorAndExit (runErr , "Error running tests" )
134124 }
135125 if len (mainResults ) == 0 {
136- // Use logMsgAndExit for non-error exits
137126 exitHandler .logMsgAndExit (zerolog .ErrorLevel , "No tests were run." , ErrorExitCode )
138127 }
139128
140- // --- Report Generation and Saving ---
141129 mainReport , err := generateMainReport (mainResults , cfg , goProject )
142130 if err != nil {
143131 exitHandler .logErrorAndExit (err , "Error creating main test report" )
144132 }
145133 if cfg .MainResultsPath != "" {
146134 if err := reports .SaveTestResultsToFile (mainResults , cfg .MainResultsPath ); err != nil {
147- // Log error but continue processing if possible
148135 log .Error ().Err (err ).Str ("path" , cfg .MainResultsPath ).Msg ("Error saving main test results to file" )
149136 } else {
150137 log .Info ().Str ("path" , cfg .MainResultsPath ).Msg ("Main test report saved" )
151138 }
152139 }
153140
154- // --- Rerun Logic ---
155141 if cfg .RerunFailedCount > 0 {
156142 handleReruns (exitHandler , testRunner , mainReport , cfg , goProject )
157143 } else {
158144 handleNoReruns (exitHandler , mainReport , cfg )
159145 }
160146
161- // If we haven't exited yet, exit successfully
162147 exitHandler .code = 0
163148 exitHandler .flush ()
164149 },
@@ -189,7 +174,6 @@ func parseAndValidateFlags(cmd *cobra.Command) (*runConfig, error) {
189174 cfg .FailFast , _ = cmd .Flags ().GetBool ("fail-fast" )
190175 cfg .GoTestTimeout , _ = cmd .Flags ().GetString ("go-test-timeout" )
191176
192- // Resolve paths
193177 cfg .ProjectPath , err = utils .ResolveFullPath (cfg .ProjectPath )
194178 if err != nil {
195179 return nil , fmt .Errorf ("failed to resolve full path for project path '%s': %w" , cfg .ProjectPath , err )
@@ -207,7 +191,6 @@ func parseAndValidateFlags(cmd *cobra.Command) (*runConfig, error) {
207191 }
208192 }
209193
210- // Handle go-test-count flag
211194 if cmd .Flags ().Changed ("go-test-count" ) {
212195 v , err := cmd .Flags ().GetInt ("go-test-count" )
213196 if err != nil {
@@ -216,9 +199,8 @@ func parseAndValidateFlags(cmd *cobra.Command) (*runConfig, error) {
216199 cfg .GoTestCount = & v
217200 }
218201
219- // Handle pass ratio compatibility
220202 minPassRatio , _ := cmd .Flags ().GetFloat64 ("min-pass-ratio" )
221- maxPassRatio , _ := cmd .Flags ().GetFloat64 ("max-pass-ratio" ) // Deprecated
203+ maxPassRatio , _ := cmd .Flags ().GetFloat64 ("max-pass-ratio" )
222204 maxPassRatioSpecified := cmd .Flags ().Changed ("max-pass-ratio" )
223205
224206 cfg .MinPassRatio = minPassRatio
@@ -227,7 +209,6 @@ func parseAndValidateFlags(cmd *cobra.Command) (*runConfig, error) {
227209 cfg .MinPassRatio = maxPassRatio // Use the deprecated value if specified
228210 }
229211
230- // Validate pass ratio
231212 if cfg .MinPassRatio < 0 || cfg .MinPassRatio > 1 {
232213 return nil , fmt .Errorf ("pass ratio must be between 0 and 1, got: %.2f" , cfg .MinPassRatio )
233214 }
@@ -249,7 +230,6 @@ func determineTestPackages(cfg *runConfig) ([]string, error) {
249230 } else if len (cfg .TestPackages ) > 0 {
250231 testPackages = cfg .TestPackages
251232 } else {
252- // TODO: Consider adding support for --run-all-packages flag if needed
253233 return nil , fmt .Errorf ("must specify either --test-packages-json, --test-packages, or --test-cmd" )
254234 }
255235 return testPackages , nil
@@ -259,8 +239,7 @@ func determineTestPackages(cfg *runConfig) ([]string, error) {
259239func initializeRunner (cfg * runConfig ) * runner.Runner {
260240 return runner .NewRunner (
261241 cfg .ProjectPath ,
262- true , // Assuming verbose is always true for now, consider adding a flag if needed
263- // Runner specific config
242+ true ,
264243 cfg .RunCount ,
265244 cfg .GoTestCount ,
266245 cfg .UseRace ,
@@ -271,30 +250,27 @@ func initializeRunner(cfg *runConfig) *runner.Runner {
271250 cfg .FailFast ,
272251 cfg .SkipTests ,
273252 cfg .SelectTests ,
274- // Parser specific config
275253 cfg .IgnoreParentFailuresOnSubtests ,
276254 cfg .OmitOutputsOnSuccess ,
277- // Dependencies (pass nil to get defaults)
278255 nil , // exec
279256 nil , // parser
280257 )
281258}
282259
283260// generateMainReport creates the initial test report from the main run results.
284261func generateMainReport (results []reports.TestResult , cfg * runConfig , goProject string ) (* reports.TestReport , error ) {
285- // Assuming NewTestReport returns (TestReport, error) based on the linter error
286262 reportVal , err := reports .NewTestReport (results ,
287263 reports .WithGoProject (goProject ),
288264 reports .WithCodeOwnersPath (cfg .CodeownersPath ),
289- reports .WithMaxPassRatio (cfg .MinPassRatio ), // Use the validated MinPassRatio
265+ reports .WithMaxPassRatio (cfg .MinPassRatio ),
290266 reports .WithGoRaceDetection (cfg .UseRace ),
291267 reports .WithExcludedTests (cfg .SkipTests ),
292268 reports .WithSelectedTests (cfg .SelectTests ),
293269 )
294270 if err != nil {
295- return nil , err // Propagate the error
271+ return nil , err
296272 }
297- return & reportVal , nil // Return the address of the struct value
273+ return & reportVal , nil
298274}
299275
300276// handleReruns manages the process of rerunning failed tests and reporting results.
@@ -305,15 +281,13 @@ func handleReruns(exitHandler *summaryAndExit, testRunner *runner.Runner, mainRe
305281
306282 if len (failedTests ) == 0 {
307283 log .Info ().Msg ("All tests passed the initial run. No tests to rerun." )
308- // Use explicit newlines for clarity and to avoid linter issues
309284 fmt .Fprint (& exitHandler .buffer , "\n Flakeguard Summary\n " )
310285 reports .RenderTestReport (& exitHandler .buffer , * mainReport , false , false )
311286 exitHandler .code = 0
312287 exitHandler .flush ()
313- return // Exit successfully
288+ return
314289 }
315290
316- // Check if we should skip reruns due to --test-cmd and command-line-arguments
317291 if len (cfg .TestCmds ) > 0 {
318292 foundCommandLineArgs := false
319293 for _ , test := range failedTests {
@@ -328,17 +302,14 @@ func handleReruns(exitHandler *summaryAndExit, testRunner *runner.Runner, mainRe
328302 "Flakeguard cannot reliably rerun these tests as it loses the original directory context. " +
329303 "Results are based on the initial run only. To enable reruns, use 'go test . -run TestPattern' instead of 'go test <file.go>' within your --test-cmd."
330304 log .Warn ().Msg (warningMsg )
331- // Use explicit newlines
332305 fmt .Fprint (& exitHandler .buffer , "\n Failed Tests On The First Run:\n \n " )
333306 reports .PrintTestResultsTable (& exitHandler .buffer , failedTests , false , false , true , false , false , false )
334- fmt .Fprintf (& exitHandler .buffer , "\n \n %s\n " , warningMsg ) // Print the detailed warning
335- // Evaluate the initial run results as if reruns were disabled
307+ fmt .Fprintf (& exitHandler .buffer , "\n \n %s\n " , warningMsg )
336308 handleNoReruns (exitHandler , mainReport , cfg )
337- return // Exit after handling based on the initial run
309+ return
338310 }
339311 }
340312
341- // Use explicit newlines
342313 fmt .Fprint (& exitHandler .buffer , "\n Failed Tests On The First Run:\n \n " )
343314 reports .PrintTestResultsTable (& exitHandler .buffer , failedTests , false , false , true , false , false , false )
344315 fmt .Fprintln (& exitHandler .buffer )
@@ -355,11 +326,10 @@ func handleReruns(exitHandler *summaryAndExit, testRunner *runner.Runner, mainRe
355326 exitHandler .logErrorAndExit (err , "Error rerunning failed tests" )
356327 }
357328
358- // --- Process Rerun Results ---
359329 rerunReport , err := reports .NewTestReport (rerunResults ,
360330 reports .WithGoProject (goProject ),
361331 reports .WithCodeOwnersPath (cfg .CodeownersPath ),
362- reports .WithMaxPassRatio (1 ), // Rerun target is always 100% pass
332+ reports .WithMaxPassRatio (1 ),
363333 reports .WithExcludedTests (cfg .SkipTests ),
364334 reports .WithSelectedTests (cfg .SelectTests ),
365335 reports .WithJSONOutputPaths (rerunJsonOutputPaths ),
@@ -368,7 +338,6 @@ func handleReruns(exitHandler *summaryAndExit, testRunner *runner.Runner, mainRe
368338 exitHandler .logErrorAndExit (err , "Error creating rerun test report" )
369339 }
370340
371- // Use explicit newlines
372341 fmt .Fprint (& exitHandler .buffer , "\n Tests After Rerun:\n \n " )
373342 reports .PrintTestResultsTable (& exitHandler .buffer , rerunResults , false , false , true , true , true , true )
374343 fmt .Fprintln (& exitHandler .buffer )
@@ -377,7 +346,6 @@ func handleReruns(exitHandler *summaryAndExit, testRunner *runner.Runner, mainRe
377346 if cfg .RerunResultsPath != "" && len (rerunResults ) > 0 {
378347 if err := reports .SaveTestResultsToFile (rerunResults , cfg .RerunResultsPath ); err != nil {
379348 log .Error ().Err (err ).Str ("path" , cfg .RerunResultsPath ).Msg ("Error saving rerun test results to file" )
380- // Continue even if saving fails
381349 } else {
382350 log .Info ().Str ("path" , cfg .RerunResultsPath ).Msg ("Rerun test report saved" )
383351 }
@@ -389,37 +357,29 @@ func handleReruns(exitHandler *summaryAndExit, testRunner *runner.Runner, mainRe
389357 })
390358
391359 if len (failedAfterRerun ) > 0 {
392- // Use explicit newlines
393360 fmt .Fprint (& exitHandler .buffer , "\n Persistently Failing Test Logs:\n \n " )
394- // Attempt to print logs only for tests that never passed
395361 err := rerunReport .PrintGotestsumOutput (& exitHandler .buffer , "pkgname" )
396362 if err != nil {
397363 log .Error ().Err (err ).Msg ("Error printing gotestsum output for persistently failing tests" )
398- // Try printing all logs as a fallback - already done above
399364 }
400365
401366 exitHandler .logMsgAndExit (zerolog .ErrorLevel , "Some tests are still failing after multiple reruns with no successful attempts." , ErrorExitCode , map [string ]interface {}{
402367 "persistently_failing_count" : len (failedAfterRerun ),
403368 "rerun_attempts" : cfg .RerunFailedCount ,
404369 })
405370 } else {
406- // All originally failing tests passed at least once during reruns
407371 log .Info ().Msg ("All initially failing tests passed at least once after reruns." )
408- exitHandler .code = 0 // Success
409- // Summary already includes rerun info printed above
372+ exitHandler .code = 0
410373 exitHandler .flush ()
411374 }
412375}
413376
414377// handleNoReruns determines the outcome when reruns are disabled.
415378func handleNoReruns (exitHandler * summaryAndExit , mainReport * reports.TestReport , cfg * runConfig ) {
416- // Filter flaky tests based on the MinPassRatio from the initial run
417379 flakyTests := reports .FilterTests (mainReport .Results , func (tr reports.TestResult ) bool {
418- // A test is flaky if it wasn't skipped and its pass ratio is below the configured threshold
419380 return ! tr .Skipped && tr .PassRatio < cfg .MinPassRatio
420381 })
421382
422- // Use explicit newlines
423383 fmt .Fprint (& exitHandler .buffer , "\n Flakeguard Summary\n " )
424384 reports .RenderTestReport (& exitHandler .buffer , * mainReport , false , false )
425385
@@ -430,28 +390,22 @@ func handleNoReruns(exitHandler *summaryAndExit, mainReport *reports.TestReport,
430390 })
431391 } else {
432392 log .Info ().Msg ("All tests passed stability requirements." )
433- exitHandler .code = 0 // Success
434- // Summary already printed above
393+ exitHandler .code = 0
435394 exitHandler .flush ()
436395 }
437396}
438397
439398// init sets up the cobra command flags.
440399func init () {
441- // General execution flags
442400 RunTestsCmd .Flags ().StringP ("project-path" , "r" , "." , "The path to the Go project. Default is the current directory. Useful for subprojects" )
443401 RunTestsCmd .Flags ().StringP ("codeowners-path" , "" , "" , "Path to the CODEOWNERS file" )
444-
445- // Test selection flags
446402 RunTestsCmd .Flags ().String ("test-packages-json" , "" , "JSON-encoded string of test packages" )
447403 RunTestsCmd .Flags ().StringSlice ("test-packages" , nil , "Comma-separated list of test packages to run" )
448404 RunTestsCmd .Flags ().StringArray ("test-cmd" , nil ,
449405 "Optional custom test command(s) (e.g. 'go test -json ./... -v'), which must produce 'go test -json' output. Can be specified multiple times." ,
450406 )
451407 RunTestsCmd .Flags ().StringSlice ("skip-tests" , nil , "Comma-separated list of test names (regex supported by `go test -skip`) to skip" )
452408 RunTestsCmd .Flags ().StringSlice ("select-tests" , nil , "Comma-separated list of test names (regex supported by `go test -run`) to specifically run" )
453-
454- // Test execution control flags
455409 RunTestsCmd .Flags ().IntP ("run-count" , "c" , 1 , "Number of times to run the tests (for main run)" )
456410 RunTestsCmd .Flags ().Int ("rerun-failed-count" , 0 , "Number of times to rerun tests that did not achieve 100% pass rate in the main run (0 disables reruns)" )
457411 RunTestsCmd .Flags ().StringArray ("tags" , nil , "Passed on to the 'go test' command as the -tags flag" )
@@ -461,14 +415,10 @@ func init() {
461415 RunTestsCmd .Flags ().Bool ("shuffle" , false , "Enable test shuffling ('go test -shuffle=on')" )
462416 RunTestsCmd .Flags ().String ("shuffle-seed" , "" , "Set seed for test shuffling. Requires --shuffle. ('go test -shuffle=on -shuffle.seed=...')" )
463417 RunTestsCmd .Flags ().Bool ("fail-fast" , false , "Stop test execution on the first failure (-failfast flag for 'go test')" )
464-
465- // Reporting and output flags
466418 RunTestsCmd .Flags ().String ("main-results-path" , "" , "Path to save the main test results (JSON format)" )
467419 RunTestsCmd .Flags ().String ("rerun-results-path" , "" , "Path to save the rerun test results (JSON format)" )
468420 RunTestsCmd .Flags ().Bool ("omit-test-outputs-on-success" , true , "Omit test outputs and package outputs for tests that pass all runs" )
469421 RunTestsCmd .Flags ().Bool ("ignore-parent-failures-on-subtests" , false , "Ignore failures in parent tests when only subtests fail (affects parsing)" )
470-
471- // Flake detection flags
472422 RunTestsCmd .Flags ().Float64 ("min-pass-ratio" , 1.0 , "The minimum pass ratio (0.0-1.0) required for a test in the main run to be considered stable." )
473423 RunTestsCmd .Flags ().Float64 ("max-pass-ratio" , 1.0 , "DEPRECATED: Use --min-pass-ratio instead. This flag will be removed in a future version." )
474424 _ = RunTestsCmd .Flags ().MarkDeprecated ("max-pass-ratio" , "use --min-pass-ratio instead" )
@@ -485,7 +435,6 @@ func checkDependencies(projectPath string) error {
485435 cmd .Stderr = & out // Capture stderr as well
486436
487437 if err := cmd .Run (); err != nil {
488- // Try to provide a more helpful error message including the output
489438 return fmt .Errorf ("dependency check ('go mod tidy') failed: %w\n %s" , err , out .String ())
490439 }
491440 log .Debug ().Msg ("'go mod tidy' completed successfully." )
0 commit comments