feat(flash): firmware pin + matches_pin detection + pm3flash CLI
Adds the 'is the device running the right firmware, and flash it if not' layer on top of the flasher. - _firmware.py: FirmwarePin (pinned to the firmware/ submodule commit) and matches() — a device version-string test against the pinned git SHA (or a release tag, once tagged releases exist). - FirmwareInfo gains expected_firmware + matches_pin, computed on connect, so pm3.firmware.matches_pin tells you whether the fork build is present. - pm3flash console-script: auto-detect, compare to the pin, resolve the image (--image / $PM3PY_FIRMWARE / local build), confirm, flash with progress, then reopen and verify the new version. Flashes only on mismatch unless --force; fullimage-only unless --allow-bootrom. Detection + CLI are unit-tested; the auto-download layer (fetch the pinned release asset) and hardware validation are still to come. Suite 1034 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
75
tests/test_firmware_pin.py
Normal file
75
tests/test_firmware_pin.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""Tests for the firmware pin + needs-reflash detection (no hardware)."""
|
||||
import asyncio
|
||||
|
||||
from pm3py._firmware import FirmwarePin, FIRMWARE_PIN, matches
|
||||
from pm3py.core.client import Proxmark3
|
||||
from pm3py import flash_cli
|
||||
|
||||
|
||||
def run(coro):
|
||||
return asyncio.get_event_loop().run_until_complete(coro)
|
||||
|
||||
|
||||
# --- matches() ---
|
||||
|
||||
def test_matches_sha_substring():
|
||||
pin = FirmwarePin(sha="303bd5873")
|
||||
assert matches("Iceman/main/303bd5873-dirty", pin) is True
|
||||
assert matches("Iceman/main/303bd58-clean", pin) is True # 7-char prefix
|
||||
|
||||
|
||||
def test_matches_rejects_other_build():
|
||||
pin = FirmwarePin(sha="303bd5873")
|
||||
assert matches("Iceman/master/deadbeef", pin) is False
|
||||
|
||||
|
||||
def test_matches_empty_is_false():
|
||||
assert matches("", FirmwarePin(sha="303bd5873")) is False
|
||||
|
||||
|
||||
def test_matches_tag():
|
||||
pin = FirmwarePin(sha="303bd5873", tag="fw-v0.1.0")
|
||||
assert matches("pm3py fw-v0.1.0 build", pin) is True
|
||||
|
||||
|
||||
def test_default_pin_is_present():
|
||||
assert FIRMWARE_PIN.sha # a pin is baked in
|
||||
|
||||
|
||||
# --- _probe_firmware wiring ---
|
||||
|
||||
def _fake_probe(pm3, version_string):
|
||||
async def fake_ping(length=32):
|
||||
return {"success": True}
|
||||
|
||||
async def fake_version():
|
||||
return {"version_string": version_string, "chip_id": "0x1", "section_size": 0}
|
||||
|
||||
pm3.hw.ping = fake_ping
|
||||
pm3.hw.version = fake_version
|
||||
|
||||
|
||||
def test_probe_sets_matches_pin_true():
|
||||
pm3 = Proxmark3("/dev/ttyFAKE")
|
||||
_fake_probe(pm3, f"Iceman/main/{FIRMWARE_PIN.sha}-dirty")
|
||||
run(pm3._probe_firmware())
|
||||
assert pm3.firmware.matches_pin is True
|
||||
assert pm3.firmware.expected_firmware == FIRMWARE_PIN.sha
|
||||
|
||||
|
||||
def test_probe_sets_matches_pin_false_for_stock():
|
||||
pm3 = Proxmark3("/dev/ttyFAKE")
|
||||
_fake_probe(pm3, "Iceman/master/00000000")
|
||||
run(pm3._probe_firmware())
|
||||
assert pm3.firmware.matches_pin is False
|
||||
|
||||
|
||||
# --- CLI wiring ---
|
||||
|
||||
def test_cli_help_exits_clean(capsys):
|
||||
try:
|
||||
flash_cli.main(["--help"])
|
||||
except SystemExit as e:
|
||||
assert e.code == 0
|
||||
out = capsys.readouterr().out
|
||||
assert "pm3flash" in out
|
||||
Reference in New Issue
Block a user