Consolidate test reports from multiple test runs, including from subdirectories.
(reports_dir)
| 238 | |
| 239 | |
| 240 | def consolidate_reports(reports_dir): |
| 241 | """Consolidate test reports from multiple test runs, including from subdirectories.""" |
| 242 | # Get all stats files, including those in subdirectories |
| 243 | stats_files = glob.glob(f"{reports_dir}/**/*_stats.txt", recursive=True) |
| 244 | |
| 245 | results = {} |
| 246 | total_stats = {"tests": 0, "passed": 0, "failed": 0, "skipped": 0} |
| 247 | |
| 248 | # Collect all slow tests across all test suites |
| 249 | all_slow_tests = [] |
| 250 | |
| 251 | # Process each stats file and its corresponding failures file |
| 252 | for stats_file in stats_files: |
| 253 | # Extract test suite name from filename (e.g., tests_pipeline_allegro_cuda_stats.txt -> pipeline_allegro_cuda) |
| 254 | base_name = os.path.basename(stats_file).replace("_stats.txt", "") |
| 255 | |
| 256 | # Include parent directory in suite name if it's in a subdirectory |
| 257 | rel_path = os.path.relpath(os.path.dirname(stats_file), reports_dir) |
| 258 | if rel_path and rel_path != ".": |
| 259 | # Remove 'test_reports' suffix from directory name if present |
| 260 | dir_name = os.path.basename(rel_path) |
| 261 | if dir_name.endswith("_test_reports"): |
| 262 | dir_name = dir_name[:-13] # Remove '_test_reports' suffix |
| 263 | base_name = f"{dir_name}/{base_name}" |
| 264 | |
| 265 | # Parse stats |
| 266 | stats = parse_stats_file(stats_file) |
| 267 | |
| 268 | # If no slowest tests found in stats file, try the durations file directly |
| 269 | if not stats.get("slowest_tests"): |
| 270 | stats["slowest_tests"] = parse_durations_file(stats_file) |
| 271 | |
| 272 | # Update total stats |
| 273 | for key in ["tests", "passed", "failed", "skipped"]: |
| 274 | total_stats[key] += stats[key] |
| 275 | |
| 276 | # Collect slowest tests with their suite name |
| 277 | for slow_test in stats.get("slowest_tests", []): |
| 278 | all_slow_tests.append({"test": slow_test["test"], "duration": slow_test["duration"], "suite": base_name}) |
| 279 | |
| 280 | # Parse failures if there are any |
| 281 | failures = [] |
| 282 | if stats["failed"] > 0: |
| 283 | # First try to get test paths from summary_short.txt which has the best format |
| 284 | summary_file = stats_file.replace("_stats.txt", "_summary_short.txt") |
| 285 | if os.path.exists(summary_file): |
| 286 | try: |
| 287 | with open(summary_file, "r") as f: |
| 288 | content = f.read() |
| 289 | # Look for full lines with test path and error message: "FAILED test_path - error_msg" |
| 290 | failed_test_lines = re.findall( |
| 291 | r"FAILED\s+(tests/[\w/]+\.py::[A-Za-z0-9_\.]+::[A-Za-z0-9_]+)(?:\s+-\s+(.+))?", content |
| 292 | ) |
| 293 | |
| 294 | if failed_test_lines: |
| 295 | for match in failed_test_lines: |
| 296 | test_path = match[0] |
| 297 | error_msg = match[1] if len(match) > 1 and match[1] else "No error message" |
no test coverage detected
searching dependent graphs…