Coverage for lintro / parsers / pytest / summary_extractor.py: 94%
32 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Summary extraction from pytest output.
3This module provides functions to extract summary statistics from pytest output.
4"""
6from __future__ import annotations
8import re
10from lintro.parsers.base_parser import strip_ansi_codes
11from lintro.parsers.pytest.models import PytestSummary
14def extract_pytest_summary(output: str) -> PytestSummary:
15 """Extract test summary statistics from pytest output.
17 Parses the summary line from pytest output to extract:
18 - Number of passed tests
19 - Number of failed tests
20 - Number of skipped tests
21 - Number of error tests
22 - Execution duration
24 Args:
25 output: Raw output from pytest.
27 Returns:
28 PytestSummary: Extracted summary statistics.
29 """
30 summary = PytestSummary()
32 if not output:
33 return summary
35 # Strip ANSI color codes
36 clean_output = strip_ansi_codes(output)
38 # Extract duration first (it's always at the end)
39 duration_match = re.search(r"in\s+([\d.]+)s", clean_output)
40 if duration_match:
41 summary.duration = float(duration_match.group(1))
43 # Extract counts independently since order can vary
44 # Patterns handle various formats like:
45 # - "511 passed in 18.53s"
46 # - "509 passed, 2 failed in 18.53s"
47 # - "7 failed, 505 passed, 1 warning in 18.53s"
48 # - "510 passed, 1 skipped in 18.53s"
50 passed_match = re.search(r"(\d+)\s+passed", clean_output)
51 if passed_match:
52 summary.passed = int(passed_match.group(1))
54 failed_match = re.search(r"(\d+)\s+failed", clean_output)
55 if failed_match:
56 summary.failed = int(failed_match.group(1))
58 skipped_match = re.search(r"(\d+)\s+skipped", clean_output)
59 if skipped_match:
60 summary.skipped = int(skipped_match.group(1))
62 error_match = re.search(r"(\d+)\s+errors?", clean_output)
63 if error_match:
64 summary.error = int(error_match.group(1))
66 xfailed_match = re.search(r"(\d+)\s+xfailed", clean_output)
67 if xfailed_match:
68 summary.xfailed = int(xfailed_match.group(1))
70 xpassed_match = re.search(r"(\d+)\s+xpassed", clean_output)
71 if xpassed_match:
72 summary.xpassed = int(xpassed_match.group(1))
74 # Calculate total as sum of all test outcomes
75 summary.total = (
76 summary.passed
77 + summary.failed
78 + summary.skipped
79 + summary.error
80 + summary.xfailed
81 + summary.xpassed
82 )
84 return summary