1
0

More decompiling work

This commit is contained in:
Aiden
2026-05-25 17:32:00 +10:00
parent 56829b6e0b
commit 07f48c76e0
22 changed files with 9837 additions and 5 deletions

View File

@@ -0,0 +1,154 @@
import io
import json
import tempfile
import unittest
from pathlib import Path
from h8536.protocol_trace import (
checksum_for,
decode_trace,
format_text_report,
main,
parse_byte_text,
)
def frame(prefix: list[int]) -> list[int]:
return prefix + [checksum_for(prefix)]
class ProtocolTraceTest(unittest.TestCase):
def test_decodes_six_byte_frame_fields_and_checksum(self):
decoded = decode_trace(frame([0x08, 0x85, 0x34, 0x12, 0xAB]), direction="rx")
only = decoded["frames"][0]
self.assertTrue(only["checksum"]["valid"])
self.assertEqual(only["direction"], "rx")
self.assertEqual(only["command"]["value"], 0)
self.assertEqual(only["index"]["byte1_low3"], 5)
self.assertEqual(only["index"]["combined"], 0x0534)
self.assertEqual(only["payload_value"]["word_be"], 0x12AB)
self.assertEqual(only["payload_value"]["word_le"], 0xAB12)
def test_reports_bad_checksum_and_trailing_bytes(self):
decoded = decode_trace([0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x99])
only = decoded["frames"][0]
self.assertFalse(only["checksum"]["valid"])
self.assertEqual(only["checksum"]["expected"], checksum_for([1, 2, 3, 4, 5]))
self.assertEqual(decoded["trailing_bytes"], ["0x99"])
def test_loads_semantic_command_names_from_decompiler_json(self):
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "rom.json"
path.write_text(
json.dumps(
{
"serial_semantics": {
"protocol_semantics": [
{
"command_effects": [
{
"command_value": 1,
"name_candidate": "read_value",
}
],
"response_schema": [],
}
]
}
}
),
encoding="utf-8",
)
decoded = decode_trace(frame([0x09, 0, 1, 2, 3]), semantics_path=path)
self.assertTrue(decoded["semantics"]["loaded"])
self.assertEqual(decoded["frames"][0]["command"]["name_candidate"], "read_value")
def test_marks_rx_cmd_7_as_retransmit_or_error_candidate_with_previous_frame(self):
prior = frame([0x01, 0, 0, 0, 0])
retry = frame([0x07, 0, 0, 0, 0])
decoded = decode_trace(prior + retry, direction="rx")
annotation = decoded["frames"][1]["stateful_annotations"][0]
self.assertEqual(annotation["kind"], "retransmit_or_error_candidate")
self.assertEqual(annotation["previous_valid_same_direction"]["frame_index"], 0)
def test_decodes_observed_tx_frames_as_reports(self):
samples = [
([0x00, 0x00, 0x00, 0x00, 0x80, 0xDA], 0x0000, 0x0080, "heartbeat_alive_candidate", None),
([0x00, 0x00, 0x15, 0x80, 0x00, 0xCF], 0x0015, 0x8000, "call_button_candidate", "active"),
([0x00, 0x00, 0x15, 0x00, 0x00, 0x4F], 0x0015, 0x0000, "call_button_candidate", "inactive"),
([0x00, 0x00, 0x07, 0x80, 0x00, 0xDD], 0x0007, 0x8000, "cam_power_button_candidate", "active"),
]
decoded = decode_trace([byte for sample, *_ in samples for byte in sample], direction="tx")
for actual, (_, index, value, name, state) in zip(decoded["frames"], samples):
self.assertTrue(actual["checksum"]["valid"])
self.assertFalse(actual["command"]["applicable"])
self.assertIsNone(actual["command"]["name_candidate"])
self.assertEqual(actual["response_schema_candidates"], [])
self.assertEqual(actual["stateful_annotations"], [])
self.assertEqual(actual["report"]["index"], index)
self.assertEqual(actual["report"]["value"], value)
self.assertEqual(actual["report"]["observed_candidate"]["name_candidate"], name)
self.assertEqual(actual["report"]["observed_candidate"].get("state_candidate"), state)
def test_tx_text_report_does_not_render_cmd_or_set_value_acked(self):
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "rom.json"
path.write_text(
json.dumps(
{
"serial_semantics": {
"command_effects": [
{
"command_value": 0,
"name_candidate": "set_value_acked",
}
]
}
}
),
encoding="utf-8",
)
decoded = decode_trace(
[0x00, 0x00, 0x15, 0x80, 0x00, 0xCF],
direction="tx",
semantics_path=path,
)
text = format_text_report(decoded)
self.assertIn("report_index=0x0015", text)
self.assertIn("observed_candidate=call_button_candidate", text)
self.assertNotIn("cmd=", text)
self.assertNotIn("set_value_acked", text)
def test_auto_direction_uses_rx_tx_prefixes(self):
events = parse_byte_text(
"rx: 01 00 00 00 00 5B\n"
"tx: 04 00 00 00 00 5E\n"
)
decoded = decode_trace(events, direction="auto")
self.assertEqual(decoded["frames"][0]["direction"], "rx")
self.assertEqual(decoded["frames"][1]["direction"], "tx")
def test_cli_json_output(self):
output = io.StringIO()
rc = main(["--json", "01", "00", "00", "00", "00", "5B"], stdout=output)
self.assertEqual(rc, 0)
payload = json.loads(output.getvalue())
self.assertEqual(payload["frames"][0]["command"]["value"], 1)
if __name__ == "__main__":
unittest.main()