|
1 | 1 | package cmd |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "encoding/json" |
5 | | - "log" |
6 | | - "os" |
7 | | - "path/filepath" |
| 4 | + "fmt" |
8 | 5 |
|
9 | 6 | "github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/reports" |
10 | 7 | "github.com/spf13/cobra" |
11 | 8 | ) |
12 | 9 |
|
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 | | - |
23 | 10 | var AggregateResultsCmd = &cobra.Command{ |
24 | 11 | Use: "aggregate-results", |
25 | | - Short: "Aggregate test results and optionally filter failed tests based on a threshold", |
| 12 | + Short: "Aggregate test results into a single report, with optional filtering and code owners mapping", |
26 | 13 | 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 | | - }) |
| 14 | + // Get flag values |
| 15 | + aggregateResultsPath, _ := cmd.Flags().GetString("results-path") |
| 16 | + aggregateOutputPath, _ := cmd.Flags().GetString("output-path") |
| 17 | + includeOutputs, _ := cmd.Flags().GetBool("include-outputs") |
| 18 | + includePackageOutputs, _ := cmd.Flags().GetBool("include-package-outputs") |
| 19 | + filterFailed, _ := cmd.Flags().GetBool("filter-failed") |
| 20 | + maxPassRatio, _ := cmd.Flags().GetFloat64("max-pass-ratio") |
| 21 | + codeOwnersPath, _ := cmd.Flags().GetString("codeowners-path") |
| 22 | + repoPath, _ := cmd.Flags().GetString("repo-path") |
| 23 | + |
| 24 | + // Load test reports from JSON files |
| 25 | + testReports, err := reports.LoadReports(aggregateResultsPath) |
47 | 26 | if err != nil { |
48 | | - log.Fatalf("Error reading test reports: %v", err) |
| 27 | + return fmt.Errorf("error loading test reports: %w", err) |
49 | 28 | } |
50 | 29 |
|
51 | | - allReport, err := reports.Aggregate(testReports...) |
| 30 | + // Aggregate the reports |
| 31 | + aggregatedReport, err := reports.Aggregate(testReports...) |
52 | 32 | if err != nil { |
53 | | - log.Fatalf("Error aggregating results: %v", err) |
| 33 | + return fmt.Errorf("error aggregating test reports: %w", err) |
54 | 34 | } |
55 | 35 |
|
56 | | - // Map test results to paths |
57 | | - err = reports.MapTestResultsToPaths(allReport, projectPath) |
| 36 | + // Map test results to test paths |
| 37 | + err = reports.MapTestResultsToPaths(aggregatedReport, repoPath) |
58 | 38 | if err != nil { |
59 | | - log.Fatalf("Error mapping test results to paths: %v", err) |
| 39 | + return fmt.Errorf("error mapping test results to paths: %w", err) |
60 | 40 | } |
61 | 41 |
|
62 | | - // Map test results to owners if CODEOWNERS path is provided |
| 42 | + // Map test results to code owners if codeOwnersPath is provided |
63 | 43 | if codeOwnersPath != "" { |
64 | | - err = reports.MapTestResultsToOwners(allReport, codeOwnersPath) |
| 44 | + err = reports.MapTestResultsToOwners(aggregatedReport, codeOwnersPath) |
65 | 45 | if err != nil { |
66 | | - log.Fatalf("Error mapping test results to owners: %v", err) |
| 46 | + return fmt.Errorf("error mapping test results to code owners: %w", err) |
67 | 47 | } |
68 | 48 | } |
69 | 49 |
|
70 | | - var resultsToSave []reports.TestResult |
71 | | - |
| 50 | + // Filter results if needed |
72 | 51 | 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) |
| 52 | + aggregatedReport.Results = reports.FilterTests(aggregatedReport.Results, func(tr reports.TestResult) bool { |
| 53 | + return !tr.Skipped && tr.PassRatio < maxPassRatio |
| 54 | + }) |
| 55 | + } |
| 56 | + |
| 57 | + // Process the aggregated results based on the flags |
| 58 | + if !includeOutputs || !includePackageOutputs { |
| 59 | + for i := range aggregatedReport.Results { |
| 60 | + if !includeOutputs { |
| 61 | + aggregatedReport.Results[i].Outputs = nil |
| 62 | + } |
| 63 | + if !includePackageOutputs { |
| 64 | + aggregatedReport.Results[i].PackageOutputs = nil |
77 | 65 | } |
78 | 66 | } |
79 | | - } else { |
80 | | - resultsToSave = allReport.Results |
81 | 67 | } |
82 | | - allReport.Results = resultsToSave |
83 | 68 |
|
84 | | - // Output results to JSON files |
85 | | - if len(resultsToSave) > 0 { |
86 | | - return reports.SaveFilteredResultsAndLogs(outputResultsPath, outputLogsPath, allReport, codeOwnersPath != "") |
| 69 | + // Save the aggregated report |
| 70 | + if err := reports.SaveReport(reports.OSFileSystem{}, aggregateOutputPath, *aggregatedReport); err != nil { |
| 71 | + return fmt.Errorf("error saving aggregated report: %w", err) |
87 | 72 | } |
| 73 | + |
| 74 | + fmt.Printf("Aggregated report saved to %s\n", aggregateOutputPath) |
88 | 75 | return nil |
89 | 76 | }, |
90 | 77 | } |
91 | 78 |
|
92 | 79 | 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") |
| 80 | + AggregateResultsCmd.Flags().StringP("results-path", "p", "", "Path to the folder containing JSON test result files (required)") |
| 81 | + AggregateResultsCmd.Flags().StringP("output-path", "o", "./aggregated-results.json", "Path to output the aggregated test results") |
| 82 | + AggregateResultsCmd.Flags().Bool("include-outputs", false, "Include test outputs in the aggregated test results") |
| 83 | + AggregateResultsCmd.Flags().Bool("include-package-outputs", false, "Include test package outputs in the aggregated test results") |
| 84 | + AggregateResultsCmd.Flags().Bool("filter-failed", false, "If true, filter and output only failed tests based on the max-pass-ratio threshold") |
| 85 | + AggregateResultsCmd.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.") |
| 86 | + AggregateResultsCmd.Flags().String("codeowners-path", "", "Path to the CODEOWNERS file") |
| 87 | + AggregateResultsCmd.Flags().String("repo-path", ".", "The path to the root of the repository/project") |
| 88 | + AggregateResultsCmd.MarkFlagRequired("results-path") |
| 89 | + AggregateResultsCmd.MarkFlagRequired("repo-path") |
100 | 90 | } |
0 commit comments