Run tests and auto-mark failures in a test directory. Runs the test once for the whole directory, then applies results to each file. Args: test_dir: Path to the test directory mark_failure: If True, add @expectedFailure to ALL failing tests verbose: Print progr
(
test_dir: pathlib.Path,
mark_failure: bool = False,
verbose: bool = True,
original_methods_per_file: dict[pathlib.Path, set[tuple[str, str]]] | None = None,
skip_build: bool = False,
)
| 840 | |
| 841 | |
| 842 | def auto_mark_directory( |
| 843 | test_dir: pathlib.Path, |
| 844 | mark_failure: bool = False, |
| 845 | verbose: bool = True, |
| 846 | original_methods_per_file: dict[pathlib.Path, set[tuple[str, str]]] | None = None, |
| 847 | skip_build: bool = False, |
| 848 | ) -> tuple[int, int, int]: |
| 849 | """ |
| 850 | Run tests and auto-mark failures in a test directory. |
| 851 | |
| 852 | Runs the test once for the whole directory, then applies results to each file. |
| 853 | |
| 854 | Args: |
| 855 | test_dir: Path to the test directory |
| 856 | mark_failure: If True, add @expectedFailure to ALL failing tests |
| 857 | verbose: Print progress messages |
| 858 | original_methods_per_file: If provided, only auto-mark failures for NEW methods |
| 859 | even without mark_failure. Dict maps file path to |
| 860 | set of (class_name, method_name) tuples. |
| 861 | |
| 862 | Returns: |
| 863 | (num_failures_added, num_successes_removed, num_regressions) |
| 864 | """ |
| 865 | test_dir = pathlib.Path(test_dir).resolve() |
| 866 | if not test_dir.exists(): |
| 867 | raise FileNotFoundError(f"Directory not found: {test_dir}") |
| 868 | if not test_dir.is_dir(): |
| 869 | raise ValueError(f"Not a directory: {test_dir}") |
| 870 | |
| 871 | # Get all .py files in directory |
| 872 | test_files = sorted(test_dir.glob("**/*.py")) |
| 873 | |
| 874 | # Strip reason-less markers from ALL files before running tests so those |
| 875 | # tests fail normally and we capture their error messages. |
| 876 | stripped_per_file: dict[pathlib.Path, set[tuple[str, str]]] = {} |
| 877 | original_per_file: dict[pathlib.Path, str] = {} |
| 878 | for test_file in test_files: |
| 879 | contents = test_file.read_text(encoding="utf-8") |
| 880 | stripped_contents, stripped = strip_reasonless_expected_failures(contents) |
| 881 | if stripped: |
| 882 | original_per_file[test_file] = contents |
| 883 | test_file.write_text(stripped_contents, encoding="utf-8") |
| 884 | stripped_per_file[test_file] = stripped |
| 885 | |
| 886 | test_name = get_test_module_name(test_dir) |
| 887 | if verbose: |
| 888 | print(f"Running test: {test_name}") |
| 889 | |
| 890 | results = run_test(test_name, skip_build=skip_build) |
| 891 | |
| 892 | # Check if test run failed entirely (e.g., import error, crash) |
| 893 | if ( |
| 894 | not results.tests_result |
| 895 | and not results.tests |
| 896 | and not results.unexpected_successes |
| 897 | ): |
| 898 | # Restore original contents before raising |
| 899 | for fpath, original in original_per_file.items(): |