test_cache_status_fields.py
python
sha256:ce542c4d0f6dacc9520c02d0e3378bf96bc377d118a7402f2bbc8af1749b08e0
feat(code-test): audit cache internals, add cache_hit/--cac…
Sonnet 5
minor
⚠ breaking
9 hours ago
| 1 | """TDD tests for muse#49 Phase 2 — cache_hit/cache_misses on SelectionResult. |
| 2 | |
| 3 | ``select_tests`` now threads a mutable ``cache_stats`` dict through |
| 4 | ``symbols_for_snapshot`` and ``build_forward_graph`` (both accept an |
| 5 | optional ``stats=`` kwarg that increments hit/miss counters in place). |
| 6 | The final ``SelectionResult`` exposes ``cache_hit`` (True only when zero |
| 7 | misses occurred across both caches) and ``cache_misses`` (the raw count). |
| 8 | |
| 9 | These tests prove the *mechanism* — that the reported values trace back to |
| 10 | real cache misses in the underlying cache objects — rather than asserting |
| 11 | only on final selection output, which would pass trivially even if the |
| 12 | stats wiring were a no-op (test-selection correctness is independent of |
| 13 | cache warmth; only performance differs). |
| 14 | """ |
| 15 | |
| 16 | from __future__ import annotations |
| 17 | |
| 18 | import logging |
| 19 | import pathlib |
| 20 | |
| 21 | import pytest |
| 22 | |
| 23 | from muse.core.callgraph_cache import CallGraphCache |
| 24 | from muse.core.object_store import write_object |
| 25 | from muse.core.paths import muse_dir |
| 26 | from muse.core.symbol_cache import SymbolCache |
| 27 | from muse.core.test_selection import ChangedSymbol, select_tests |
| 28 | from muse.core.types import Manifest, blob_id |
| 29 | |
| 30 | |
| 31 | def _make_repo(tmp_path: pathlib.Path) -> tuple[pathlib.Path, Manifest]: |
| 32 | dot_muse = muse_dir(tmp_path) |
| 33 | dot_muse.mkdir(exist_ok=True) |
| 34 | |
| 35 | prod_src = b"""\ |
| 36 | def compute(x: int) -> int: |
| 37 | return x * 2 |
| 38 | """ |
| 39 | test_src = b"""\ |
| 40 | from prod import compute |
| 41 | |
| 42 | def test_compute() -> None: |
| 43 | assert compute(2) == 4 |
| 44 | """ |
| 45 | prod_oid = blob_id(prod_src) |
| 46 | test_oid = blob_id(test_src) |
| 47 | write_object(tmp_path, prod_oid, prod_src) |
| 48 | write_object(tmp_path, test_oid, test_src) |
| 49 | |
| 50 | (tmp_path / "prod.py").write_bytes(prod_src) |
| 51 | tests_dir = tmp_path / "tests" |
| 52 | tests_dir.mkdir(exist_ok=True) |
| 53 | (tests_dir / "test_prod.py").write_bytes(test_src) |
| 54 | |
| 55 | manifest: Manifest = { |
| 56 | "prod.py": prod_oid, |
| 57 | "tests/test_prod.py": test_oid, |
| 58 | } |
| 59 | return tmp_path, manifest |
| 60 | |
| 61 | |
| 62 | class TestColdCacheReportsMiss: |
| 63 | def test_first_run_cache_hit_false_with_misses(self, tmp_path: pathlib.Path) -> None: |
| 64 | """A fully cold run (empty caches) must report cache_hit=False and |
| 65 | cache_misses > 0 — proving the counters actually observe the misses |
| 66 | that occur in symbols_for_snapshot/build_forward_graph, not a |
| 67 | hardcoded value.""" |
| 68 | root, manifest = _make_repo(tmp_path) |
| 69 | changed = [ChangedSymbol(address="prod.py::compute", change_kind="modified")] |
| 70 | |
| 71 | result = select_tests( |
| 72 | root, changed, manifest, |
| 73 | cache=SymbolCache.empty(), |
| 74 | callgraph_cache=CallGraphCache.empty(), |
| 75 | ) |
| 76 | |
| 77 | assert result["cache_hit"] is False |
| 78 | assert result["cache_misses"] > 0 |
| 79 | |
| 80 | def test_cold_run_emits_warning( |
| 81 | self, tmp_path: pathlib.Path, caplog: pytest.LogCaptureFixture |
| 82 | ) -> None: |
| 83 | """CT_03: the cold-rebuild path must emit a visible warning log line, |
| 84 | not fail silently with no feedback to the user.""" |
| 85 | root, manifest = _make_repo(tmp_path) |
| 86 | changed = [ChangedSymbol(address="prod.py::compute", change_kind="modified")] |
| 87 | |
| 88 | with caplog.at_level(logging.WARNING, logger="muse.core.test_selection"): |
| 89 | select_tests( |
| 90 | root, changed, manifest, |
| 91 | cache=SymbolCache.empty(), |
| 92 | callgraph_cache=CallGraphCache.empty(), |
| 93 | ) |
| 94 | |
| 95 | assert any("cache cold" in rec.message for rec in caplog.records), ( |
| 96 | "expected a 'cache cold' warning log on a cold-cache run" |
| 97 | ) |
| 98 | |
| 99 | |
| 100 | class TestWarmCacheReportsHit: |
| 101 | def test_second_run_cache_hit_true_zero_misses(self, tmp_path: pathlib.Path) -> None: |
| 102 | """Proves the mechanism directly: after warming both caches via a |
| 103 | first select_tests call, a second call against the SAME manifest |
| 104 | must report zero misses — i.e. the stats dict is actually wired to |
| 105 | the real cache .get() calls, not independently computed.""" |
| 106 | root, manifest = _make_repo(tmp_path) |
| 107 | changed = [ChangedSymbol(address="prod.py::compute", change_kind="modified")] |
| 108 | sym_cache = SymbolCache.empty() |
| 109 | cg_cache = CallGraphCache.empty() |
| 110 | |
| 111 | first = select_tests(root, changed, manifest, cache=sym_cache, callgraph_cache=cg_cache) |
| 112 | assert first["cache_hit"] is False # sanity: first run was indeed cold |
| 113 | |
| 114 | second = select_tests(root, changed, manifest, cache=sym_cache, callgraph_cache=cg_cache) |
| 115 | |
| 116 | assert second["cache_hit"] is True |
| 117 | assert second["cache_misses"] == 0 |
| 118 | |
| 119 | def test_warm_run_emits_no_cold_warning( |
| 120 | self, tmp_path: pathlib.Path, caplog: pytest.LogCaptureFixture |
| 121 | ) -> None: |
| 122 | root, manifest = _make_repo(tmp_path) |
| 123 | changed = [ChangedSymbol(address="prod.py::compute", change_kind="modified")] |
| 124 | sym_cache = SymbolCache.empty() |
| 125 | cg_cache = CallGraphCache.empty() |
| 126 | |
| 127 | select_tests(root, changed, manifest, cache=sym_cache, callgraph_cache=cg_cache) |
| 128 | |
| 129 | caplog.clear() |
| 130 | with caplog.at_level(logging.WARNING, logger="muse.core.test_selection"): |
| 131 | select_tests(root, changed, manifest, cache=sym_cache, callgraph_cache=cg_cache) |
| 132 | |
| 133 | assert not any("cache cold" in rec.message for rec in caplog.records) |
| 134 | |
| 135 | |
| 136 | class TestEmptyChangedAddressesShortCircuit: |
| 137 | def test_no_changed_symbols_reports_hit_true(self, tmp_path: pathlib.Path) -> None: |
| 138 | """The early-return path (no changed symbols) never touches either |
| 139 | cache, so it must report a trivial cache_hit=True, cache_misses=0 — |
| 140 | it would be misleading to claim a miss when no lookup happened.""" |
| 141 | root, manifest = _make_repo(tmp_path) |
| 142 | |
| 143 | result = select_tests(root, [], manifest) |
| 144 | |
| 145 | assert result["cache_hit"] is True |
| 146 | assert result["cache_misses"] == 0 |
File History
1 commit
sha256:ce542c4d0f6dacc9520c02d0e3378bf96bc377d118a7402f2bbc8af1749b08e0
feat(code-test): audit cache internals, add cache_hit/--cac…
Sonnet 5
minor
⚠
9 hours ago