step 2: add thread safe state commands and cache model

This commit is contained in:
2026-05-22 18:28:09 +02:00
parent bd1ce7abff
commit 13b6a1e65b
12 changed files with 1272 additions and 51 deletions

View File

@@ -2,7 +2,15 @@ from __future__ import annotations
from pathlib import Path
from dpg_map.cache import CacheStats, DiskCacheConfig, MemoryCacheConfig
from dpg_map.cache import (
CacheStats,
DiskCacheConfig,
DiskCacheMetadata,
MemoryCacheConfig,
plan_disk_prune,
tile_cache_path,
write_disk_metadata,
)
def test_cache_stats_dataclass_construction() -> None:
@@ -29,3 +37,32 @@ def test_initial_cache_config_dataclasses() -> None:
assert memory_config.max_tiles == 512
assert disk_config.max_bytes == 2_000_000_000
def test_disk_cache_path_generation(tmp_path: Path) -> None:
path = tile_cache_path(tmp_path, "osm", 4, 8, 9, "jpg")
assert path == tmp_path / "osm" / "4" / "8" / "9.jpg"
def test_disk_cache_prune_ordering(tmp_path: Path) -> None:
first = tile_cache_path(tmp_path, "osm", 1, 1, 1)
second = tile_cache_path(tmp_path, "osm", 1, 1, 2)
protected = tile_cache_path(tmp_path, "osm", 1, 1, 3)
for path, accessed_at in [(first, 1.0), (second, 2.0), (protected, 0.0)]:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(b"abcde")
write_disk_metadata(
path.with_suffix(".json"),
DiskCacheMetadata(
url=str(path),
downloaded_at=accessed_at,
last_accessed_at=accessed_at,
size_bytes=5,
),
)
planned = plan_disk_prune(tmp_path, 5, protected_paths={protected})
assert planned == [first, second]

35
tests/test_commands.py Normal file
View File

@@ -0,0 +1,35 @@
from __future__ import annotations
from dpg_map.commands import CommandKind, MapCommand, MapCommandQueue
def test_overlay_updates_coalesce_by_overlay_tag() -> None:
queue = MapCommandQueue()
queue.put(MapCommand(CommandKind.UPDATE_OVERLAY, "map", {"tag": "vehicle", "lat": 1.0}))
queue.put(MapCommand(CommandKind.UPDATE_OVERLAY, "map", {"tag": "vehicle", "lat": 2.0}))
queue.put(MapCommand(CommandKind.UPDATE_OVERLAY, "map", {"tag": "other", "lat": 3.0}))
drained = queue.drain()
assert len(drained) == 2
assert drained[0].payload == {"tag": "vehicle", "lat": 2.0}
assert drained[1].payload == {"tag": "other", "lat": 3.0}
def test_latest_view_update_wins_but_structural_commands_keep_order() -> None:
queue = MapCommandQueue()
queue.put(MapCommand(CommandKind.ADD_OVERLAY, "map", {"tag": "a"}))
queue.put(MapCommand(CommandKind.SET_VIEW, "map", {"zoom": 3}))
queue.put(MapCommand(CommandKind.SET_VIEW, "map", {"zoom": 4}))
queue.put(MapCommand(CommandKind.DELETE_OVERLAY, "map", {"tag": "a"}))
drained = queue.drain()
assert [command.kind for command in drained] == [
CommandKind.ADD_OVERLAY,
CommandKind.SET_VIEW,
CommandKind.DELETE_OVERLAY,
]
assert drained[1].payload == {"zoom": 4}

View File

@@ -0,0 +1,63 @@
from __future__ import annotations
import pytest
import dpg_map as dpgm
from dpg_map.exceptions import CoordinateError
from dpg_map.overlays import TrajectoryOverlay
from dpg_map.state import DirtyFlags, create_map_state, get_map_state
def test_overlay_update_does_not_alter_center_or_zoom() -> None:
create_map_state(tag="overlay-isolation", center=(47.0, 2.0), zoom=8)
dpgm.add_marker("vehicle", lat=47.1, lon=2.1, map_tag="overlay-isolation")
before_center = dpgm.get_center(map_tag="overlay-isolation")
before_zoom = dpgm.get_zoom(map_tag="overlay-isolation")
dpgm.update_marker("vehicle", lat=47.2, lon=2.2, map_tag="overlay-isolation")
assert dpgm.get_center(map_tag="overlay-isolation") == before_center
assert dpgm.get_zoom(map_tag="overlay-isolation") == before_zoom
state = get_map_state("overlay-isolation")
assert state.dirty & DirtyFlags.OVERLAYS
def test_trajectory_inputs_are_copied() -> None:
create_map_state(tag="trajectory-copy")
lats = [1.0, 2.0]
lons = [3.0, 4.0]
dpgm.add_trajectory("track", lats=lats, lons=lons, map_tag="trajectory-copy")
lats[0] = 99.0
lons[0] = 99.0
state = get_map_state("trajectory-copy")
overlay = state.overlays["track"]
assert isinstance(overlay, TrajectoryOverlay)
assert overlay.points == ((1.0, 3.0), (2.0, 4.0))
def test_mismatched_lat_lon_lengths_raise() -> None:
create_map_state(tag="bad-coordinates")
with pytest.raises(CoordinateError):
dpgm.add_trajectory("track", lats=[1.0], lons=[2.0, 3.0], map_tag="bad-coordinates")
def test_layer_state_tracks_visibility_and_overlay_membership() -> None:
create_map_state(tag="layers")
dpgm.add_layer("fleet", map_tag="layers")
dpgm.add_marker("vehicle", lat=1.0, lon=2.0, layer="fleet", map_tag="layers")
dpgm.hide_layer("fleet", map_tag="layers")
state = get_map_state("layers")
assert state.layers["fleet"].show is False
assert state.layers["fleet"].overlay_tags == {"vehicle"}
dpgm.clear_layer("fleet", map_tag="layers")
assert state.layers["fleet"].overlay_tags == set()
assert "vehicle" not in state.overlays