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,145 @@
import io
import json
import unittest
from h8536.protocol_capture import analyze_capture_text, format_text_report, main, parse_capture_text
class ProtocolCaptureTest(unittest.TestCase):
def test_parses_timestamped_capture_chunks(self):
chunks = parse_capture_text("16:06:15.502 RX 006 bytes 00 00 15 80 00 CF\n")
self.assertEqual(len(chunks), 1)
self.assertEqual(chunks[0].timestamp_ms, 57975502)
self.assertEqual(chunks[0].analyzer_direction, "rx")
self.assertEqual(chunks[0].device_direction, "tx")
self.assertEqual(chunks[0].bytes, (0x00, 0x00, 0x15, 0x80, 0x00, 0xCF))
def test_recombines_user_split_rx_chunks_into_valid_call_frame(self):
analysis = analyze_capture_text(
"16:06:15.502 RX 003 bytes 00 00 15\n"
"16:06:15.506 RX 003 bytes 80 00 CF\n"
)
self.assertEqual(analysis["frame_count"], 1)
frame = analysis["frames"][0]
self.assertEqual(frame["source_chunk_indexes"], [0, 1])
self.assertEqual(frame["analyzer_direction"], "rx")
self.assertEqual(frame["device_direction"], "tx")
self.assertEqual(frame["report_candidate"]["index"], 0x15)
self.assertEqual(frame["report_candidate"]["value"], 0x8000)
self.assertEqual(
frame["report_candidate"]["observed_candidate"]["name_candidate"],
"call_button_candidate",
)
self.assertEqual(frame["report_candidate"]["observed_candidate"]["state_candidate"], "active")
def test_recombines_split_chunks_with_multiple_frames_and_labels_known_reports(self):
analysis = analyze_capture_text(
"16:06:15.500 RX 004 bytes 00 00 00 00\n"
"16:06:15.502 RX 005 bytes 80 DA 00 00 07\n"
"16:06:15.504 RX 003 bytes 80 00 DD\n"
"16:06:15.600 RX 006 bytes 00 00 00 00 80 DA\n"
)
self.assertEqual(analysis["frame_count"], 3)
names = [
frame["report_candidate"]["observed_candidate"]["name_candidate"]
for frame in analysis["frames"]
]
self.assertEqual(
names,
[
"heartbeat_alive_candidate",
"cam_power_button_candidate",
"heartbeat_alive_candidate",
],
)
self.assertEqual(analysis["repeated_group_count"], 1)
group = analysis["repeated_groups"][0]
self.assertEqual(group["count"], 2)
self.assertEqual(group["cadence_ms"]["samples"], [100])
def test_text_report_mentions_split_label_and_cadence(self):
report = format_text_report(
analyze_capture_text(
"16:06:15.502 RX 003 bytes 00 00 15\n"
"16:06:15.506 RX 003 bytes 80 00 CF\n"
"16:06:15.602 RX 006 bytes 00 00 15 80 00 CF\n"
)
)
self.assertIn("call_button_candidate state=active", report)
self.assertIn("checksum=ok split", report)
self.assertIn("cadence=avg=100.0ms", report)
def test_gate_session_hints_summarize_reports_transitions_and_heartbeat_interruptions(self):
analysis = analyze_capture_text(
"16:06:15.500 RX 006 bytes 00 00 00 00 80 DA\n"
"16:06:15.550 RX 006 bytes 00 00 15 80 00 CF\n"
"16:06:15.600 RX 006 bytes 00 00 00 00 80 DA\n"
"16:06:15.650 RX 006 bytes 00 00 15 00 00 4F\n"
"16:06:15.700 RX 006 bytes 00 00 00 00 80 DA\n"
)
hints = analysis["gate_session_hints"]
self.assertEqual(
hints["observed_autonomous_report_names"],
["call_button_candidate", "heartbeat_alive_candidate"],
)
reports = {item["name_candidate"]: item for item in hints["observed_reports"]}
self.assertEqual(reports["heartbeat_alive_candidate"]["count"], 3)
self.assertEqual(reports["heartbeat_alive_candidate"]["first_timestamp"], "16:06:15.500")
self.assertEqual(reports["heartbeat_alive_candidate"]["last_timestamp"], "16:06:15.700")
self.assertEqual(reports["call_button_candidate"]["states"], ["active", "inactive"])
self.assertEqual(hints["heartbeat_cadence_ms"]["samples"], [100, 100])
self.assertEqual(hints["heartbeat_cadence_ms"]["average"], 100.0)
self.assertEqual(len(hints["heartbeat_interruptions"]), 2)
self.assertEqual(
hints["heartbeat_interruptions"][0]["interrupted_by"][0]["name_candidate"],
"call_button_candidate",
)
self.assertEqual(len(hints["active_inactive_transitions"]), 1)
transition = hints["active_inactive_transitions"][0]
self.assertEqual(transition["index_hex"], "0x0015")
self.assertEqual(transition["from_state"], "active")
self.assertEqual(transition["to_state"], "inactive")
self.assertEqual(hints["evidence_scope"], "capture_side_observation_only")
self.assertIn("host/session gating", hints["caveat"])
def test_text_report_mentions_gate_session_hints_and_caveat(self):
report = format_text_report(
analyze_capture_text(
"16:06:15.500 RX 006 bytes 00 00 00 00 80 DA\n"
"16:06:15.550 RX 006 bytes 00 00 15 80 00 CF\n"
"16:06:15.600 RX 006 bytes 00 00 00 00 80 DA\n"
"16:06:15.650 RX 006 bytes 00 00 15 00 00 4F\n"
)
)
self.assertIn(
"observed autonomous report candidates: call_button_candidate, heartbeat_alive_candidate",
report,
)
self.assertIn("heartbeat cadence count=2 cadence=avg=100.0ms", report)
self.assertIn("transition index=0x0015 active->inactive", report)
self.assertIn("heartbeat gap 16:06:15.500..16:06:15.600", report)
self.assertIn("caveat: Missing autonomous reports", report)
def test_cli_json_output(self):
output = io.StringIO()
rc = main(
["--json", "-"],
stdin=io.StringIO("16:06:15.502 RX 006 bytes 00 00 15 80 00 CF\n"),
stdout=output,
)
self.assertEqual(rc, 0)
payload = json.loads(output.getvalue())
self.assertEqual(payload["frames"][0]["report_candidate"]["index"], 0x15)
if __name__ == "__main__":
unittest.main()