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

1"""Summary extraction from pytest output. 

2 

3This module provides functions to extract summary statistics from pytest output. 

4""" 

5 

6from __future__ import annotations 

7 

8import re 

9 

10from lintro.parsers.base_parser import strip_ansi_codes 

11from lintro.parsers.pytest.models import PytestSummary 

12 

13 

14def extract_pytest_summary(output: str) -> PytestSummary: 

15 """Extract test summary statistics from pytest output. 

16 

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 

23 

24 Args: 

25 output: Raw output from pytest. 

26 

27 Returns: 

28 PytestSummary: Extracted summary statistics. 

29 """ 

30 summary = PytestSummary() 

31 

32 if not output: 

33 return summary 

34 

35 # Strip ANSI color codes 

36 clean_output = strip_ansi_codes(output) 

37 

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)) 

42 

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" 

49 

50 passed_match = re.search(r"(\d+)\s+passed", clean_output) 

51 if passed_match: 

52 summary.passed = int(passed_match.group(1)) 

53 

54 failed_match = re.search(r"(\d+)\s+failed", clean_output) 

55 if failed_match: 

56 summary.failed = int(failed_match.group(1)) 

57 

58 skipped_match = re.search(r"(\d+)\s+skipped", clean_output) 

59 if skipped_match: 

60 summary.skipped = int(skipped_match.group(1)) 

61 

62 error_match = re.search(r"(\d+)\s+errors?", clean_output) 

63 if error_match: 

64 summary.error = int(error_match.group(1)) 

65 

66 xfailed_match = re.search(r"(\d+)\s+xfailed", clean_output) 

67 if xfailed_match: 

68 summary.xfailed = int(xfailed_match.group(1)) 

69 

70 xpassed_match = re.search(r"(\d+)\s+xpassed", clean_output) 

71 if xpassed_match: 

72 summary.xpassed = int(xpassed_match.group(1)) 

73 

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 ) 

83 

84 return summary