121 lines
4.1 KiB
Python
121 lines
4.1 KiB
Python
import io
|
|
import json
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from h8536.emulator.state_search import (
|
|
SearchCase,
|
|
SearchResult,
|
|
StatePatch,
|
|
build_cases,
|
|
classify_display,
|
|
main,
|
|
parse_address,
|
|
parse_matrix_patch,
|
|
parse_single_patch,
|
|
target_matches,
|
|
)
|
|
|
|
|
|
class EmulatorStateSearchTest(unittest.TestCase):
|
|
def test_parse_patch_accepts_hex_address_and_value(self):
|
|
patch = parse_single_patch("F730=0x41", size=1)
|
|
|
|
self.assertEqual(patch, StatePatch(1, 0xF730, 0x41, "user"))
|
|
self.assertEqual(patch.label(), "byte:H'F730=0x41")
|
|
|
|
def test_parse_matrix_patch_expands_values(self):
|
|
patches = parse_matrix_patch("E000=0x4080,0x8080", size=2)
|
|
|
|
self.assertEqual(patches, [
|
|
StatePatch(2, 0xE000, 0x4080, "user"),
|
|
StatePatch(2, 0xE000, 0x8080, "user"),
|
|
])
|
|
|
|
def test_parse_address_accepts_h_quote(self):
|
|
self.assertEqual(parse_address("H'F970"), 0xF970)
|
|
|
|
def test_connect_queue_preset_builds_small_rom_driven_matrix(self):
|
|
parser = __import__("h8536.emulator.state_search", fromlist=["build_arg_parser"]).build_arg_parser()
|
|
args = parser.parse_args(["--preset", "connect-queue"])
|
|
|
|
cases = build_cases(args)
|
|
|
|
self.assertEqual(len(cases), 25)
|
|
first = cases[0]
|
|
self.assertEqual(first.pc, 0x2806)
|
|
self.assertIn(StatePatch(2, 0xF970, 0x0000, "preset"), first.patches)
|
|
self.assertIn(StatePatch(2, 0xE000, 0x0000, "preset"), first.patches)
|
|
|
|
def test_custom_matrix_combines_fixed_and_matrix_patches(self):
|
|
parser = __import__("h8536.emulator.state_search", fromlist=["build_arg_parser"]).build_arg_parser()
|
|
args = parser.parse_args([
|
|
"--preset",
|
|
"custom",
|
|
"--pc",
|
|
"0x2CB9",
|
|
"--byte",
|
|
"F730=0",
|
|
"--matrix-word",
|
|
"E000=0x4080,0x8080",
|
|
])
|
|
|
|
cases = build_cases(args)
|
|
|
|
self.assertEqual(len(cases), 2)
|
|
self.assertEqual(cases[0], SearchCase((StatePatch(1, 0xF730, 0, "user"), StatePatch(2, 0xE000, 0x4080, "user")), 0x2CB9))
|
|
|
|
def test_classify_display(self):
|
|
self.assertEqual(classify_display(" CONNECT: OK | "), "ok")
|
|
self.assertEqual(classify_display(" CONNECT:DXC-637 | "), "dxc")
|
|
self.assertEqual(classify_display(" CONNECT:NOT ACT | "), "not-act")
|
|
|
|
def test_target_matching(self):
|
|
self.assertTrue(target_matches("ok", "ok"))
|
|
self.assertTrue(target_matches("dxc", "any-connect"))
|
|
self.assertTrue(target_matches("ok", "changed"))
|
|
self.assertFalse(target_matches("not-act", "changed"))
|
|
|
|
def test_cli_dry_run_lists_cases(self):
|
|
stdout = io.StringIO()
|
|
|
|
rc = main(["--dry-run", "--preset", "connect-branch", "--limit", "2"], stdout=stdout)
|
|
|
|
self.assertEqual(rc, 0)
|
|
output = stdout.getvalue()
|
|
self.assertIn("preset=connect-branch cases=2", output)
|
|
self.assertIn("case[0] pc=H'2CB9", output)
|
|
|
|
def test_cli_json_output_uses_results_from_run_search(self):
|
|
fake_result = SearchResult(
|
|
case_index=0,
|
|
patches=(StatePatch(2, 0xE000, 0x8080, "preset"),),
|
|
pc=0x2CB9,
|
|
steps=10,
|
|
stopped_reason="stop_pc",
|
|
final_pc=0xFFFF,
|
|
display=" CONNECT: OK | ",
|
|
line0=" CONNECT: OK ",
|
|
outcome="ok",
|
|
f730=0x81,
|
|
e000=0x8080,
|
|
f9b4=0,
|
|
f9b9=0,
|
|
)
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
path = Path(tmpdir) / "out.json"
|
|
stdout = io.StringIO()
|
|
with patch("h8536.emulator.state_search.run_search", return_value=[fake_result]):
|
|
rc = main(["--preset", "custom", "--word", "E000=0x8080", "--json-out", str(path)], stdout=stdout)
|
|
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("hits=1", stdout.getvalue())
|
|
self.assertEqual(payload["hits"][0]["outcome"], "ok")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|