Files
gps-denied-onboard/tests/test_satellite.py
T
Yuzviak 5744ff65ac feat(02-03): apply module-level pytestmark to 37 test files
- Add pytestmark = [pytest.mark.<category>] to all 23 root test files and 14 e2e test files
- Marker distribution: 22 unit, 7 integration, 1 blackbox, 1 sitl, 5 e2e + 2 e2e integration
- Add import pytest to test_models.py, test_download.py, test_synthetic_adapter.py (were missing)
- Convert test_sitl_integration.py's bare pytestmark to list form preserving skipif guard
- Union of all 5 markers = 298/298 = 100% coverage; 216 tests pass with --strict-markers
2026-05-11 18:20:05 +03:00

162 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tests for SatelliteDataManager (F04) — SAT-01/02 and mercator utils (H06)."""
import numpy as np
import pytest
pytestmark = [pytest.mark.unit]
from gps_denied.core.satellite import SatelliteDataManager
from gps_denied.schemas import GPSPoint
from gps_denied.utils import mercator
# ---------------------------------------------------------------
# Mercator utils
# ---------------------------------------------------------------
def test_latlon_to_tile():
lat, lon, zoom = 50.4501, 30.5234, 15
coords = mercator.latlon_to_tile(lat, lon, zoom)
assert coords.zoom == 15
assert coords.x > 0
assert coords.y > 0
def test_tile_to_latlon():
gps = mercator.tile_to_latlon(19131, 10927, 15)
assert 50.0 < gps.lat < 52.0
assert 30.0 < gps.lon < 31.0
def test_tile_bounds():
coords = mercator.TileCoords(x=19131, y=10927, zoom=15)
bounds = mercator.compute_tile_bounds(coords)
assert bounds.nw.lat > bounds.se.lat
assert bounds.nw.lon < bounds.se.lon
assert bounds.gsd > 0
# ---------------------------------------------------------------
# SAT-01: Local tile storage (no HTTP)
# ---------------------------------------------------------------
@pytest.fixture
def satellite_manager(tmp_path):
return SatelliteDataManager(tile_dir=str(tmp_path / "tiles"))
def test_load_local_tile_missing(satellite_manager):
"""Missing tile returns None — no crash."""
coords = mercator.TileCoords(x=0, y=0, zoom=12)
result = satellite_manager.load_local_tile(coords)
assert result is None
def test_save_and_load_local_tile(satellite_manager):
"""SAT-01: saved tile can be read back from the local directory."""
coords = mercator.TileCoords(x=19131, y=10927, zoom=15)
img = np.zeros((256, 256, 3), dtype=np.uint8)
img[:] = [0, 128, 255]
ok = satellite_manager.save_local_tile(coords, img)
assert ok is True
loaded = satellite_manager.load_local_tile(coords)
assert loaded is not None
assert loaded.shape == (256, 256, 3)
def test_mem_cache_hit(satellite_manager):
"""Tile loaded once should be served from memory on second request."""
coords = mercator.TileCoords(x=1, y=1, zoom=10)
img = np.ones((256, 256, 3), dtype=np.uint8) * 42
satellite_manager.save_local_tile(coords, img)
r1 = satellite_manager.load_local_tile(coords)
r2 = satellite_manager.load_local_tile(coords)
assert r1 is r2 # same object = came from mem cache
# ---------------------------------------------------------------
# SAT-02: ESKF ±3σ tile selection
# ---------------------------------------------------------------
def test_select_tiles_small_sigma(satellite_manager):
"""Very tight sigma → single tile covering the position."""
gps = GPSPoint(lat=50.45, lon=30.52)
tiles = satellite_manager.select_tiles_for_eskf_position(gps, sigma_h_m=1.0, zoom=18)
# Should produce at least the center tile
assert len(tiles) >= 1
for t in tiles:
assert t.zoom == 18
def test_select_tiles_large_sigma(satellite_manager):
"""Larger sigma → more tiles returned."""
gps = GPSPoint(lat=50.45, lon=30.52)
small = satellite_manager.select_tiles_for_eskf_position(gps, sigma_h_m=10.0, zoom=18)
large = satellite_manager.select_tiles_for_eskf_position(gps, sigma_h_m=200.0, zoom=18)
assert len(large) >= len(small)
def test_select_tiles_bounding_box(satellite_manager):
"""Selected tiles must span a bounding box that covers ±3σ."""
gps = GPSPoint(lat=49.0, lon=32.0)
sigma = 50.0 # 50 m → 3σ = 150 m
zoom = 18
tiles = satellite_manager.select_tiles_for_eskf_position(gps, sigma_h_m=sigma, zoom=zoom)
assert len(tiles) >= 1
# All returned tiles must be at the requested zoom
assert all(t.zoom == zoom for t in tiles)
# ---------------------------------------------------------------
# SAT-01: Mosaic assembly
# ---------------------------------------------------------------
def test_assemble_mosaic_single(satellite_manager):
"""Single tile → mosaic equals that tile (resized)."""
coords = mercator.TileCoords(x=10, y=10, zoom=15)
img = np.zeros((256, 256, 3), dtype=np.uint8)
mosaic, bounds = satellite_manager.assemble_mosaic([(coords, img)], target_size=256)
assert mosaic.shape == (256, 256, 3)
assert bounds.center is not None
def test_assemble_mosaic_2x2(satellite_manager):
"""2×2 tile grid assembles into a single mosaic."""
mercator.TileCoords(x=10, y=10, zoom=15)
tiles = [
(mercator.TileCoords(x=10, y=10, zoom=15), np.zeros((256, 256, 3), dtype=np.uint8)),
(mercator.TileCoords(x=11, y=10, zoom=15), np.zeros((256, 256, 3), dtype=np.uint8)),
(mercator.TileCoords(x=10, y=11, zoom=15), np.zeros((256, 256, 3), dtype=np.uint8)),
(mercator.TileCoords(x=11, y=11, zoom=15), np.zeros((256, 256, 3), dtype=np.uint8)),
]
mosaic, bounds = satellite_manager.assemble_mosaic(tiles, target_size=512)
assert mosaic.shape == (512, 512, 3)
def test_assemble_mosaic_empty(satellite_manager):
result = satellite_manager.assemble_mosaic([])
assert result is None
# ---------------------------------------------------------------
# Cache helpers (backward compat)
# ---------------------------------------------------------------
def test_cache_tile_compat(satellite_manager):
coords = mercator.TileCoords(x=100, y=100, zoom=12)
img = np.zeros((256, 256, 3), dtype=np.uint8)
assert satellite_manager.cache_tile("f1", coords, img) is True
cached = satellite_manager.get_cached_tile("f1", coords)
assert cached is not None
def test_grid_calculations(satellite_manager):
center = mercator.TileCoords(x=100, y=100, zoom=15)
grid = satellite_manager.get_tile_grid(center, 9)
assert len(grid) == 9
assert any(c.x == 100 and c.y == 100 for c in grid)
new_tiles = satellite_manager.expand_search_grid(center, 9, 25)
assert len(new_tiles) == 16 # 25 - 9