from fnmatch import fnmatch from .paths import is_excluded_path, iter_source_files CS_SUFFIXES = frozenset({".cs"}) def is_test_file(path, config): return any(path.is_relative_to(root) for root in config.test_roots) def evaluate_paths(paths, root, config): root = root.resolve() candidates = None for raw in paths: path = raw.resolve() if not path.is_relative_to(root): return f"TDD GUARD: '{path}' is outside the repository" if path.suffix.lower() not in CS_SUFFIXES: continue if is_excluded_path(path, root, config.exclude) or is_test_file(path, config): continue if candidates is None: candidates = tuple(p for p in iter_source_files(root) if p.suffix.lower() == ".cs") roots = (*config.test_roots, path.parent / "tests", path.parent / "test") names = tuple(pattern.format(stem=path.stem) for pattern in config.test_patterns) found = any( candidate.is_relative_to(test_root) and any(fnmatch(candidate.name if "/" not in pattern else candidate.relative_to(test_root).as_posix(), pattern) for pattern in names) for test_root in roots for candidate in candidates ) if not found: return (f"TDD GUARD: '{path.name}' requires an existing test such as '{names[0]}'. " "Add the test in a configured test root first.") return None