forked from IQ.Lvbs/IQ.Pilot
IQ.Pilot Prebuilt Release @ ab07000
This commit is contained in:
235
iqpilot/iq_maps/tests/test_tile_bundle_downloader.py
Normal file
235
iqpilot/iq_maps/tests/test_tile_bundle_downloader.py
Normal file
@@ -0,0 +1,235 @@
|
||||
import hashlib
|
||||
import json
|
||||
import sqlite3
|
||||
import threading
|
||||
from functools import partial
|
||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
|
||||
import pytest
|
||||
|
||||
from openpilot.iqpilot.iq_maps import tile_bundle_downloader as tbd
|
||||
from openpilot.iqpilot.ui.onroad import offline_tiles
|
||||
|
||||
|
||||
PNG_1X1 = (
|
||||
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01" +
|
||||
b"\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\rIDATx\x9cc\xf8\xcf" +
|
||||
b"\xc0\xf0\x1f\x00\x05\x00\x01\xff\x89\x99=\x1d\x00\x00\x00\x00IEND\xaeB`\x82"
|
||||
)
|
||||
|
||||
|
||||
class FakeParams:
|
||||
def __init__(self):
|
||||
self.store: dict[str, object] = {}
|
||||
|
||||
def get(self, key, return_default=False):
|
||||
return self.store.get(key)
|
||||
|
||||
def get_bool(self, key):
|
||||
return bool(self.store.get(key))
|
||||
|
||||
def put(self, key, value):
|
||||
self.store[key] = value
|
||||
|
||||
def put_bool(self, key, value):
|
||||
self.store[key] = bool(value)
|
||||
|
||||
def remove(self, key):
|
||||
self.store.pop(key, None)
|
||||
|
||||
|
||||
def _make_mbtiles(path):
|
||||
conn = sqlite3.connect(path)
|
||||
conn.execute("CREATE TABLE metadata (name text, value text)")
|
||||
conn.execute("CREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob)")
|
||||
conn.executemany("INSERT INTO metadata (name, value) VALUES (?, ?)",
|
||||
[("format", "png"), ("minzoom", "10"), ("maxzoom", "16"),
|
||||
("bounds", "-124.5,32.4,-114.1,42.1")])
|
||||
conn.execute("INSERT INTO tiles VALUES (?, ?, ?, ?)", (10, 163, 396, PNG_1X1))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hosting(tmp_path, monkeypatch):
|
||||
"""Local static host serving index.json + a us_state.CA bundle; offline root redirected."""
|
||||
serve_root = tmp_path / "serve"
|
||||
serve_root.mkdir()
|
||||
bundle = serve_root / "us_state.CA.mbtiles"
|
||||
_make_mbtiles(bundle)
|
||||
payload = bundle.read_bytes()
|
||||
index = {
|
||||
"version": 1,
|
||||
"regions": {
|
||||
"us_state.CA": {
|
||||
"path": "us_state.CA.mbtiles",
|
||||
"bytes": len(payload),
|
||||
"sha256": hashlib.sha256(payload).hexdigest(),
|
||||
"bounds": "-124.5,32.4,-114.1,42.1",
|
||||
"minzoom": 10,
|
||||
"maxzoom": 16,
|
||||
"version": "20260709",
|
||||
}
|
||||
},
|
||||
}
|
||||
(serve_root / "index.json").write_text(json.dumps(index))
|
||||
|
||||
handler = partial(SimpleHTTPRequestHandler, directory=str(serve_root))
|
||||
server = HTTPServer(("127.0.0.1", 0), handler)
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
|
||||
offline_root = tmp_path / "offline_maps"
|
||||
monkeypatch.setenv(offline_tiles.OFFLINE_TILE_ROOT_ENV, str(offline_root / "tiles"))
|
||||
offline_tiles._region_roots_cache = None
|
||||
offline_tiles._region_bounds_cache.clear()
|
||||
|
||||
params = FakeParams()
|
||||
params.put(tbd.BASE_URL_PARAM, f"http://127.0.0.1:{server.server_address[1]}")
|
||||
sessions: list = []
|
||||
real_ctor = tbd.TileBundleDownloader.__init__
|
||||
|
||||
def tracking_ctor(self, *args, **kwargs):
|
||||
real_ctor(self, *args, **kwargs)
|
||||
sessions.append(self.session)
|
||||
|
||||
monkeypatch.setattr(tbd.TileBundleDownloader, "__init__", tracking_ctor)
|
||||
try:
|
||||
yield params, index, offline_root
|
||||
finally:
|
||||
for session in sessions:
|
||||
session.close()
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
|
||||
|
||||
def test_download_installs_bundle_and_manifest(hosting):
|
||||
params, index, offline_root = hosting
|
||||
dl = tbd.TileBundleDownloader(params=params, mem_params=params)
|
||||
assert dl.download_regions(["us_state.CA"]) is True
|
||||
|
||||
installed = offline_root / "regions" / "us_state.CA" / "tiles" / "offline.mbtiles"
|
||||
assert installed.exists()
|
||||
manifest = json.loads((installed.parent.parent / "manifest.json").read_text())
|
||||
assert manifest["mbtiles"]["bounds"] == "-124.5,32.4,-114.1,42.1"
|
||||
assert manifest["mbtiles"]["sha256"] == index["regions"]["us_state.CA"]["sha256"]
|
||||
# request/progress params cleaned up
|
||||
assert params.get(tbd.REQUEST_PARAM) is None
|
||||
assert params.get(tbd.PROGRESS_PARAM)["active"] is False
|
||||
|
||||
# and the on-screen map provider can find + read it
|
||||
assert offline_tiles.find_offline_mbtiles_path(37.0, -120.0) == installed
|
||||
conn = offline_tiles.open_mbtiles(installed)
|
||||
try:
|
||||
assert offline_tiles.mbtiles_is_raster(conn)
|
||||
assert offline_tiles.load_raster_tile_blob(conn, 10, 163, 2 ** 10 - 1 - 396) == PNG_1X1
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_skips_already_installed_matching_sha(hosting):
|
||||
params, _, offline_root = hosting
|
||||
dl = tbd.TileBundleDownloader(params=params, mem_params=params)
|
||||
assert dl.download_regions(["us_state.CA"]) is True
|
||||
installed = offline_root / "regions" / "us_state.CA" / "tiles" / "offline.mbtiles"
|
||||
first_mtime = installed.stat().st_mtime_ns
|
||||
assert dl.download_regions(["us_state.CA"]) is True
|
||||
assert installed.stat().st_mtime_ns == first_mtime
|
||||
|
||||
|
||||
def test_unknown_region_fails_cleanly(hosting):
|
||||
params, _, offline_root = hosting
|
||||
dl = tbd.TileBundleDownloader(params=params, mem_params=params)
|
||||
assert dl.download_regions(["us_state.ZZ"]) is False
|
||||
assert not (offline_root / "regions" / "us_state.ZZ").exists()
|
||||
|
||||
|
||||
def test_resume_from_partial(hosting):
|
||||
params, _, offline_root = hosting
|
||||
part = offline_root / "regions" / "us_state.CA" / "tiles" / "offline.mbtiles.part"
|
||||
part.parent.mkdir(parents=True)
|
||||
# pre-seed the first half as an interrupted download
|
||||
full = (offline_root / ".." / "serve" / "us_state.CA.mbtiles").resolve().read_bytes()
|
||||
part.write_bytes(full[: len(full) // 2])
|
||||
|
||||
dl = tbd.TileBundleDownloader(params=params, mem_params=params)
|
||||
assert dl.download_regions(["us_state.CA"]) is True
|
||||
installed = part.parent / "offline.mbtiles"
|
||||
assert installed.read_bytes() == full
|
||||
assert not part.exists()
|
||||
|
||||
|
||||
def test_cancel_aborts_before_install(hosting):
|
||||
params, _, offline_root = hosting
|
||||
dl = tbd.TileBundleDownloader(params=params, mem_params=params, abort_check=lambda: True)
|
||||
assert dl.download_regions(["us_state.CA"]) is False
|
||||
assert not (offline_root / "regions" / "us_state.CA" / "tiles" / "offline.mbtiles").exists()
|
||||
# request param cleaned up so the UI doesn't show a stuck download
|
||||
assert params.get(tbd.REQUEST_PARAM) is None
|
||||
|
||||
|
||||
def test_sha_mismatch_rejected(hosting):
|
||||
params, index, offline_root = hosting
|
||||
index["regions"]["us_state.CA"]["sha256"] = "0" * 64
|
||||
serve_root = (offline_root / ".." / "serve").resolve()
|
||||
(serve_root / "index.json").write_text(json.dumps(index))
|
||||
dl = tbd.TileBundleDownloader(params=params, mem_params=params)
|
||||
assert dl.download_regions(["us_state.CA"]) is False
|
||||
assert not (offline_root / "regions" / "us_state.CA" / "tiles" / "offline.mbtiles").exists()
|
||||
|
||||
|
||||
def test_new_region_visible_without_process_restart(hosting):
|
||||
"""Regression: lru_cache on _candidate_region_roots hid freshly downloaded regions."""
|
||||
params, _, offline_root = hosting
|
||||
# UI already scanned (and found nothing)
|
||||
offline_tiles._region_roots_cache = None
|
||||
assert offline_tiles.find_offline_mbtiles_path(37.0, -120.0) is None
|
||||
|
||||
dl = tbd.TileBundleDownloader(params=params, mem_params=params)
|
||||
assert dl.download_regions(["us_state.CA"]) is True
|
||||
|
||||
# TTL cache: expire it and the new region shows up in the same process
|
||||
offline_tiles._region_roots_cache = None
|
||||
found = offline_tiles.find_offline_mbtiles_path(37.0, -120.0)
|
||||
assert found is not None and found.exists()
|
||||
|
||||
|
||||
def test_candidate_base_urls_param_override_wins():
|
||||
params = FakeParams()
|
||||
params.put(tbd.BASE_URL_PARAM, "https://my-r2.example.com/v1/")
|
||||
assert tbd.candidate_base_urls(params) == ["https://my-r2.example.com/v1"]
|
||||
|
||||
|
||||
def test_candidate_base_urls_private_endpoints_first():
|
||||
params = FakeParams()
|
||||
urls = tbd.candidate_base_urls(params)
|
||||
# embedded private endpoints (gitea) come before the public default
|
||||
assert urls[-1] == tbd.DEFAULT_TILE_BUNDLE_BASE_URL
|
||||
if tbd._private_base_urls is not None:
|
||||
assert any("git.konn3kt.com" in url for url in urls[:-1])
|
||||
assert tbd.request_auth() is not None
|
||||
|
||||
|
||||
def test_day_variant_downloaded_and_manifested(hosting, tmp_path):
|
||||
params, index, offline_root = hosting
|
||||
serve_root = (offline_root / ".." / "serve").resolve()
|
||||
day_bundle = serve_root / "us_state.CA_day.mbtiles"
|
||||
_make_mbtiles(day_bundle)
|
||||
day_payload = day_bundle.read_bytes()
|
||||
entry = index["regions"]["us_state.CA"]
|
||||
entry["day_path"] = "us_state.CA_day.mbtiles"
|
||||
entry["day_bytes"] = len(day_payload)
|
||||
entry["day_sha256"] = hashlib.sha256(day_payload).hexdigest()
|
||||
(serve_root / "index.json").write_text(json.dumps(index))
|
||||
|
||||
dl = tbd.TileBundleDownloader(params=params, mem_params=params)
|
||||
assert dl.download_regions(["us_state.CA"]) is True
|
||||
tiles = offline_root / "regions" / "us_state.CA" / "tiles"
|
||||
assert (tiles / "offline.mbtiles").exists()
|
||||
assert (tiles / "offline_day.mbtiles").exists()
|
||||
manifest = json.loads((tiles.parent / "manifest.json").read_text())
|
||||
assert manifest["mbtiles_day"]["sha256"] == entry["day_sha256"]
|
||||
# installed-and-current check must account for the day file
|
||||
assert dl._installed_matches("us_state.CA", entry) is True
|
||||
(tiles / "offline_day.mbtiles").unlink()
|
||||
assert dl._installed_matches("us_state.CA", entry) is False
|
||||
Reference in New Issue
Block a user