modify framework
This commit is contained in:
+67
-51
@@ -4,19 +4,9 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SOURCE_SUFFIXES = {".ts", ".tsx", ".js", ".jsx"}
|
||||
TEST_SUFFIXES = ("ts", "tsx", "js", "jsx")
|
||||
CONFIG_SUFFIXES = {".json", ".css", ".scss", ".md", ".yml", ".yaml"}
|
||||
NEXT_SPECIAL_FILES = {
|
||||
"layout.ts",
|
||||
"layout.tsx",
|
||||
"page.ts",
|
||||
"page.tsx",
|
||||
"loading.tsx",
|
||||
"error.tsx",
|
||||
"not-found.tsx",
|
||||
"globals.css",
|
||||
}
|
||||
SOURCE_SUFFIXES = {".h", ".hpp", ".hh", ".hxx", ".c", ".cc", ".cpp", ".cxx", ".ixx"}
|
||||
TEST_SUFFIXES = {".h", ".hpp", ".hh", ".hxx", ".c", ".cc", ".cpp", ".cxx", ".ixx"}
|
||||
CONFIG_SUFFIXES = {".json", ".md", ".yml", ".yaml", ".txt", ".cmake"}
|
||||
|
||||
|
||||
def _repo_root(cwd: Path) -> Path:
|
||||
@@ -72,13 +62,66 @@ def _normalize(path_text: str) -> str:
|
||||
def _is_test_path(path_text: str) -> bool:
|
||||
normalized = _normalize(path_text)
|
||||
name = normalized.rsplit("/", 1)[-1]
|
||||
path = Path(path_text)
|
||||
return (
|
||||
"__tests__/" in normalized
|
||||
"/tests/" in f"/{normalized}"
|
||||
or "/test/" in f"/{normalized}"
|
||||
or name.endswith("_test.cpp")
|
||||
or name.startswith("test_")
|
||||
or ".test." in name
|
||||
or ".spec." in name
|
||||
or "test" in name
|
||||
or "spec" in name
|
||||
)
|
||||
) and path.suffix.lower() in TEST_SUFFIXES
|
||||
|
||||
|
||||
def _token(text: str) -> str:
|
||||
return "".join(ch for ch in text.lower() if ch.isalnum())
|
||||
|
||||
|
||||
def _module_token(path: Path) -> str:
|
||||
parts = [part.lower() for part in path.parts]
|
||||
for marker in ("include", "src"):
|
||||
if marker not in parts:
|
||||
continue
|
||||
idx = parts.index(marker)
|
||||
if marker == "include" and idx + 2 < len(parts) and parts[idx + 1] == "fesa":
|
||||
return _token(parts[idx + 2])
|
||||
if marker == "src" and idx + 1 < len(parts):
|
||||
return _token(parts[idx + 1])
|
||||
return ""
|
||||
|
||||
|
||||
def _related_tokens(path: Path) -> set[str]:
|
||||
tokens = {_token(_base_name(path))}
|
||||
module = _module_token(path)
|
||||
if module:
|
||||
tokens.add(module)
|
||||
return {token for token in tokens if token}
|
||||
|
||||
|
||||
def _candidate_test_paths(paths: list[str], cwd: Path, root: Path) -> list[Path]:
|
||||
candidates: list[Path] = []
|
||||
for path_text in paths:
|
||||
resolved = _resolve_path(path_text, cwd)
|
||||
if _is_test_path(str(resolved)):
|
||||
candidates.append(resolved)
|
||||
|
||||
for test_root_name in ("tests", "test"):
|
||||
test_root = root / test_root_name
|
||||
if not test_root.is_dir():
|
||||
continue
|
||||
for suffix in TEST_SUFFIXES:
|
||||
candidates.extend(test_root.rglob(f"*{suffix}"))
|
||||
|
||||
return candidates
|
||||
|
||||
|
||||
def _has_related_test(path: Path, candidate_tests: list[Path]) -> bool:
|
||||
tokens = _related_tokens(path)
|
||||
for test_path in candidate_tests:
|
||||
test_token = _token(test_path.stem)
|
||||
if any(token and token in test_token for token in tokens):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _is_exempt(path_text: str) -> bool:
|
||||
@@ -86,17 +129,13 @@ def _is_exempt(path_text: str) -> bool:
|
||||
path = Path(path_text)
|
||||
name = path.name.lower()
|
||||
|
||||
if _is_test_path(path_text):
|
||||
if name == "cmakelists.txt":
|
||||
return True
|
||||
if name in NEXT_SPECIAL_FILES:
|
||||
if _is_test_path(path_text):
|
||||
return True
|
||||
if path.suffix.lower() in CONFIG_SUFFIXES:
|
||||
return True
|
||||
if ".env" in name or ".config." in name:
|
||||
return True
|
||||
if any(token in name for token in ("tailwind", "postcss", "next.config", "tsconfig")):
|
||||
return True
|
||||
if "/types/" in normalized or name in {"types.ts", "types.d.ts"}:
|
||||
if "/cmake/" in normalized:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -110,38 +149,15 @@ def _resolve_path(path_text: str, cwd: Path) -> Path:
|
||||
|
||||
|
||||
def _base_name(path: Path) -> str:
|
||||
for suffix in (".tsx", ".ts", ".jsx", ".js"):
|
||||
if path.name.endswith(suffix):
|
||||
for suffix in sorted(SOURCE_SUFFIXES, key=len, reverse=True):
|
||||
if path.name.lower().endswith(suffix):
|
||||
return path.name[: -len(suffix)]
|
||||
return path.stem
|
||||
|
||||
|
||||
def _has_existing_test(path: Path, root: Path) -> bool:
|
||||
directory = path.parent
|
||||
parent = directory.parent
|
||||
base = _base_name(path)
|
||||
|
||||
for ext in TEST_SUFFIXES:
|
||||
if (directory / f"{base}.test.{ext}").exists():
|
||||
return True
|
||||
if (directory / f"{base}.spec.{ext}").exists():
|
||||
return True
|
||||
|
||||
for ext in TEST_SUFFIXES:
|
||||
if (parent / "__tests__" / f"{base}.test.{ext}").exists():
|
||||
return True
|
||||
if (directory / "__tests__" / f"{base}.test.{ext}").exists():
|
||||
return True
|
||||
|
||||
for ext in TEST_SUFFIXES:
|
||||
if (root / "src" / "__tests__" / f"{base}.test.{ext}").exists():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _guarded_paths(paths: list[str], cwd: Path, root: Path) -> list[str]:
|
||||
missing_tests: list[str] = []
|
||||
candidate_tests = _candidate_test_paths(paths, cwd, root)
|
||||
for path_text in paths:
|
||||
if _is_exempt(path_text):
|
||||
continue
|
||||
@@ -149,7 +165,7 @@ def _guarded_paths(paths: list[str], cwd: Path, root: Path) -> list[str]:
|
||||
path = _resolve_path(path_text, cwd)
|
||||
if path.suffix.lower() not in SOURCE_SUFFIXES:
|
||||
continue
|
||||
if not _has_existing_test(path, root):
|
||||
if not _has_related_test(path, candidate_tests):
|
||||
missing_tests.append(_base_name(path))
|
||||
|
||||
return missing_tests
|
||||
|
||||
Reference in New Issue
Block a user