grammar-inference-engine/tests/test_analyze.py
tobjend b92b7653e2 feat: iDRegEx refinement for CRX flat bags (Round 16)
Heuristic: only run iDRegEx when n_methods ≤ 10 AND CRX grammar has
>50% top-level optional parts (flat chain signal). If iDRegEx grammar
is >10x tighter by lang_size, use it. Otherwise keep CRX.

RAGSAK result: agents/capability (5 methods) refined from
slot?.(defaultCapabilityId+summarize)?... (lang_size=1432) to
(defaultCapabilityId|summarize) (lang_size=3) — 477x tighter.

Speed cost: ~0.7s per candidate, negligible on 74s pipeline.
CLI: --idregex-refine flag (default off).

Also adds _count_optionals() and _should_try_idregex() helpers
with 8 pytest tests. 234 tests pass.
2026-07-12 16:54:40 +02:00

247 lines
8.2 KiB
Python

"""Tests for tag-preprocessor orchestrator (analyze.py)."""
from pathlib import Path
import tempfile
import sys
sys.path.insert(0, str(Path(__file__).parent.parent))
from bex.tag_preprocessor.analyze import (
scan_directory, frequency_filter, infer, analyze_directory, _filter_glob,
_group_by_package, _count_optionals, _should_try_idregex,
)
def test_scan_directory_empty():
with tempfile.TemporaryDirectory() as td:
result = scan_directory(td)
assert result == {}
print(" PASS test_scan_directory_empty")
def test_scan_directory_groups_by_extension():
with tempfile.TemporaryDirectory() as td:
d = Path(td)
(d / "a.py").write_text("x = 1")
(d / "b.py").write_text("y = 2")
(d / "c.js").write_text("let x = 1;")
(d / "d.rs").write_text("fn main() {}")
result = scan_directory(td)
assert ".py" in result
assert ".js" in result
assert ".rs" in result
assert len(result[".py"]) == 2
assert len(result[".js"]) == 1
assert len(result[".rs"]) == 1
print(" PASS test_scan_directory_groups_by_extension")
def test_scan_directory_skips_unsupported():
with tempfile.TemporaryDirectory() as td:
d = Path(td)
(d / "f.py").write_text("x = 1")
(d / "f.txt").write_text("hello")
(d / "f.md").write_text("# doc")
result = scan_directory(td)
assert ".py" in result
assert ".txt" not in result
assert ".md" not in result
print(" PASS test_scan_directory_skips_unsupported")
def test_scan_directory_nested():
with tempfile.TemporaryDirectory() as td:
d = Path(td)
(d / "sub").mkdir()
(d / "sub" / "a.py").write_text("x = 1")
(d / "b.py").write_text("y = 2")
result = scan_directory(td)
assert len(result[".py"]) == 2
print(" PASS test_scan_directory_nested")
def test_scan_directory_skips_build_dirs():
with tempfile.TemporaryDirectory() as td:
d = Path(td)
(d / "src" / "main").mkdir(parents=True)
(d / "build" / "reports").mkdir(parents=True)
(d / "node_modules" / "pkg").mkdir(parents=True)
(d / "src" / "main" / "app.py").write_text("x = 1")
(d / "build" / "reports" / "report.js").write_text("let x = 1;")
(d / "node_modules" / "pkg" / "index.js").write_text("let y = 2;")
result = scan_directory(td)
assert ".py" in result
assert ".js" not in result
assert len(result[".py"]) == 1
print(" PASS test_scan_directory_skips_build_dirs")
def test_frequency_filter_nothing_to_filter():
sequences = [
[("function", "foo", 1), ("keyword.return", "return", 2)],
[("function", "bar", 1), ("keyword.return", "return", 2)],
]
filtered = frequency_filter(sequences, min_coverage=0.5)
assert len(filtered) == 2
assert filtered == sequences
print(" PASS test_frequency_filter_nothing_to_filter")
def test_frequency_filter_removes_infrequent_symbol():
sequences = [
[("keyword.return", "return", 1)],
[("keyword.return", "return", 1)],
[("function", "rare_fn", 1)],
]
filtered = frequency_filter(sequences, min_coverage=0.67)
assert len(filtered) == 3
assert len(filtered[0]) == 1 # "return" kept
assert len(filtered[1]) == 1 # "return" kept
assert len(filtered[2]) == 0 # "rare_fn" removed
print(" PASS test_frequency_filter_removes_infrequent_symbol")
def test_frequency_filter_edge_empty_sequences():
assert frequency_filter([], min_coverage=0.5) == []
assert frequency_filter([[], []], min_coverage=0.5) == [[], []]
print(" PASS test_frequency_filter_edge_empty_sequences")
def test_infer_returns_ensemble_dict():
with tempfile.TemporaryDirectory() as td:
d = Path(td)
(d / "a.py").write_text("def foo():\n return 1")
(d / "b.py").write_text("def bar():\n return 2")
result = infer([str(d / "a.py"), str(d / "b.py")], ".py", min_coverage=0.5)
assert isinstance(result, dict)
assert "best" in result
assert "all" in result
assert "why" in result
print(" PASS test_infer_returns_ensemble_dict")
def test_filter_glob():
files = [
"/repo/src/main/app.kt",
"/repo/src/main/org/app.kt",
"/repo/src/test/app.kt",
"/repo/build/app.kt",
]
inc = _filter_glob(files, include="**/src/main/**")
assert inc == ["/repo/src/main/app.kt", "/repo/src/main/org/app.kt"]
exc = _filter_glob(files, exclude="**/build/**")
assert exc == ["/repo/src/main/app.kt", "/repo/src/main/org/app.kt", "/repo/src/test/app.kt"]
both = _filter_glob(files, include="**/src/**", exclude="**/test/**")
assert both == ["/repo/src/main/app.kt", "/repo/src/main/org/app.kt"]
print(" PASS test_filter_glob")
def test_group_by_package():
data = [(0, "src/main"), (1, "src/main"), (2, "src/main"),
(3, "src/test"), (4, "src/test"),
(5, "docs")]
groups, ungrouped = _group_by_package(data, min_size=3)
labels = [l for l, _ in groups]
assert "src/main" in labels
assert "src" not in labels
assert "" not in labels # no root group — small packages discarded
assert len(groups) == 1
assert len(ungrouped) == 3 # src/test (2) + docs (1) → discarded
print(" PASS test_group_by_package")
def test_infer_low_coverage_filters_noise():
with tempfile.TemporaryDirectory() as td:
d = Path(td)
(d / "common.py").write_text(
"def setup():\n pass\ndef teardown():\n pass"
)
(d / "rare.py").write_text(
"def setup():\n pass\ndef one_off():\n raise Exception('boom')"
)
result = infer(
[str(d / "common.py"), str(d / "rare.py")], ".py", min_coverage=0.6
)
assert result["best"] is not None
assert result["best"]["grammar"] is not None
print(" PASS test_infer_low_coverage_filters_noise")
def test_count_optionals_flat_chain():
n_opt, n_total = _count_optionals("a?.b?.c?.d?.e?")
assert n_opt == 5
assert n_total == 5
def test_count_optionals_no_optionals():
n_opt, n_total = _count_optionals("a.b.c")
assert n_opt == 0
assert n_total == 3
def test_count_optionals_mixed():
n_opt, n_total = _count_optionals("return.error?.(request+response)?.data?")
assert n_opt == 3 # error?, (request+response)?, data?
assert n_total == 4 # return.error?.(request+response)?.data?
def test_count_optionals_repetition_not_optional():
n_opt, n_total = _count_optionals("a.b+.c?")
assert n_opt == 1 # only c? is optional
assert n_total == 3
def test_should_try_idregex_small_many_optionals():
assert _should_try_idregex("a?.b?.c?.d?.e?", 5) is True
def test_should_try_idregex_large_group():
assert _should_try_idregex("a?.b?.c?.d?.e?", 15) is False
def test_should_try_idregex_few_optionals():
assert _should_try_idregex("a.b.c.d.e", 5) is False
def test_should_short_concat():
assert _should_try_idregex("a?.b", 5) is False # too few parts
def run_all():
tests = [
test_scan_directory_empty,
test_scan_directory_groups_by_extension,
test_scan_directory_skips_unsupported,
test_scan_directory_nested,
test_scan_directory_skips_build_dirs,
test_filter_glob,
test_group_by_package,
test_frequency_filter_nothing_to_filter,
test_frequency_filter_removes_infrequent_symbol,
test_frequency_filter_edge_empty_sequences,
test_infer_returns_ensemble_dict,
test_infer_low_coverage_filters_noise,
test_count_optionals_flat_chain,
test_count_optionals_no_optionals,
test_count_optionals_mixed,
test_count_optionals_repetition_not_optional,
test_should_try_idregex_small_many_optionals,
test_should_try_idregex_large_group,
test_should_try_idregex_few_optionals,
test_should_short_concat,
]
passed = 0
failed = 0
for t in tests:
try:
t()
passed += 1
except Exception as e:
print(f" FAIL {t.__name__}: {e}")
failed += 1
print(f"\n{passed} passed, {failed} failed")
if __name__ == "__main__":
run_all()