Parse a stats file to extract test statistics.
(file_path)
| 22 | |
| 23 | |
| 24 | def parse_stats_file(file_path): |
| 25 | """Parse a stats file to extract test statistics.""" |
| 26 | try: |
| 27 | with open(file_path, "r") as f: |
| 28 | content = f.read() |
| 29 | |
| 30 | # Extract the numbers using regex |
| 31 | tests_pattern = r"collected (\d+) items" |
| 32 | passed_pattern = r"(\d+) passed" |
| 33 | failed_pattern = r"(\d+) failed" |
| 34 | skipped_pattern = r"(\d+) skipped" |
| 35 | xpassed_pattern = r"(\d+) xpassed" |
| 36 | |
| 37 | tests_match = re.search(tests_pattern, content) |
| 38 | passed_match = re.search(passed_pattern, content) |
| 39 | failed_match = re.search(failed_pattern, content) |
| 40 | skipped_match = re.search(skipped_pattern, content) |
| 41 | xpassed_match = re.search(xpassed_pattern, content) |
| 42 | |
| 43 | passed = int(passed_match.group(1)) if passed_match else 0 |
| 44 | failed = int(failed_match.group(1)) if failed_match else 0 |
| 45 | skipped = int(skipped_match.group(1)) if skipped_match else 0 |
| 46 | xpassed = int(xpassed_match.group(1)) if xpassed_match else 0 |
| 47 | |
| 48 | # If tests_match exists, use it, otherwise calculate from passed/failed/skipped |
| 49 | if tests_match: |
| 50 | tests = int(tests_match.group(1)) |
| 51 | else: |
| 52 | tests = passed + failed + skipped + xpassed |
| 53 | |
| 54 | # Extract timing information if available |
| 55 | timing_pattern = r"slowest \d+ test durations[\s\S]*?\n([\s\S]*?)={70}" |
| 56 | timing_match = re.search(timing_pattern, content, re.MULTILINE) |
| 57 | slowest_tests = [] |
| 58 | |
| 59 | if timing_match: |
| 60 | timing_text = timing_match.group(1).strip() |
| 61 | test_timing_lines = timing_text.split("\n") |
| 62 | for line in test_timing_lines: |
| 63 | if line.strip(): |
| 64 | # Format is typically: 10.37s call tests/path/to/test.py::TestClass::test_method |
| 65 | parts = line.strip().split() |
| 66 | if len(parts) >= 3: |
| 67 | time_str = parts[0] |
| 68 | test_path = " ".join(parts[2:]) |
| 69 | |
| 70 | # Skip entries with "< 0.05 secs were omitted" or similar |
| 71 | if "secs were omitted" in test_path: |
| 72 | continue |
| 73 | |
| 74 | try: |
| 75 | time_seconds = float(time_str.rstrip("s")) |
| 76 | slowest_tests.append({"test": test_path, "duration": time_seconds}) |
| 77 | except ValueError: |
| 78 | pass |
| 79 | |
| 80 | return { |
| 81 | "tests": tests, |
no test coverage detected
searching dependent graphs…