Run tests and auto-mark failures in a test file. Args: test_path: Path to the test file mark_failure: If True, add @expectedFailure to ALL failing tests verbose: Print progress messages original_methods: If provided, only auto-mark failures for NEW methods
(
test_path: pathlib.Path,
mark_failure: bool = False,
verbose: bool = True,
original_methods: set[tuple[str, str]] | None = None,
skip_build: bool = False,
)
| 718 | |
| 719 | |
| 720 | def auto_mark_file( |
| 721 | test_path: pathlib.Path, |
| 722 | mark_failure: bool = False, |
| 723 | verbose: bool = True, |
| 724 | original_methods: set[tuple[str, str]] | None = None, |
| 725 | skip_build: bool = False, |
| 726 | ) -> tuple[int, int, int]: |
| 727 | """ |
| 728 | Run tests and auto-mark failures in a test file. |
| 729 | |
| 730 | Args: |
| 731 | test_path: Path to the test file |
| 732 | mark_failure: If True, add @expectedFailure to ALL failing tests |
| 733 | verbose: Print progress messages |
| 734 | original_methods: If provided, only auto-mark failures for NEW methods |
| 735 | (methods not in original_methods) even without mark_failure. |
| 736 | Failures in existing methods are treated as regressions. |
| 737 | |
| 738 | Returns: |
| 739 | (num_failures_added, num_successes_removed, num_regressions) |
| 740 | """ |
| 741 | test_path = pathlib.Path(test_path).resolve() |
| 742 | if not test_path.exists(): |
| 743 | raise FileNotFoundError(f"File not found: {test_path}") |
| 744 | |
| 745 | # Strip reason-less markers so those tests fail normally and we capture |
| 746 | # their error messages during the test run. |
| 747 | contents = test_path.read_text(encoding="utf-8") |
| 748 | original_contents = contents |
| 749 | contents, stripped_tests = strip_reasonless_expected_failures(contents) |
| 750 | if stripped_tests: |
| 751 | test_path.write_text(contents, encoding="utf-8") |
| 752 | |
| 753 | test_name = get_test_module_name(test_path) |
| 754 | if verbose: |
| 755 | print(f"Running test: {test_name}") |
| 756 | |
| 757 | results = run_test(test_name, skip_build=skip_build) |
| 758 | |
| 759 | # Check if test run failed entirely (e.g., import error, crash) |
| 760 | if ( |
| 761 | not results.tests_result |
| 762 | and not results.tests |
| 763 | and not results.unexpected_successes |
| 764 | ): |
| 765 | # Restore original contents before raising |
| 766 | if stripped_tests: |
| 767 | test_path.write_text(original_contents, encoding="utf-8") |
| 768 | raise TestRunError( |
| 769 | f"Test run failed for {test_name}. " |
| 770 | f"Output: {results.stdout[-500:] if results.stdout else '(no output)'}" |
| 771 | ) |
| 772 | |
| 773 | # If the run crashed (incomplete), restore original file so that markers |
| 774 | # for tests that never ran are preserved. Only observed results will be |
| 775 | # re-applied below. |
| 776 | if not results.tests_result and stripped_tests: |
| 777 | test_path.write_text(original_contents, encoding="utf-8") |