Files
Cesarus85--mjolnir-vr/tools/test_parked_functions.py
T

153 lines
5.2 KiB
Python

import copy
import json
import tempfile
import unittest
from pathlib import Path
from tools.coff_compare import build_coff, load, section_info
from tools.parked_functions import (
ParkedFunctionsError,
require_valid_parked_functions,
validate_parked_functions,
)
class ParkedFunctionsTests(unittest.TestCase):
def setUp(self):
self.temporary_directory = tempfile.TemporaryDirectory()
self.root = Path(self.temporary_directory.name)
self.target_path = self.root / "target.obj"
self.base_path = self.root / "base.obj"
self.report_path = self.root / "report.json"
self.config_path = self.root / "objdiff.json"
self.manifest_path = self.root / "parked.json"
self.target_path.write_bytes(self._object(b"\x90" * 16))
self.base_path.write_bytes(self._object(b"\xcc" * 16))
self.report = {
"units": [{
"name": "unit",
"functions": [{
"name": "_fn",
"size": 16,
"fuzzy_match_percent": 75.0,
}],
}],
}
self.config = {
"units": [{
"name": "unit",
"target_path": "target.obj",
"base_path": "base.obj",
}],
}
self.entry = {
"unit": "unit",
"function": "_fn",
"class": "register-allocation",
"evidence": "Same control flow and relocations; only registers differ.",
"measurements": self._measurements(),
}
self._write_inputs([self.entry])
def tearDown(self):
self.temporary_directory.cleanup()
@staticmethod
def _object(raw):
return build_coff(
sections=[{"name": ".text", "size": 16, "raw_data": raw}],
symbols=[{
"name": "_fn",
"value": 0,
"section": 1,
"type": 0x20,
"storage": 2,
}],
)
@staticmethod
def _snapshot(info):
return {
"size": info["size"],
"relocation_count": info["relocation_count"],
"normalized_sha256": info["normalized_sha256"],
}
def _measurements(self):
return {
"target": self._snapshot(section_info(load(self.target_path), "_fn")),
"base": self._snapshot(section_info(load(self.base_path), "_fn")),
"objdiff_percent": 75.0,
}
def _write_inputs(self, entries):
self.report_path.write_text(json.dumps(self.report), encoding="utf-8")
self.config_path.write_text(json.dumps(self.config), encoding="utf-8")
self.manifest_path.write_text(
json.dumps({"version": 1, "entries": entries}), encoding="utf-8"
)
def _validate(self):
return validate_parked_functions(
self.root, self.report_path, self.config_path, self.manifest_path
)
def test_current_entry_is_active_and_machine_readable(self):
result = self._validate()
self.assertEqual(result["summary"], {"active": 1, "stale": 0, "invalid": 0})
self.assertEqual(result["active_keys"], ["unit:_fn"])
def test_changed_object_hash_reopens_entry(self):
self.base_path.write_bytes(self._object(b"\x00" * 16))
result = self._validate()
self.assertEqual(result["summary"]["stale"], 1)
self.assertEqual(
result["stale"][0]["differences"][0]["measurement"],
"base.normalized_sha256",
)
def test_changed_relocation_measurement_reopens_entry(self):
entry = copy.deepcopy(self.entry)
entry["measurements"]["target"]["relocation_count"] = 1
self._write_inputs([entry])
result = self._validate()
self.assertEqual(result["summary"]["stale"], 1)
self.assertEqual(
result["stale"][0]["differences"][0]["measurement"],
"target.relocation_count",
)
def test_changed_objdiff_percent_reopens_entry(self):
self.report["units"][0]["functions"][0]["fuzzy_match_percent"] = 80.0
self._write_inputs([self.entry])
result = self._validate()
self.assertEqual(result["summary"]["stale"], 1)
self.assertEqual(
result["stale"][0]["differences"][0]["measurement"],
"objdiff_percent",
)
def test_function_becoming_exact_invalidates_entry(self):
self.base_path.write_bytes(self.target_path.read_bytes())
result = self._validate()
self.assertEqual(result["summary"]["invalid"], 1)
self.assertEqual(result["invalid"][0]["reason"], "function is now semantically exact")
def test_duplicate_key_is_invalid(self):
self._write_inputs([self.entry, self.entry])
result = self._validate()
self.assertEqual(result["summary"]["invalid"], 1)
def test_strict_validation_raises_for_stale_entry(self):
self.report["units"][0]["functions"][0]["fuzzy_match_percent"] = 80.0
self._write_inputs([self.entry])
with self.assertRaises(ParkedFunctionsError):
require_valid_parked_functions(
self.root, self.report_path, self.config_path, self.manifest_path
)
if __name__ == "__main__":
unittest.main()