115 lines
4.7 KiB
Python
115 lines
4.7 KiB
Python
import io
|
|
import json
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from h8536.state_map_runner import (
|
|
CONNECT_FORCE_PRESETS,
|
|
StateMapEvent,
|
|
analyze_events,
|
|
format_analysis_report,
|
|
main,
|
|
parse_bench_log,
|
|
)
|
|
|
|
|
|
class StateMapRunnerTest(unittest.TestCase):
|
|
def test_force_presets_match_expected_selector_zero_words(self):
|
|
self.assertEqual(CONNECT_FORCE_PRESETS["dxc"][0], bytes.fromhex("04000040001E"))
|
|
self.assertEqual(CONNECT_FORCE_PRESETS["dxc"][1], 0x4080)
|
|
self.assertEqual(CONNECT_FORCE_PRESETS["ok"][0], bytes.fromhex("0400008000DE"))
|
|
self.assertEqual(CONNECT_FORCE_PRESETS["ok"][1], 0x8080)
|
|
self.assertEqual(CONNECT_FORCE_PRESETS["both"][0], bytes.fromhex("040000C0009E"))
|
|
self.assertEqual(CONNECT_FORCE_PRESETS["both"][1], 0xC080)
|
|
|
|
def test_analyzes_successful_selector_zero_readback(self):
|
|
events = [
|
|
StateMapEvent("rx", bytes.fromhex("0780C060205D"), timestamp_ms=1000),
|
|
StateMapEvent("tx", bytes.fromhex("0400008000DE"), timestamp_ms=1006),
|
|
StateMapEvent("tx", bytes.fromhex("01000000005B"), timestamp_ms=1060),
|
|
StateMapEvent("rx", bytes.fromhex("04001280804C"), timestamp_ms=1070),
|
|
]
|
|
|
|
analysis = analyze_events(events, expected_word=0x8080)
|
|
|
|
self.assertEqual(analysis["outcome"]["name"], "selector_zero_retained")
|
|
self.assertEqual(analysis["direct_readbacks"][0]["value"], 0x8080)
|
|
self.assertTrue(analysis["direct_readbacks"][0]["matches_expected"])
|
|
|
|
def test_warns_when_command1_readback_is_between_trigger_and_force(self):
|
|
events = [
|
|
StateMapEvent("rx", bytes.fromhex("07804040A07D"), timestamp_ms=1000),
|
|
StateMapEvent("tx", bytes.fromhex("01000000005B"), timestamp_ms=1005),
|
|
StateMapEvent("tx", bytes.fromhex("04000040001E"), timestamp_ms=1010),
|
|
]
|
|
|
|
analysis = analyze_events(events, expected_word=0x4080)
|
|
|
|
self.assertIn("command1_readback_between_trigger_and_force_can_spend_token", analysis["warnings"])
|
|
self.assertEqual(analysis["outcome"]["name"], "force_not_proven")
|
|
|
|
def test_parse_bench_log_uses_detect_lines_for_rx_and_tx_chunks_for_host_frames(self):
|
|
log = (
|
|
"00:00:01.000 RX 006 bytes 07 80 C0 60 20 5D\n"
|
|
"00:00:01.000 DETECT visible_C0_6020_family_candidate 07 80 C0 60 20 5D\n"
|
|
"00:00:01.006 TX 006 bytes 04 00 00 80 00 DE\n"
|
|
)
|
|
|
|
events = parse_bench_log(log)
|
|
|
|
self.assertEqual([(event.direction, event.frame.hex()) for event in events], [
|
|
("rx", "0780c060205d"),
|
|
("tx", "0400008000de"),
|
|
])
|
|
|
|
def test_format_report_mentions_outcome_and_readback_value(self):
|
|
analysis = analyze_events(
|
|
[
|
|
StateMapEvent("rx", bytes.fromhex("0780C060205D"), timestamp_ms=1000),
|
|
StateMapEvent("tx", bytes.fromhex("0400008000DE"), timestamp_ms=1006),
|
|
StateMapEvent("rx", bytes.fromhex("04001280804C"), timestamp_ms=1070),
|
|
],
|
|
expected_word=0x8080,
|
|
)
|
|
|
|
report = format_analysis_report(analysis)
|
|
|
|
self.assertIn("outcome=selector_zero_retained", report)
|
|
self.assertIn("value=0x8080 expected", report)
|
|
|
|
def test_cli_dry_run_prints_state_map_sequence(self):
|
|
stdout = io.StringIO()
|
|
|
|
rc = main(["--dry-run", "--preset", "dxc", "--prime-frame", "01 80 40 40 30 EB"], stdout=stdout)
|
|
|
|
output = stdout.getvalue()
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("PT2 state-map proof runner", output)
|
|
self.assertIn("force=04 00 00 40 00 1E", output)
|
|
self.assertIn("expected_e0000=0x4080", output)
|
|
self.assertIn("prime=01 80 40 40 30 EB", output)
|
|
|
|
def test_cli_analyze_log_writes_json(self):
|
|
log = (
|
|
"00:00:01.000 DETECT visible_C0_6020_family_candidate 07 80 C0 60 20 5D\n"
|
|
"00:00:01.006 TX 006 bytes 04 00 00 80 00 DE\n"
|
|
"00:00:01.070 DETECT table_readback_candidate 04 00 12 80 80 4C\n"
|
|
)
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
log_path = Path(tmpdir) / "capture.txt"
|
|
json_path = Path(tmpdir) / "analysis.json"
|
|
log_path.write_text(log, encoding="utf-8")
|
|
stdout = io.StringIO()
|
|
|
|
rc = main(["--analyze-log", str(log_path), "--preset", "ok", "--json-out", str(json_path)], stdout=stdout)
|
|
|
|
payload = json.loads(json_path.read_text(encoding="utf-8"))
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("outcome=selector_zero_retained", stdout.getvalue())
|
|
self.assertEqual(payload["outcome"]["name"], "selector_zero_retained")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|