PCB review and src checks use native parse, constraints lookup, and review_session. Inherited validate.py and validation_tools.py stay in dependency/.
90 lines
3.0 KiB
Python
90 lines
3.0 KiB
Python
"""Tests for shortest_path, library_gate, cache_stats."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from backend.periscopex.library_gate import pintable_checksum, should_promote_extraction
|
|
from backend.periscopex.models import Component, ComponentType, DesignGraph, Net, NetType, PinConnection
|
|
from backend.periscopex.review_tools import shortest_path
|
|
from backend.services.api_logs import cache_stats_by_stage
|
|
|
|
|
|
def _graph_r_chain() -> DesignGraph:
|
|
"""U1.1 — NET_A — R1 — NET_B — U2.2"""
|
|
return DesignGraph(
|
|
components={
|
|
"U1": Component(
|
|
reference="U1", value="IC", footprint="",
|
|
component_type=ComponentType.IC, pins={"1": "NET_A"},
|
|
),
|
|
"R1": Component(
|
|
reference="R1", value="10k", footprint="",
|
|
component_type=ComponentType.RESISTOR,
|
|
pins={"1": "NET_A", "2": "NET_B"},
|
|
),
|
|
"U2": Component(
|
|
reference="U2", value="IC", footprint="",
|
|
component_type=ComponentType.IC, pins={"2": "NET_B"},
|
|
),
|
|
},
|
|
nets={
|
|
"NET_A": Net(
|
|
name="NET_A", net_type=NetType.SIGNAL,
|
|
pins=[
|
|
PinConnection(component_ref="U1", pin_number="1"),
|
|
PinConnection(component_ref="R1", pin_number="1"),
|
|
],
|
|
),
|
|
"NET_B": Net(
|
|
name="NET_B", net_type=NetType.SIGNAL,
|
|
pins=[
|
|
PinConnection(component_ref="R1", pin_number="2"),
|
|
PinConnection(component_ref="U2", pin_number="2"),
|
|
],
|
|
),
|
|
},
|
|
)
|
|
|
|
|
|
def test_shortest_path_two_hops():
|
|
g = _graph_r_chain()
|
|
msg = shortest_path(g, {}, "U1", "1", "U2", "2")
|
|
assert "hop" in msg.lower()
|
|
assert "NET_A" in msg and "NET_B" in msg
|
|
assert "U1.1" in msg and "U2.2" in msg
|
|
|
|
|
|
def test_shortest_path_same_net():
|
|
g = _graph_r_chain()
|
|
msg = shortest_path(g, {}, "U1", "1", "R1", "1")
|
|
assert "same net" in msg.lower() or "Direct" in msg
|
|
|
|
|
|
def test_library_gate_rejects_empty():
|
|
ok, reason = should_promote_extraction({"pintable": []})
|
|
assert not ok
|
|
assert "empty" in reason
|
|
|
|
|
|
def test_library_gate_accepts_named_pins():
|
|
data = {
|
|
"pintable": [
|
|
{"number": "1", "name": "VDD"},
|
|
{"number": "2", "name": "GND"},
|
|
],
|
|
}
|
|
ok, checksum = should_promote_extraction(data)
|
|
assert ok
|
|
assert checksum == pintable_checksum(data["pintable"])
|
|
|
|
|
|
def test_cache_stats_by_stage():
|
|
entries = [
|
|
{"stage": "pintable", "input_tokens": 100, "cache_read_input_tokens": 40},
|
|
{"stage": "pintable", "input_tokens": 100, "cache_read_input_tokens": 60},
|
|
{"stage": "validation", "input_tokens": 50, "cache_read_input_tokens": 0},
|
|
]
|
|
stats = cache_stats_by_stage(entries)
|
|
assert stats["pintable"]["calls"] == 2
|
|
assert stats["pintable"]["hit_ratio"] == 0.5
|
|
assert stats["validation"]["hit_ratio"] == 0.0
|