79 lines
3.1 KiB
Python
79 lines
3.1 KiB
Python
"""Synthetic structural tests only; no OEM image or ARM execution is used."""
|
|
|
|
import hashlib
|
|
import importlib.util
|
|
from pathlib import Path
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
PATH = ROOT / "navigation/archived/v038/patch_nav_vehicle_speed_source.py"
|
|
SPEC = importlib.util.spec_from_file_location("archived_speed_patch", PATH)
|
|
MODULE = importlib.util.module_from_spec(SPEC)
|
|
SPEC.loader.exec_module(MODULE)
|
|
|
|
|
|
class ArchivedSpeedPatchTests(unittest.TestCase):
|
|
def fixture(self):
|
|
settings = dict(
|
|
SETTER_OFFSET=16,
|
|
SETTER_CONTINUE=20,
|
|
CAVE_OFFSET=64,
|
|
CAVE_SIZE=60,
|
|
DISPLAY_CALL_SITES=(160, 168, 176, 184, 192),
|
|
CONVERT_SPEED=256,
|
|
SAFETY_CALL_SITE=200,
|
|
)
|
|
image = bytearray(320)
|
|
image[16:20] = MODULE.SETTER_ORIGINAL
|
|
for site in settings["DISPLAY_CALL_SITES"] + (settings["SAFETY_CALL_SITE"],):
|
|
image[site:site + 4] = MODULE.encode_thumb_branch(site, 256, link=True)
|
|
settings["SOURCE_SHA256"] = hashlib.sha256(image).hexdigest()
|
|
return bytes(image), settings
|
|
|
|
def test_unknown_original_is_rejected(self):
|
|
with self.assertRaisesRegex(ValueError, "unknown source"):
|
|
MODULE.patch_image(b"synthetic-not-a-library")
|
|
|
|
def test_thumb_alignment_and_range_are_checked(self):
|
|
self.assertEqual(MODULE.encode_thumb_branch(0x1000, 0x1004), b"\x00\xf0\x00\xb8")
|
|
with self.assertRaisesRegex(ValueError, "halfword"):
|
|
MODULE.encode_thumb_branch(0x1000, 0x1005)
|
|
with self.assertRaisesRegex(ValueError, "out of range"):
|
|
MODULE.encode_thumb_branch(0x1000, 0x2001004)
|
|
|
|
def test_only_declared_windows_change(self):
|
|
original, settings = self.fixture()
|
|
with patch.multiple(MODULE, **settings):
|
|
result = MODULE.patch_image(original)
|
|
allowed = set(range(16, 20)) | set(range(64, 124))
|
|
changed = {i for i, (a, b) in enumerate(zip(original, result)) if a != b}
|
|
self.assertTrue(changed)
|
|
self.assertLessEqual(changed, allowed)
|
|
self.assertEqual(len(result), len(original))
|
|
for site in settings["DISPLAY_CALL_SITES"] + (settings["SAFETY_CALL_SITE"],):
|
|
self.assertEqual(result[site:site + 4], original[site:site + 4])
|
|
|
|
def test_occupied_cave_is_rejected_even_with_matching_hash(self):
|
|
original, settings = self.fixture()
|
|
bad = bytearray(original)
|
|
bad[64] = 1
|
|
settings["SOURCE_SHA256"] = hashlib.sha256(bad).hexdigest()
|
|
with patch.multiple(MODULE, **settings):
|
|
with self.assertRaisesRegex(ValueError, "code cave"):
|
|
MODULE.patch_image(bytes(bad))
|
|
|
|
def test_changed_safety_call_is_rejected(self):
|
|
original, settings = self.fixture()
|
|
bad = bytearray(original)
|
|
bad[200] ^= 1
|
|
settings["SOURCE_SHA256"] = hashlib.sha256(bad).hexdigest()
|
|
with patch.multiple(MODULE, **settings):
|
|
with self.assertRaisesRegex(ValueError, "overspeed"):
|
|
MODULE.patch_image(bytes(bad))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|