reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
192
vendor-new/apk-reverse/tests/unit/test_protobuf_decode_raw.py
Normal file
192
vendor-new/apk-reverse/tests/unit/test_protobuf_decode_raw.py
Normal file
@@ -0,0 +1,192 @@
|
||||
"""Unit tests for skills/apk-reverse/scripts/protobuf_decode_raw.py.
|
||||
|
||||
Two properties are pinned here, because both are claims the reference material
|
||||
makes and neither is visible from reading the decoder:
|
||||
|
||||
* the built-in known-answer fixture set passes in full (26/26), and
|
||||
* a decode -> re-encode round trip is byte-identical for every fixture except
|
||||
the one fixture that is deliberately non-canonical, which must instead
|
||||
report *why* it differs rather than silently normalising.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from conftest import load_script, run_script
|
||||
|
||||
pb = load_script("protobuf_decode_raw.py")
|
||||
|
||||
pytestmark = pytest.mark.unit
|
||||
|
||||
# 12 fixtures x (fixture check + round trip) + 2 wire-format trap checks.
|
||||
EXPECTED_FIXTURE_COUNT = 12
|
||||
EXPECTED_TOTAL_CHECKS = 26
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def fixtures():
|
||||
return pb._fixtures()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# varint primitives
|
||||
# --------------------------------------------------------------------------- #
|
||||
class TestVarintPrimitives:
|
||||
def test_canonical_example(self):
|
||||
assert pb.encode_varint(150) == bytes.fromhex("9601")
|
||||
|
||||
def test_read_varint_returns_info_next_index_and_no_error(self):
|
||||
info, nxt, err = pb.read_varint(bytes.fromhex("9601"), 0, 2)
|
||||
assert err is None
|
||||
assert info["value"] == 150
|
||||
assert info["bytes"] == 2
|
||||
assert nxt == 2
|
||||
|
||||
def test_a_truncated_varint_is_an_error_not_a_value(self):
|
||||
info, nxt, err = pb.read_varint(bytes.fromhex("96"), 0, 1)
|
||||
assert info is None
|
||||
assert err and "truncated" in err
|
||||
assert nxt == 0
|
||||
|
||||
def test_a_non_canonical_encoding_is_flagged(self):
|
||||
"""A padded varint must be reported: it is why re-encode cannot be assumed equal."""
|
||||
info, _nxt, _err = pb.read_varint(bytes.fromhex("8000"), 0, 2)
|
||||
assert info["value"] == 0
|
||||
assert info["non_canonical"] is True
|
||||
assert info["canonical_bytes"] == 1
|
||||
|
||||
def test_varint_len_matches_the_encoding(self):
|
||||
for value in (0, 1, 127, 128, 300, 2 ** 31, 2 ** 63 - 1):
|
||||
assert pb.varint_len(value) == len(pb.encode_varint(value))
|
||||
|
||||
def test_zigzag(self):
|
||||
assert pb.zigzag_decode(0) == 0
|
||||
assert pb.zigzag_decode(1) == -1
|
||||
assert pb.zigzag_decode(2) == 1
|
||||
assert pb.zigzag_decode(4294967294) == 2147483647
|
||||
|
||||
def test_int_views_wrap(self):
|
||||
assert pb.int32_view(0xFFFFFFFF) == -1
|
||||
assert pb.int64_view(0xFFFFFFFFFFFFFFFF) == -1
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# the built-in fixture set
|
||||
# --------------------------------------------------------------------------- #
|
||||
class TestFixtureSet:
|
||||
def test_the_selftest_function_passes_end_to_end(self, capsys):
|
||||
assert pb.selftest(None) == 0
|
||||
out = capsys.readouterr().out
|
||||
assert "selftest: %d/%d PASS" % (EXPECTED_TOTAL_CHECKS, EXPECTED_TOTAL_CHECKS) in out
|
||||
assert "FAIL" not in out
|
||||
|
||||
def test_fixture_count_is_stable(self, fixtures):
|
||||
"""The 26/26 headline is 12 fixtures x 2 + 2; a silent drop would hide."""
|
||||
assert len(fixtures) == EXPECTED_FIXTURE_COUNT
|
||||
|
||||
def test_every_fixture_has_a_name_payload_and_expectations(self, fixtures):
|
||||
for name, payload, expects, _rt in fixtures:
|
||||
assert isinstance(name, str) and name
|
||||
assert isinstance(payload, (bytes, bytearray)) and payload
|
||||
assert expects, "fixture %s has no assertions" % name
|
||||
|
||||
def test_each_declared_expectation_holds(self, fixtures):
|
||||
failures = []
|
||||
for name, payload, expects, _rt in fixtures:
|
||||
ctx = pb.Ctx(max_depth=6)
|
||||
frames = pb._fixture_frames(name, payload)
|
||||
fields = []
|
||||
for start, end, _p in frames:
|
||||
found, _n, _s = pb.walk(payload, start, end, 0, ctx, "f", analyse=True)
|
||||
fields.extend(found)
|
||||
for label, num, expected in expects:
|
||||
got = pb._selftest_lookup(fields, frames, payload, label, num)
|
||||
if got != expected:
|
||||
failures.append((name, label, expected, got))
|
||||
assert failures == []
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# round trip
|
||||
# --------------------------------------------------------------------------- #
|
||||
class TestRoundTrip:
|
||||
def _reencode(self, name, payload):
|
||||
frames = pb._fixture_frames(name, payload)
|
||||
tree = {"frames": [{"fields": pb.walk(payload, s, e, 0, pb.Ctx(6), "f",
|
||||
analyse=True)[0]}
|
||||
for s, e, _p in frames]}
|
||||
report = {"fields": 0, "identical": 0, "mismatched": []}
|
||||
out = pb.reencode_tree(tree, report)
|
||||
expected = b"".join(payload[s:e] for s, e, _p in frames)
|
||||
return out, expected, report
|
||||
|
||||
def test_canonical_fixtures_reencode_byte_identically(self, fixtures):
|
||||
differing = []
|
||||
for name, payload, _expects, rt in fixtures:
|
||||
if rt == "non-canonical":
|
||||
continue
|
||||
out, expected, _report = self._reencode(name, payload)
|
||||
if out != expected:
|
||||
differing.append((name, expected.hex(), out.hex()))
|
||||
assert differing == []
|
||||
|
||||
def test_the_non_canonical_fixture_reports_why_it_differs(self, fixtures):
|
||||
"""A normalising re-encode must say so, not return different bytes quietly."""
|
||||
targets = [f for f in fixtures if f[3] == "non-canonical"]
|
||||
assert targets, "the non-canonical fixture disappeared from the set"
|
||||
for name, payload, _expects, _rt in targets:
|
||||
out, expected, report = self._reencode(name, payload)
|
||||
assert out != expected
|
||||
assert out.hex() == "089601"
|
||||
assert report["mismatched"], "the mismatch must be recorded"
|
||||
assert "non-canonical" in report["mismatched"][0]["reason"]
|
||||
|
||||
def test_reencode_accounts_for_every_field(self, fixtures):
|
||||
for name, payload, _expects, rt in fixtures:
|
||||
_out, _expected, report = self._reencode(name, payload)
|
||||
assert report["fields"] > 0, name
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# the two wire-format traps, asserted rather than narrated
|
||||
# --------------------------------------------------------------------------- #
|
||||
class TestWireFormatTraps:
|
||||
def test_explicit_zero_is_not_the_same_bytes_as_absent(self):
|
||||
zero = pb.tag_0(5, 0) + pb.encode_varint(0)
|
||||
assert zero != b""
|
||||
|
||||
def test_explicit_zero_decodes_to_value_zero(self):
|
||||
zero = pb.tag_0(5, 0) + pb.encode_varint(0)
|
||||
fields = pb.walk(zero, 0, len(zero), 0, pb.Ctx(6), "f")[0]
|
||||
assert fields[0]["value"] == 0
|
||||
|
||||
def test_absent_field_decodes_to_no_fields(self):
|
||||
assert pb.walk(b"", 0, 0, 0, pb.Ctx(6), "f")[0] == []
|
||||
|
||||
def test_a_packed_payload_offers_both_readings(self):
|
||||
"""Three varints are also a valid nested message; only a schema decides."""
|
||||
packed = b"".join(pb.encode_varint(v) for v in (3, 270, 86942))
|
||||
cands, view = pb.analyse_payload(packed, 0, pb.Ctx(6), "f", 0)
|
||||
live = sorted(c["kind"] for c in cands if c["confidence"] > 0.0)
|
||||
all_kinds = sorted(c["kind"] for c in cands)
|
||||
assert "packed_varint" in live
|
||||
assert "nested_message" in all_kinds
|
||||
assert view == "packed_varint"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# the subprocess contract
|
||||
# --------------------------------------------------------------------------- #
|
||||
class TestSelftestCli:
|
||||
def test_selftest_subcommand_exits_zero_and_prints_the_tally(self):
|
||||
result = run_script("protobuf_decode_raw.py", ["--selftest"], timeout=180)
|
||||
assert not result.timed_out
|
||||
assert not result.has_traceback, result.output
|
||||
assert result.returncode == 0, result.output
|
||||
assert "selftest: %d/%d PASS" % (EXPECTED_TOTAL_CHECKS, EXPECTED_TOTAL_CHECKS) \
|
||||
in result.stdout
|
||||
|
||||
def test_help_lists_the_selftest_flag(self):
|
||||
result = run_script("protobuf_decode_raw.py", ["--help"])
|
||||
assert result.returncode == 0
|
||||
assert "--selftest" in result.stdout
|
||||
Reference in New Issue
Block a user