feat(2-7): operational radar loop, startup screen, and systemd wiring
Implements story 2-7: full main.py with _make_startup_screen(), _run_one_cycle() with per-phase timing and slow-render warning, and main() connecting config → bounds → fetcher → renderer → display. Adds provision.py early-exit when already provisioned. Adds User=root to both systemd service files. Adds tests/test_main.py (3 new tests, 99 total). Updates scaffold test to allow provisioning.config import from main.py. All quality gates pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import pathlib
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from planemapper.display import NullDisplay
|
||||
from planemapper.fetcher import FileFixtureFetcher
|
||||
from planemapper.main import _run_one_cycle
|
||||
from planemapper.renderer.projection import MapBounds
|
||||
from planemapper.renderer.renderer import Renderer
|
||||
|
||||
FIXTURE_DIR = pathlib.Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def renderer(monkeypatch: pytest.MonkeyPatch) -> Renderer:
|
||||
monkeypatch.setattr(
|
||||
"planemapper.renderer.airspace.AIRSPACE_PATH",
|
||||
FIXTURE_DIR / "airspace_sample.geojson",
|
||||
)
|
||||
base_map = Image.new("RGB", (800, 480), color=(255, 255, 255))
|
||||
bounds = MapBounds(home_lat=53.0, home_lon=-6.0, radius_nm=100.0)
|
||||
return Renderer(base_map, bounds)
|
||||
|
||||
|
||||
def test_run_one_cycle_calls_display_show(renderer: Renderer) -> None:
|
||||
fetcher = FileFixtureFetcher(FIXTURE_DIR / "aircraft_sample.json")
|
||||
display = NullDisplay()
|
||||
display_mock = MagicMock(wraps=display)
|
||||
_run_one_cycle(renderer, fetcher, display_mock)
|
||||
display_mock.show.assert_called_once()
|
||||
|
||||
|
||||
def test_run_one_cycle_logs_warning_when_slow(
|
||||
renderer: Renderer, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
fetcher = FileFixtureFetcher(FIXTURE_DIR / "aircraft_sample.json")
|
||||
display = NullDisplay()
|
||||
# Simulate 43s total: t0=0, t1=1, t2=2, t3=43
|
||||
with patch("planemapper.main.time.monotonic", side_effect=[0.0, 1.0, 2.0, 43.0]):
|
||||
with caplog.at_level(logging.WARNING, logger="planemapper.main"):
|
||||
_run_one_cycle(renderer, fetcher, display)
|
||||
assert any("render slow" in r.message for r in caplog.records)
|
||||
|
||||
|
||||
def test_provision_main_exits_if_already_provisioned() -> None:
|
||||
with patch("planemapper.provisioning.config.read", return_value={"provisioned": True}):
|
||||
with patch("planemapper.provisioning.wifi.start_ap") as mock_ap:
|
||||
from planemapper.provision import main as provision_main
|
||||
|
||||
provision_main()
|
||||
mock_ap.assert_not_called()
|
||||
Reference in New Issue
Block a user