feat(reset): config wipe, setup screen, and re-exec into provisioning (story 4-2)

Add _handle_reset() and _make_setup_screen() to main.py; integrate ButtonHoldDetector
and LEDController into the radar loop; LED lights immediately on hold, config is wiped,
setup screen shown, then os.execvp hands off to planemapper-provision. Wipe failures
log ERROR and abort without exec. Completes epic-4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Edholm
2026-04-22 23:59:22 -04:00
parent 8e57f2d927
commit 7f34a7b926
5 changed files with 168 additions and 3 deletions
+48
View File
@@ -0,0 +1,48 @@
from __future__ import annotations
from unittest.mock import MagicMock, patch
import pytest
from gpiozero import Device
from gpiozero.pins.mock import MockFactory
from PIL import Image
from planemapper.display import NullDisplay
from planemapper.gpio_ctrl import LEDController
from planemapper.main import _handle_reset
@pytest.fixture(autouse=True)
def mock_gpio():
Device.pin_factory = MockFactory()
def test_reset_wipes_config_and_execvp():
display = NullDisplay()
led = LEDController()
with patch("planemapper.main.wipe_config") as mock_wipe:
with patch("planemapper.main.os.execvp") as mock_exec:
_handle_reset(display, led)
mock_wipe.assert_called_once()
mock_exec.assert_called_once_with("planemapper-provision", ["planemapper-provision"])
def test_reset_shows_setup_screen():
display = MagicMock()
led = LEDController()
with patch("planemapper.main.wipe_config"):
with patch("planemapper.main.os.execvp"):
_handle_reset(display, led)
display.show.assert_called_once()
img = display.show.call_args[0][0]
assert isinstance(img, Image.Image)
assert img.size == (800, 480)
def test_reset_does_not_execvp_on_wipe_failure():
display = NullDisplay()
led = LEDController()
with patch("planemapper.main.wipe_config", side_effect=PermissionError("no perms")):
with patch("planemapper.main.os.execvp") as mock_exec:
_handle_reset(display, led)
mock_exec.assert_not_called()