from __future__ import annotations from math import nan import pytest import dpg_map as dpgm from dpg_map.commands import CommandKind from dpg_map.exceptions import ( CoordinateError, MapNotFoundError, OverlayNotFoundError, ProviderNotFoundError, ) from dpg_map.overlays import TrajectoryOverlay from dpg_map.providers import TileProvider from dpg_map.renderer import drain_renderer_commands from dpg_map.state import DirtyFlags, InteractionState, create_map_state, get_map_state from dpg_map.tiles import TileID, TileResult, TileStatus def test_public_callables_have_docstrings() -> None: for name in dpgm.__all__: value = getattr(dpgm, name) if callable(value): assert value.__doc__, name def test_unknown_map_raises_public_error() -> None: with pytest.raises(MapNotFoundError): dpgm.get_center(map_tag="missing-map") def test_unknown_overlay_raises_public_error() -> None: create_map_state(tag="missing-overlay") with pytest.raises(OverlayNotFoundError): dpgm.update_marker("vehicle", lat=47.0, lon=2.0, map_tag="missing-overlay") def test_unknown_provider_raises_public_error() -> None: create_map_state(tag="missing-provider") with pytest.raises(ProviderNotFoundError): dpgm.set_provider("missing-provider-name", map_tag="missing-provider") def test_invalid_coordinates_raise_public_error() -> None: create_map_state(tag="invalid-coordinates") with pytest.raises(CoordinateError): dpgm.add_marker("bad-lat", lat=91.0, lon=2.0, map_tag="invalid-coordinates") with pytest.raises(CoordinateError): dpgm.set_center(nan, 2.0, map_tag="invalid-coordinates") def test_mismatched_polyline_lat_lon_lengths_raise_public_error() -> None: create_map_state(tag="mismatched-polyline") with pytest.raises(CoordinateError): dpgm.add_polyline("line", lats=[47.0, 47.1], lons=[2.0], map_tag="mismatched-polyline") def test_empty_trajectory_is_valid_for_live_updates() -> None: create_map_state(tag="empty-trajectory") dpgm.add_trajectory("track", points=[], map_tag="empty-trajectory") state = get_map_state("empty-trajectory") overlay = state.overlays["track"] assert isinstance(overlay, TrajectoryOverlay) assert overlay.points == () def test_clear_deleted_overlay_raises_public_error() -> None: create_map_state(tag="deleted-overlay") dpgm.add_marker("vehicle", lat=47.0, lon=2.0, map_tag="deleted-overlay") dpgm.delete_overlay("vehicle", map_tag="deleted-overlay") with pytest.raises(OverlayNotFoundError): dpgm.delete_overlay("vehicle", map_tag="deleted-overlay") def test_provider_switch_ignores_tiles_that_finish_after_switch() -> None: provider = TileProvider( name="hardening-switch-provider", url_template="https://tiles.example.test/{z}/{x}/{y}.png", min_zoom=0, max_zoom=4, ) dpgm.register_provider(provider) try: state = create_map_state(tag="provider-switch-loading", zoom=3) old_tile = TileID("osm", 3, 1, 2) with state.tile_manager._lock: state.tile_manager._loading.add(old_tile) dpgm.set_provider("hardening-switch-provider", map_tag="provider-switch-loading") state.tile_manager._result_queue.put( TileResult( old_tile, generation=0, status=TileStatus.READY, width=1, height=1, pixels=(1.0, 1.0, 1.0, 1.0), source="network", ) ) commands = drain_renderer_commands(state) accepted = state.tile_manager.drain_results( generation=state.generation, provider_name=state.provider.name, ) assert [command.kind for command in commands] == [CommandKind.SET_PROVIDER] assert accepted == [] assert state.tile_manager.snapshot().stale_results == 1 assert state.tile_manager.get_ready_tile(old_tile) is None finally: dpgm.unregister_provider("hardening-switch-provider") def test_overlay_update_preserves_active_drag_model_state() -> None: create_map_state(tag="update-while-dragging", center=(47.0, 2.0), zoom=9) dpgm.add_marker("vehicle", lat=47.0, lon=2.0, map_tag="update-while-dragging") state = get_map_state("update-while-dragging") state.command_queue.drain() state.dirty = DirtyFlags.NONE state.interaction = InteractionState(active_drag=True, last_mouse_position=(20.0, 30.0)) dpgm.update_marker("vehicle", lat=47.1, lon=2.1, map_tag="update-while-dragging") assert state.interaction.active_drag is True assert state.interaction.last_mouse_position == (20.0, 30.0) assert state.center == (47.0, 2.0) assert state.zoom == 9 assert state.dirty == DirtyFlags.OVERLAYS