"""Tests for common.test_map_loader."""
from __future__ import annotations
import json
from datetime import date
from pathlib import Path
import pytest
from scripts.helpers._config import Config, ConfigError
from scripts.helpers.ci_gate.models import SourceExemption
from scripts.helpers.ci_gate.policy import is_exempt
from scripts.helpers.ci_gate.test_map_query import prune_deleted_sources
from scripts.helpers.common.test_map_loader import (
assess_test_map_freshness,
is_product_source,
load_baseline,
load_test_map,
load_test_map_with_commit,
validate_test_map_freshness,
)
from tests.regression.scripts.helpers.gate_policy_writer import write_gate_policy
_LEGACY_MAP_JSON = json.dumps(
{
"schema_version": 1,
"map": {
"tensor_cast/foo.py": {
"bar": ["tests/smoke/test_bar.py::test_x"],
},
"cli/main.py": {
"run": ["tests/regression/cli/test_run.py::test_run"],
},
},
}
)
_NODE_MAP_JSON = json.dumps(
{
"schema_version": 1,
"built_from_commit": "abc123",
"map": {
"tests/smoke/test_bar.py::test_x": {
"tensor_cast/foo.py": ["bar"],
},
"tests/regression/cli/test_run.py::test_run": {
"cli/main.py": ["run"],
},
},
}
)
def test_load_test_map_rejects_source_oriented_map(tmp_path: Path) -> None:
map_path = tmp_path / "map.json"
map_path.write_text(_LEGACY_MAP_JSON, encoding="utf-8")
with pytest.raises(ConfigError, match="pytest node id"):
load_test_map(_cfg_with_path(str(map_path)))
def test_load_test_map_accepts_decorator_suffix_symbol(tmp_path: Path) -> None:
map_path = tmp_path / "map.json"
map_path.write_text(
json.dumps(
{
"schema_version": 1,
"map": {
"tests/regression/cli/test_run.py::test_run": {
"cli/main.py": ["_@_decorator(torch.ops.foo.bar)"],
},
},
}
),
encoding="utf-8",
)
result = load_test_map(_cfg_with_path(str(map_path)))
assert result["tests/regression/cli/test_run.py::test_run"]["cli/main.py"] == ["_@_decorator(torch.ops.foo.bar)"]
def test_load_test_map_rejects_legacy_dot_symbol(tmp_path: Path) -> None:
map_path = tmp_path / "map.json"
map_path.write_text(
json.dumps(
{
"schema_version": 1,
"map": {
"tests/regression/cli/test_run.py::test_run": {
"cli/main.py": ["Widget.run"],
},
},
}
),
encoding="utf-8",
)
with pytest.raises(ConfigError, match="canonical Class::method"):
load_test_map(_cfg_with_path(str(map_path)))
def test_load_test_map_v2_returns_node_oriented_map(tmp_path: Path) -> None:
map_path = tmp_path / "map.json"
map_path.write_text(_NODE_MAP_JSON, encoding="utf-8")
result = load_test_map(_cfg_with_path(str(map_path)))
assert result["tests/regression/cli/test_run.py::test_run"]["cli/main.py"] == ["run"]
def test_load_test_map_unsupported_schema_version_raises_config_error(
tmp_path: Path,
) -> None:
map_path = tmp_path / "map.json"
map_path.write_text(json.dumps({"schema_version": 9, "map": {}}), encoding="utf-8")
with pytest.raises(ConfigError, match="unsupported schema_version"):
load_test_map(_cfg_with_path(str(map_path)))
def test_load_test_map_invalid_json_raises_config_error_with_path(
tmp_path: Path,
) -> None:
map_path = tmp_path / "map.json"
map_path.write_text("{invalid", encoding="utf-8")
with pytest.raises(ConfigError, match=f"invalid JSON at {map_path}:"):
load_test_map(_cfg_with_path(str(map_path)))
def test_load_test_map_invalid_test_node_raises_config_error(tmp_path: Path) -> None:
payload = json.dumps({"schema_version": 1, "map": {"not-a-test-node": {"cli/main.py": ["run"]}}})
map_path = tmp_path / "map.json"
map_path.write_text(payload, encoding="utf-8")
with pytest.raises(ConfigError, match="pytest node id"):
load_test_map(_cfg_with_path(str(map_path)))
def test_load_test_map_with_commit_reads_built_from_commit(tmp_path: Path) -> None:
map_path = tmp_path / "map.json"
map_path.write_text(_NODE_MAP_JSON, encoding="utf-8")
mapping, commit = load_test_map_with_commit(_cfg_with_path(str(map_path)))
assert commit == "abc123"
assert "tests/regression/cli/test_run.py::test_run" in mapping
def test_validate_test_map_freshness_rejects_missing_commit(tmp_path: Path) -> None:
with pytest.raises(ConfigError, match="built_from_commit is required"):
validate_test_map_freshness(tmp_path, None, "abc123")
def test_assess_test_map_freshness_warns_when_map_is_ancestor_of_merge_base(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
calls: list[tuple[str, str, str]] = []
def _fake_ancestor(_repo: Path, ancestor: str, descendant: str) -> bool:
calls.append((ancestor, descendant, "check"))
return ancestor == "old" and descendant == "merge"
monkeypatch.setattr("scripts.helpers.common.test_map_loader.is_git_ancestor", _fake_ancestor)
result = assess_test_map_freshness(tmp_path, "old", "merge")
assert result.block_message is None
assert result.warn_message is not None
def test_load_baseline_assembles_test_map_and_policy(tmp_path: Path) -> None:
map_path = tmp_path / "map.json"
map_path.write_text(_NODE_MAP_JSON, encoding="utf-8")
repo = tmp_path / "repo"
write_gate_policy(repo, approvers=("fangkai",))
baseline, commit = load_baseline(repo, _cfg_with_path(str(map_path)))
assert commit == "abc123"
assert baseline.test_map["tests/regression/cli/test_run.py::test_run"]["cli/main.py"] == ["run"]
assert baseline.roots
assert baseline.discovery.include_patterns
def test_is_exempt_matching_file_and_symbol_returns_true() -> None:
exemptions = (
SourceExemption(
file="a.py",
symbol="fn",
reason="",
applicant="",
approver="fangkai",
deadline=date(2099, 12, 31),
),
)
assert is_exempt(exemptions, "a.py", "fn") is True
def test_is_product_source_matching_prefix_returns_true() -> None:
assert is_product_source("cli/main.py", ("cli/",)) is True
def test_prune_removes_deleted_source_paths_from_test_nodes() -> None:
tm = {
"tests/a.py::test_x": {"a.py": ["fn"], "b.py": ["fn"]},
}
result = prune_deleted_sources(tm, ("a.py",))
assert result == {"tests/a.py::test_x": {"b.py": ["fn"]}}
def _cfg_with_path(path: str) -> Config:
return Config(
test_map_path=path,
base_branch="master",
line_threshold=70.0,
branch_threshold=50.0,
benchmark_parallel=False,
feishu_webhook_url="",
msmodeling_cache=".msmodeling_cache",
weights_prune=True,
)