Skip to content

Commit 8ab91a5

Browse files
authored
Refactor Flakeguard reports
1 parent 1c9eb82 commit 8ab91a5

File tree

15 files changed

+2098
-1047
lines changed

15 files changed

+2098
-1047
lines changed

tools/flakeguard/cmd/aggregate_results.go

Lines changed: 159 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,99 +2,197 @@ package cmd
22

33
import (
44
"encoding/json"
5-
"log"
6-
"os"
5+
"fmt"
76
"path/filepath"
7+
"time"
88

9+
"github.com/briandowns/spinner"
910
"github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/reports"
1011
"github.com/spf13/cobra"
1112
)
1213

13-
var (
14-
resultsFolderPath string
15-
outputResultsPath string
16-
outputLogsPath string
17-
codeOwnersPath string
18-
projectPath string
19-
maxPassRatio float64
20-
filterFailed bool
21-
)
22-
2314
var AggregateResultsCmd = &cobra.Command{
2415
Use: "aggregate-results",
25-
Short: "Aggregate test results and optionally filter failed tests based on a threshold",
16+
Short: "Aggregate test results into a single JSON report",
2617
RunE: func(cmd *cobra.Command, args []string) error {
27-
// Read test reports from files
28-
var testReports []*reports.TestReport
29-
err := filepath.Walk(resultsFolderPath, func(path string, info os.FileInfo, err error) error {
30-
if err != nil {
31-
return err
32-
}
33-
if !info.IsDir() && filepath.Ext(path) == ".json" {
34-
// Read file content
35-
data, readErr := os.ReadFile(path)
36-
if readErr != nil {
37-
return readErr
38-
}
39-
var report *reports.TestReport
40-
if jsonErr := json.Unmarshal(data, &report); jsonErr != nil {
41-
return jsonErr
42-
}
43-
testReports = append(testReports, report)
44-
}
45-
return nil
46-
})
18+
fs := reports.OSFileSystem{}
19+
20+
// Get flag values
21+
resultsPath, _ := cmd.Flags().GetString("results-path")
22+
outputDir, _ := cmd.Flags().GetString("output-path")
23+
summaryFileName, _ := cmd.Flags().GetString("summary-file-name")
24+
maxPassRatio, _ := cmd.Flags().GetFloat64("max-pass-ratio")
25+
codeOwnersPath, _ := cmd.Flags().GetString("codeowners-path")
26+
repoPath, _ := cmd.Flags().GetString("repo-path")
27+
28+
// Ensure the output directory exists
29+
if err := fs.MkdirAll(outputDir, 0755); err != nil {
30+
return fmt.Errorf("error creating output directory: %w", err)
31+
}
32+
33+
// Start spinner for loading test reports
34+
s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
35+
s.Suffix = " Loading test reports..."
36+
s.Start()
37+
38+
// Load test reports from JSON files
39+
testReports, err := reports.LoadReports(resultsPath)
4740
if err != nil {
48-
log.Fatalf("Error reading test reports: %v", err)
41+
s.Stop()
42+
return fmt.Errorf("error loading test reports: %w", err)
4943
}
44+
s.Stop()
45+
fmt.Println("Test reports loaded successfully.")
46+
47+
// Start spinner for aggregating reports
48+
s = spinner.New(spinner.CharSets[11], 100*time.Millisecond)
49+
s.Suffix = " Aggregating test reports..."
50+
s.Start()
5051

51-
allReport, err := reports.Aggregate(testReports...)
52+
// Aggregate the reports
53+
aggregatedReport, err := reports.Aggregate(testReports...)
5254
if err != nil {
53-
log.Fatalf("Error aggregating results: %v", err)
55+
s.Stop()
56+
return fmt.Errorf("error aggregating test reports: %w", err)
5457
}
58+
s.Stop()
59+
fmt.Println("Test reports aggregated successfully.")
5560

56-
// Map test results to paths
57-
err = reports.MapTestResultsToPaths(allReport, projectPath)
61+
// Start spinner for mapping test results to paths
62+
s = spinner.New(spinner.CharSets[11], 100*time.Millisecond)
63+
s.Suffix = " Mapping test results to paths..."
64+
s.Start()
65+
66+
// Map test results to test paths
67+
err = reports.MapTestResultsToPaths(aggregatedReport, repoPath)
5868
if err != nil {
59-
log.Fatalf("Error mapping test results to paths: %v", err)
69+
s.Stop()
70+
return fmt.Errorf("error mapping test results to paths: %w", err)
6071
}
72+
s.Stop()
73+
fmt.Println("Test results mapped to paths successfully.")
6174

62-
// Map test results to owners if CODEOWNERS path is provided
75+
// Map test results to code owners if codeOwnersPath is provided
6376
if codeOwnersPath != "" {
64-
err = reports.MapTestResultsToOwners(allReport, codeOwnersPath)
77+
s = spinner.New(spinner.CharSets[11], 100*time.Millisecond)
78+
s.Suffix = " Mapping test results to code owners..."
79+
s.Start()
80+
81+
err = reports.MapTestResultsToOwners(aggregatedReport, codeOwnersPath)
6582
if err != nil {
66-
log.Fatalf("Error mapping test results to owners: %v", err)
83+
s.Stop()
84+
return fmt.Errorf("error mapping test results to code owners: %w", err)
6785
}
86+
s.Stop()
87+
fmt.Println("Test results mapped to code owners successfully.")
6888
}
6989

70-
var resultsToSave []reports.TestResult
90+
// Save the aggregated report to the output directory
91+
aggregatedReportPath := filepath.Join(outputDir, "all-test-results.json")
92+
if err := reports.SaveReport(fs, aggregatedReportPath, *aggregatedReport); err != nil {
93+
return fmt.Errorf("error saving aggregated test report: %w", err)
94+
}
95+
fmt.Printf("Aggregated test report saved to %s\n", aggregatedReportPath)
96+
97+
// Filter failed tests (PassRatio < maxPassRatio and not skipped)
98+
s = spinner.New(spinner.CharSets[11], 100*time.Millisecond)
99+
s.Suffix = " Filtering failed tests..."
100+
s.Start()
101+
102+
failedTests := reports.FilterTests(aggregatedReport.Results, func(tr reports.TestResult) bool {
103+
return !tr.Skipped && tr.PassRatio < maxPassRatio
104+
})
105+
s.Stop()
106+
107+
// Check if there are any failed tests
108+
if len(failedTests) > 0 {
109+
fmt.Printf("Found %d failed test(s).\n", len(failedTests))
110+
111+
// Create a new report for failed tests with logs
112+
failedReportWithLogs := &reports.TestReport{
113+
GoProject: aggregatedReport.GoProject,
114+
TestRunCount: aggregatedReport.TestRunCount,
115+
RaceDetection: aggregatedReport.RaceDetection,
116+
ExcludedTests: aggregatedReport.ExcludedTests,
117+
SelectedTests: aggregatedReport.SelectedTests,
118+
Results: failedTests,
119+
}
120+
121+
// Save the failed tests report with logs
122+
failedTestsReportWithLogsPath := filepath.Join(outputDir, "failed-test-results-with-logs.json")
123+
if err := reports.SaveReport(fs, failedTestsReportWithLogsPath, *failedReportWithLogs); err != nil {
124+
return fmt.Errorf("error saving failed tests report with logs: %w", err)
125+
}
126+
fmt.Printf("Failed tests report with logs saved to %s\n", failedTestsReportWithLogsPath)
71127

72-
if filterFailed {
73-
// Filter to only include tests that failed below the threshold
74-
for _, result := range allReport.Results {
75-
if result.PassRatio < maxPassRatio && !result.Skipped {
76-
resultsToSave = append(resultsToSave, result)
77-
}
128+
// Remove logs from test results for the report without logs
129+
for i := range failedReportWithLogs.Results {
130+
failedReportWithLogs.Results[i].Outputs = nil
131+
failedReportWithLogs.Results[i].PackageOutputs = nil
78132
}
133+
134+
// Save the failed tests report without logs
135+
failedTestsReportNoLogsPath := filepath.Join(outputDir, "failed-test-results.json")
136+
if err := reports.SaveReport(fs, failedTestsReportNoLogsPath, *failedReportWithLogs); err != nil {
137+
return fmt.Errorf("error saving failed tests report without logs: %w", err)
138+
}
139+
fmt.Printf("Failed tests report without logs saved to %s\n", failedTestsReportNoLogsPath)
79140
} else {
80-
resultsToSave = allReport.Results
141+
fmt.Println("No failed tests found. Skipping generation of failed tests reports.")
81142
}
82-
allReport.Results = resultsToSave
83143

84-
// Output results to JSON files
85-
if len(resultsToSave) > 0 {
86-
return reports.SaveFilteredResultsAndLogs(outputResultsPath, outputLogsPath, allReport, codeOwnersPath != "")
144+
// Generate all-tests-summary.json
145+
if summaryFileName != "" {
146+
s = spinner.New(spinner.CharSets[11], 100*time.Millisecond)
147+
s.Suffix = " Generating summary json..."
148+
s.Start()
149+
150+
summaryFilePath := filepath.Join(outputDir, summaryFileName)
151+
err = generateAllTestsSummaryJSON(aggregatedReport, summaryFilePath, maxPassRatio)
152+
if err != nil {
153+
s.Stop()
154+
return fmt.Errorf("error generating summary json: %w", err)
155+
}
156+
s.Stop()
157+
fmt.Printf("Summary generated at %s\n", summaryFilePath)
87158
}
159+
160+
fmt.Println("Aggregation complete.")
161+
88162
return nil
89163
},
90164
}
91165

92166
func init() {
93-
AggregateResultsCmd.Flags().StringVarP(&resultsFolderPath, "results-path", "p", "", "Path to the folder containing JSON test result files")
94-
AggregateResultsCmd.Flags().StringVarP(&outputResultsPath, "output-results", "o", "./results", "Path to output the aggregated or filtered test results in JSON and markdown format")
95-
AggregateResultsCmd.Flags().StringVarP(&outputLogsPath, "output-logs", "l", "", "Path to output the filtered test logs in JSON format")
96-
AggregateResultsCmd.Flags().Float64VarP(&maxPassRatio, "max-pass-ratio", "m", 1.0, "The maximum (non-inclusive) pass ratio threshold for a test to be considered a failure. Any tests below this pass rate will be considered flaky.")
97-
AggregateResultsCmd.Flags().BoolVarP(&filterFailed, "filter-failed", "f", false, "If true, filter and output only failed tests based on the max-pass-ratio threshold")
98-
AggregateResultsCmd.Flags().StringVarP(&codeOwnersPath, "codeowners-path", "c", "", "Path to the CODEOWNERS file")
99-
AggregateResultsCmd.Flags().StringVarP(&projectPath, "project-path", "r", ".", "The path to the Go project. Default is the current directory. Useful for subprojects")
167+
AggregateResultsCmd.Flags().StringP("results-path", "p", "", "Path to the folder containing JSON test result files (required)")
168+
AggregateResultsCmd.Flags().StringP("output-path", "o", "./report", "Path to output the aggregated results (directory)")
169+
AggregateResultsCmd.Flags().StringP("summary-file-name", "s", "all-test-summary.json", "Name of the summary JSON file")
170+
AggregateResultsCmd.Flags().Float64P("max-pass-ratio", "", 1.0, "The maximum pass ratio threshold for a test to be considered flaky")
171+
AggregateResultsCmd.Flags().StringP("codeowners-path", "", "", "Path to the CODEOWNERS file")
172+
AggregateResultsCmd.Flags().StringP("repo-path", "", ".", "The path to the root of the repository/project")
173+
174+
AggregateResultsCmd.MarkFlagRequired("results-path")
175+
}
176+
177+
// New function to generate all-tests-summary.json
178+
func generateAllTestsSummaryJSON(report *reports.TestReport, outputPath string, maxPassRatio float64) error {
179+
summary := reports.GenerateSummaryData(report.Results, maxPassRatio)
180+
data, err := json.Marshal(summary)
181+
if err != nil {
182+
return fmt.Errorf("error marshaling summary data to JSON: %w", err)
183+
}
184+
185+
fs := reports.OSFileSystem{}
186+
jsonFile, err := fs.Create(outputPath)
187+
if err != nil {
188+
return fmt.Errorf("error creating file: %w", err)
189+
}
190+
defer jsonFile.Close()
191+
192+
_, err = jsonFile.Write(data)
193+
if err != nil {
194+
return fmt.Errorf("error writing data to file: %w", err)
195+
}
196+
197+
return nil
100198
}

tools/flakeguard/cmd/check_test_owners.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ var CheckTestOwnersCmd = &cobra.Command{
2020
Use: "check-test-owners",
2121
Short: "Check which tests in the project do not have code owners",
2222
RunE: func(cmd *cobra.Command, args []string) error {
23+
projectPath, _ := cmd.Flags().GetString("project-path")
24+
2325
// Scan project for test functions
2426
testFileMap, err := reports.ScanTestFiles(projectPath)
2527
if err != nil {
@@ -79,7 +81,7 @@ var CheckTestOwnersCmd = &cobra.Command{
7981
}
8082

8183
func init() {
82-
CheckTestOwnersCmd.Flags().StringVarP(&projectPath, "project-path", "p", ".", "Path to the root of the project")
84+
CheckTestOwnersCmd.Flags().StringP("project-path", "p", ".", "Path to the root of the project")
8385
CheckTestOwnersCmd.Flags().StringVarP(&codeownersPath, "codeowners-path", "c", ".github/CODEOWNERS", "Path to the CODEOWNERS file")
8486
CheckTestOwnersCmd.Flags().BoolVarP(&printTestFunctions, "print-test-functions", "t", false, "Print all test functions without owners")
8587
}

0 commit comments

Comments
 (0)