Skip to content

Commit 61140fb

Browse files
committed
Refactor run functions to return test results array
1 parent 251c735 commit 61140fb

File tree

9 files changed

+477
-365
lines changed

9 files changed

+477
-365
lines changed

tools/flakeguard/cmd/run.go

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/rs/zerolog/log"
1111
"github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/reports"
1212
"github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/runner"
13+
"github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/utils"
1314
"github.com/spf13/cobra"
1415
)
1516

@@ -35,15 +36,16 @@ var RunTestsCmd = &cobra.Command{
3536

3637
// Retrieve flags
3738
projectPath, _ := cmd.Flags().GetString("project-path")
39+
codeownersPath, _ := cmd.Flags().GetString("codeowners-path")
3840
testPackagesJson, _ := cmd.Flags().GetString("test-packages-json")
3941
testPackagesArg, _ := cmd.Flags().GetStringSlice("test-packages")
4042
testCmdStrings, _ := cmd.Flags().GetStringArray("test-cmd")
4143
runCount, _ := cmd.Flags().GetInt("run-count")
4244
rerunFailedCount, _ := cmd.Flags().GetInt("rerun-failed-count")
4345
tags, _ := cmd.Flags().GetStringArray("tags")
4446
useRace, _ := cmd.Flags().GetBool("race")
45-
mainReportPath, _ := cmd.Flags().GetString("main-report-path")
46-
rerunReportPath, _ := cmd.Flags().GetString("rerun-report-path")
47+
mainResultsPath, _ := cmd.Flags().GetString("main-results-path")
48+
rerunResultsPath, _ := cmd.Flags().GetString("rerun-results-path")
4749
minPassRatio, _ := cmd.Flags().GetFloat64("min-pass-ratio")
4850
// For backward compatibility, check if max-pass-ratio was used
4951
maxPassRatio, _ := cmd.Flags().GetFloat64("max-pass-ratio")
@@ -57,6 +59,11 @@ var RunTestsCmd = &cobra.Command{
5759
failFast, _ := cmd.Flags().GetBool("fail-fast")
5860
goTestTimeoutFlag, _ := cmd.Flags().GetString("go-test-timeout")
5961

62+
goProject, err := utils.GetGoProjectName(projectPath)
63+
if err != nil {
64+
log.Warn().Err(err).Str("projectPath", goProject).Msg("Failed to get pretty project path")
65+
}
66+
6067
// Retrieve go-test-count flag as a pointer if explicitly provided.
6168
var goTestCountFlag *int
6269
if cmd.Flags().Changed("go-test-count") {
@@ -124,45 +131,53 @@ var RunTestsCmd = &cobra.Command{
124131
FailFast: failFast,
125132
}
126133

127-
var (
128-
mainReport *reports.TestReport // Main test report
129-
rerunReport *reports.TestReport // Test report after rerunning failed tests
130-
)
131-
132134
// Run the tests
133-
var err error
135+
var mainResults []reports.TestResult
134136
if len(testCmdStrings) > 0 {
135-
mainReport, err = testRunner.RunTestCmd(testCmdStrings)
137+
mainResults, err = testRunner.RunTestCmd(testCmdStrings)
136138
if err != nil {
137139
log.Fatal().Err(err).Msg("Error running custom test command")
138140
flushSummaryAndExit(ErrorExitCode)
139141
}
140142
} else {
141-
mainReport, err = testRunner.RunTestPackages(testPackages)
143+
mainResults, err = testRunner.RunTestPackages(testPackages)
142144
if err != nil {
143145
log.Fatal().Err(err).Msg("Error running test packages")
144146
flushSummaryAndExit(ErrorExitCode)
145147
}
146148
}
147149

148-
// Save the main test report to file
149-
if mainReportPath != "" && len(mainReport.Results) > 0 {
150-
if err := mainReport.SaveToFile(mainReportPath); err != nil {
150+
if len(mainResults) == 0 {
151+
log.Warn().Msg("No tests were run for the specified packages")
152+
flushSummaryAndExit(0)
153+
}
154+
155+
// Save the main test results to file
156+
if mainResultsPath != "" && len(mainResults) > 0 {
157+
if err := reports.SaveTestResultsToFile(mainResults, mainResultsPath); err != nil {
151158
log.Error().Err(err).Msg("Error saving test results to file")
152159
flushSummaryAndExit(ErrorExitCode)
153160
}
154-
log.Info().Str("path", mainReportPath).Msg("Main test report saved")
161+
log.Info().Str("path", mainResultsPath).Msg("Main test report saved")
155162
}
156163

157-
if len(mainReport.Results) == 0 {
158-
log.Warn().Msg("No tests were run for the specified packages")
159-
flushSummaryAndExit(0)
164+
mainReport, err := reports.NewTestReport(mainResults,
165+
reports.WithGoProject(goProject),
166+
reports.WithCodeOwnersPath(codeownersPath),
167+
reports.WithMaxPassRatio(passRatioThreshold),
168+
reports.WithGoRaceDetection(useRace),
169+
reports.WithExcludedTests(skipTests),
170+
reports.WithSelectedTests(selectTests),
171+
)
172+
if err != nil {
173+
log.Error().Err(err).Msg("Error creating main test report")
174+
flushSummaryAndExit(ErrorExitCode)
160175
}
161176

162177
// Accumulate main summary into summaryBuffer
163178
fmt.Fprint(&summaryBuffer, "\nFlakeguard Main Summary:\n")
164179
fmt.Fprintf(&summaryBuffer, "-------------------------\n")
165-
reports.RenderResults(&summaryBuffer, *mainReport, false, false)
180+
reports.RenderTestReport(&summaryBuffer, mainReport, false, false)
166181
fmt.Fprintln(&summaryBuffer)
167182

168183
// Rerun failed tests
@@ -176,28 +191,41 @@ var RunTestsCmd = &cobra.Command{
176191
flushSummaryAndExit(0)
177192
}
178193

179-
rerunReport, err = testRunner.RerunFailedTests(failedTests)
194+
rerunResults, rerunJsonOutputPaths, err := testRunner.RerunFailedTests(failedTests)
180195
if err != nil {
181196
log.Fatal().Err(err).Msg("Error rerunning failed tests")
182197
flushSummaryAndExit(ErrorExitCode)
183198
}
184199

200+
rerunReport, err := reports.NewTestReport(rerunResults,
201+
reports.WithGoProject(goProject),
202+
reports.WithCodeOwnersPath(codeownersPath),
203+
reports.WithMaxPassRatio(1),
204+
reports.WithExcludedTests(skipTests),
205+
reports.WithSelectedTests(selectTests),
206+
reports.WithJSONOutputPaths(rerunJsonOutputPaths),
207+
)
208+
if err != nil {
209+
log.Error().Err(err).Msg("Error creating rerun test report")
210+
flushSummaryAndExit(ErrorExitCode)
211+
}
212+
185213
fmt.Fprint(&summaryBuffer, "\nAll Tests That Were Rerun:\n")
186214
fmt.Fprintf(&summaryBuffer, "---------------------------\n")
187215
reports.PrintTestResultsTable(&summaryBuffer, rerunReport.Results, false, false)
188216
fmt.Fprintln(&summaryBuffer)
189217

190218
// Save the rerun test report to file
191-
if rerunReportPath != "" && len(rerunReport.Results) > 0 {
192-
if err := rerunReport.SaveToFile(rerunReportPath); err != nil {
219+
if rerunResultsPath != "" && len(rerunResults) > 0 {
220+
if err := reports.SaveTestResultsToFile(rerunResults, rerunResultsPath); err != nil {
193221
log.Error().Err(err).Msg("Error saving test results to file")
194222
flushSummaryAndExit(ErrorExitCode)
195223
}
196-
log.Info().Str("path", rerunReportPath).Msg("Rerun test report saved")
224+
log.Info().Str("path", rerunResultsPath).Msg("Rerun test report saved")
197225
}
198226

199227
// Filter tests that failed after reruns
200-
failedAfterRerun := reports.FilterTests(rerunReport.Results, func(tr reports.TestResult) bool {
228+
failedAfterRerun := reports.FilterTests(rerunResults, func(tr reports.TestResult) bool {
201229
return !tr.Skipped && tr.Successes == 0
202230
})
203231

@@ -246,6 +274,7 @@ var RunTestsCmd = &cobra.Command{
246274

247275
func init() {
248276
RunTestsCmd.Flags().StringP("project-path", "r", ".", "The path to the Go project. Default is the current directory. Useful for subprojects")
277+
RunTestsCmd.Flags().StringP("codeowners-path", "", "", "Path to the CODEOWNERS file")
249278
RunTestsCmd.Flags().String("test-packages-json", "", "JSON-encoded string of test packages")
250279
RunTestsCmd.Flags().StringSlice("test-packages", nil, "Comma-separated list of test packages to run")
251280
RunTestsCmd.Flags().StringArray("test-cmd", nil,
@@ -260,8 +289,8 @@ func init() {
260289
RunTestsCmd.Flags().Bool("shuffle", false, "Enable test shuffling")
261290
RunTestsCmd.Flags().String("shuffle-seed", "", "Set seed for test shuffling. Must be used with --shuffle")
262291
RunTestsCmd.Flags().Bool("fail-fast", false, "Stop on the first test failure")
263-
RunTestsCmd.Flags().String("main-report-path", "", "Path to the main test report in JSON format")
264-
RunTestsCmd.Flags().String("rerun-report-path", "", "Path to the rerun test report in JSON format")
292+
RunTestsCmd.Flags().String("main-results-path", "", "Path to the main test results in JSON format")
293+
RunTestsCmd.Flags().String("rerun-results-path", "", "Path to the rerun test results in JSON format")
265294
RunTestsCmd.Flags().StringSlice("skip-tests", nil, "Comma-separated list of test names to skip from running")
266295
RunTestsCmd.Flags().StringSlice("select-tests", nil, "Comma-separated list of test names to specifically run")
267296

tools/flakeguard/reports/data.go

Lines changed: 16 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,161 +1,19 @@
11
package reports
22

33
import (
4-
"bytes"
54
"encoding/json"
65
"fmt"
7-
"io"
86
"os"
9-
"os/exec"
107
"sort"
118
"strings"
129
"time"
13-
14-
"github.com/google/uuid"
1510
)
1611

17-
// TestReport reports on the parameters and results of one to many test runs
18-
type TestReport struct {
19-
ID string `json:"id,omitempty"`
20-
GoProject string `json:"go_project"`
21-
BranchName string `json:"branch_name,omitempty"`
22-
HeadSHA string `json:"head_sha,omitempty"`
23-
BaseSHA string `json:"base_sha,omitempty"`
24-
RepoURL string `json:"repo_url,omitempty"`
25-
GitHubWorkflowName string `json:"github_workflow_name,omitempty"`
26-
GitHubWorkflowRunURL string `json:"github_workflow_run_url,omitempty"`
27-
SummaryData *SummaryData `json:"summary_data"`
28-
RaceDetection bool `json:"race_detection"`
29-
ExcludedTests []string `json:"excluded_tests,omitempty"`
30-
SelectedTests []string `json:"selected_tests,omitempty"`
31-
Results []TestResult `json:"results,omitempty"`
32-
FailedLogsURL string `json:"failed_logs_url,omitempty"`
33-
JSONOutputPaths []string `json:"-"` // go test -json outputs from runs
34-
// MaxPassRatio is the maximum flakiness ratio allowed for a test to be considered not flaky
35-
MaxPassRatio float64 `json:"max_pass_ratio,omitempty"`
36-
}
37-
38-
func (r *TestReport) SetRandomReportID() {
39-
uuid, err := uuid.NewRandom()
40-
if err != nil {
41-
panic(fmt.Errorf("error generating random report id: %w", err))
42-
}
43-
r.SetReportID(uuid.String())
44-
}
45-
46-
func (r *TestReport) SetReportID(reportID string) {
47-
r.ID = reportID
48-
// Set the report ID in all test results
49-
for i := range r.Results {
50-
r.Results[i].ReportID = r.ID
51-
}
52-
}
53-
54-
// SaveToFile saves the test report to a JSON file at the given path.
55-
// It returns an error if there's any issue with marshaling the report or writing to the file.
56-
func (testReport *TestReport) SaveToFile(outputPath string) error {
57-
jsonData, err := json.MarshalIndent(testReport, "", " ")
58-
if err != nil {
59-
return fmt.Errorf("error marshaling test results to JSON: %w", err)
60-
}
61-
62-
if err := os.WriteFile(outputPath, jsonData, 0600); err != nil {
63-
return fmt.Errorf("error writing test results to file: %w", err)
64-
}
65-
66-
return nil
67-
}
68-
69-
func (tr *TestReport) PrintGotestsumOutput(w io.Writer, format string) error {
70-
if len(tr.JSONOutputPaths) == 0 {
71-
fmt.Fprintf(w, "No JSON output paths found in test report\n")
72-
return nil
73-
}
74-
75-
for _, path := range tr.JSONOutputPaths {
76-
cmdStr := fmt.Sprintf("cat %q | gotestsum --raw-command --format %q -- cat", path, format)
77-
cmd := exec.Command("bash", "-c", cmdStr)
78-
79-
var outBuf bytes.Buffer
80-
cmd.Stdout = &outBuf
81-
cmd.Stderr = &outBuf
82-
83-
if err := cmd.Run(); err != nil {
84-
return fmt.Errorf("gotestsum command failed for file %s: %w\nOutput: %s", path, err, outBuf.String())
85-
}
86-
87-
fmt.Fprint(w, outBuf.String())
88-
fmt.Fprintln(w, "---------------------")
89-
}
90-
return nil
91-
}
92-
93-
// GenerateSummaryData generates a summary of a report's test results
94-
func (testReport *TestReport) GenerateSummaryData() {
95-
var runs, testRunCount, passes, fails, skips, panickedTests, racedTests, flakyTests int
96-
97-
// Map to hold unique test names that were entirely skipped
98-
uniqueSkippedTestsMap := make(map[string]struct{})
99-
100-
for _, result := range testReport.Results {
101-
runs += result.Runs
102-
if result.Runs > testRunCount {
103-
testRunCount = result.Runs
104-
}
105-
passes += result.Successes
106-
fails += result.Failures
107-
skips += result.Skips
108-
109-
if result.Runs == 0 && result.Skipped {
110-
uniqueSkippedTestsMap[result.TestName] = struct{}{}
111-
}
112-
113-
if result.Panic {
114-
panickedTests++
115-
flakyTests++
116-
} else if result.Race {
117-
racedTests++
118-
flakyTests++
119-
} else if !result.Skipped && result.Runs > 0 && result.PassRatio < testReport.MaxPassRatio {
120-
flakyTests++
121-
}
122-
}
123-
124-
// Calculate the unique count of skipped tests
125-
uniqueSkippedTestCount := len(uniqueSkippedTestsMap)
126-
127-
// Calculate the raw pass ratio
128-
passRatio := passRatio(passes, runs)
129-
130-
// Calculate the raw flake ratio
131-
totalTests := len(testReport.Results)
132-
flakeRatio := flakeRatio(flakyTests, totalTests)
133-
134-
passRatioStr := formatRatio(passRatio)
135-
flakeTestRatioStr := formatRatio(flakeRatio)
136-
137-
testReport.SummaryData = &SummaryData{
138-
UniqueTestsRun: totalTests,
139-
UniqueSkippedTestCount: uniqueSkippedTestCount,
140-
TestRunCount: testRunCount,
141-
PanickedTests: panickedTests,
142-
RacedTests: racedTests,
143-
FlakyTests: flakyTests,
144-
FlakyTestPercent: flakeTestRatioStr,
145-
146-
TotalRuns: runs,
147-
PassedRuns: passes,
148-
FailedRuns: fails,
149-
SkippedRuns: skips,
150-
PassPercent: passRatioStr,
151-
}
152-
}
153-
15412
// TestResult contains the results and outputs of a single test
15513
type TestResult struct {
15614
// ReportID is the ID of the report this test result belongs to
15715
// used mostly for Splunk logging
158-
ReportID string `json:"report_id"`
16+
ReportID string `json:"report_id,omitempty"`
15917
TestName string `json:"test_name"`
16018
TestPackage string `json:"test_package"`
16119
PackagePanic bool `json:"package_panic"`
@@ -173,8 +31,21 @@ type TestResult struct {
17331
FailedOutputs map[string][]string `json:"failed_outputs,omitempty"` // Outputs for failed runs
17432
Durations []time.Duration `json:"durations"`
17533
PackageOutputs []string `json:"package_outputs,omitempty"`
176-
TestPath string `json:"test_path"`
177-
CodeOwners []string `json:"code_owners"`
34+
TestPath string `json:"test_path,omitempty"`
35+
CodeOwners []string `json:"code_owners,omitempty"`
36+
}
37+
38+
func SaveTestResultsToFile(results []TestResult, filePath string) error {
39+
jsonData, err := json.MarshalIndent(results, "", " ")
40+
if err != nil {
41+
return fmt.Errorf("error marshaling test results to JSON: %w", err)
42+
}
43+
44+
if err := os.WriteFile(filePath, jsonData, 0o600); err != nil {
45+
return fmt.Errorf("error writing test results to file: %w", err)
46+
}
47+
48+
return nil
17849
}
17950

18051
// SummaryData contains aggregated data from a set of test results

tools/flakeguard/reports/data_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ func TestAggregate(t *testing.T) {
379379
},
380380
}
381381

382-
aggregateOptions := &aggregateOptions{reportID: "123"}
382+
aggregateOptions := &reportOptions{reportID: "123"}
383383
aggregatedReport, err := aggregateReports(aggregateOptions, report1, report2)
384384
if err != nil {
385385
t.Fatalf("Error aggregating reports: %v", err)
@@ -468,7 +468,7 @@ func TestAggregateOutputs(t *testing.T) {
468468
},
469469
}
470470

471-
aggregateOptions := &aggregateOptions{reportID: "123"}
471+
aggregateOptions := &reportOptions{reportID: "123"}
472472
aggregatedReport, err := aggregateReports(aggregateOptions, report1, report2)
473473
if err != nil {
474474
t.Fatalf("Error aggregating reports: %v", err)
@@ -538,7 +538,7 @@ func TestAggregateIdenticalOutputs(t *testing.T) {
538538
},
539539
}
540540

541-
aggregateOptions := &aggregateOptions{reportID: "123"}
541+
aggregateOptions := &reportOptions{reportID: "123"}
542542
aggregatedReport, err := aggregateReports(aggregateOptions, report1, report2)
543543
if err != nil {
544544
t.Fatalf("Error aggregating reports: %v", err)
@@ -620,7 +620,7 @@ func TestAggregate_AllSkippedTests(t *testing.T) {
620620
},
621621
}
622622

623-
aggregateOptions := &aggregateOptions{reportID: "123"}
623+
aggregateOptions := &reportOptions{reportID: "123"}
624624
aggregatedReport, err := aggregateReports(aggregateOptions, report1, report2)
625625
if err != nil {
626626
t.Fatalf("Error aggregating reports: %v", err)

0 commit comments

Comments
 (0)