| 221 | |
| 222 | |
| 223 | def generate_error_report(results: list[dict]) -> None: |
| 224 | failed_results = [result for result in results if result["returncode"] != 0] |
| 225 | if not failed_results: |
| 226 | return |
| 227 | |
| 228 | logger.info("\n%s", "=" * 60) |
| 229 | logger.info("Test Error Report") |
| 230 | logger.info("=" * 60) |
| 231 | for index, result in enumerate(failed_results, start=1): |
| 232 | output = "\n".join(part for part in (result["stdout"], result["stderr"]) if part) |
| 233 | logger.info("\n%s. File: %s", index, result["file"]) |
| 234 | logger.info("-" * 40) |
| 235 | |
| 236 | error_lines: list[str] = [] |
| 237 | capture_error = False |
| 238 | for line in output.splitlines(): |
| 239 | stripped = line.strip() |
| 240 | if stripped.startswith("=") and ("ERROR" in line or "FAIL" in line): |
| 241 | capture_error = True |
| 242 | error_lines.append(line) |
| 243 | elif stripped.startswith("=== short test summary"): |
| 244 | error_lines.append(line) |
| 245 | break |
| 246 | elif capture_error: |
| 247 | error_lines.append(line) |
| 248 | |
| 249 | if not error_lines: |
| 250 | capture_traceback = False |
| 251 | for line in output.splitlines(): |
| 252 | if "Traceback" in line: |
| 253 | capture_traceback = True |
| 254 | if capture_traceback: |
| 255 | error_lines.append(line) |
| 256 | if len(error_lines) > 15: |
| 257 | error_lines.append("... (truncated) ...") |
| 258 | break |
| 259 | |
| 260 | if not error_lines: |
| 261 | output_lines = output.splitlines() |
| 262 | error_lines = output_lines[-10:] if len(output_lines) > 10 else output_lines |
| 263 | |
| 264 | for line in error_lines: |
| 265 | logger.info(line) |
| 266 | |
| 267 | logger.info("\n%s", "=" * 60) |
| 268 | logger.info("Total failed test files: %s", len(failed_results)) |
| 269 | logger.info("=" * 60) |
| 270 | |
| 271 | |
| 272 | def _combine_coverage(current_dir: Path, project_root: Path) -> bool: |