Files
ios-location-track/tests/offline_tiles.py
T
jbjf6666 85e9a53750 docs: 新增 Windows 移植交接文档
docs/WINDOWS_PORT.md 覆盖:
- 逐文件可移植性审计(核心逻辑层约 90% 可直接复用)
- 核心难点:iOS 17+ 的 RSD 隧道无法在 Windows 复用(macOS 实现依赖 XPC +
  ObjC runtime 私有框架),改用 pymobiledevice3 的 userspace 隧道
- 已实测确认的命令与 API(--userspace 挂在 lockdown start-tunnel / mounter
  auto-mount 上,remote start-tunnel 不支持;类名是 UserspaceRsdTunnel)
- 窗口启动、数据目录、打包与分发(DMG → Inno Setup)、图标(.icns → .ico)
- macOS 侧踩过的坑:镜像挂载用 Python API、必须打包包元数据、不要把 distutils
  放进 excludes、AlreadyMountedError 当成功处理
- 设计约束(DVT 长连接、只能改经纬度、last_point 与 last_reported 分离等)
- 验收基线(复用现有测试套件)与建议的工作顺序
2026-10-16 01:05:09 +08:00

111 lines
4.1 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.
#!/usr/bin/env python3
"""瓦片离线可用验证:回源成功后断网,验证缓存仍能供图。
做法:用真实的 core.tilecache.TileCache,先把 urllib 换成"永远失败"的实现(模拟断网),
再请求已经缓存过的瓦片,断言仍然返回 200 + 合法图片;同时断言未缓存过的瓦片会优雅降级
(返回占位图 + status != 200,由上层回 404 让前端走重试)。
运行:./.venv/bin/python tests/offline_tiles.py
"""
from __future__ import annotations
import sys
import time
import urllib.request
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
from core import config_store, tilecache # noqa: E402
PASS = 0
FAIL = 0
FAILURES: list[str] = []
def check(name: str, ok: bool, detail: str = "") -> None:
global PASS, FAIL
if ok:
PASS += 1
print(f" \033[32m✓\033[0m {name}")
else:
FAIL += 1
FAILURES.append(f"{name} {detail}")
print(f" \033[31m✗\033[0m {name} {detail}")
def main() -> int:
print("\033[1m瓦片本地缓存 / 离线可用验证\033[0m")
cache_dir = Path("/tmp/iosloc-offline-tiles")
import shutil
shutil.rmtree(cache_dir, ignore_errors=True)
source_key = "amap"
source = config_store.DEFAULT_TILE_SOURCES[source_key]
# 北京天安门附近、z12 —— 本机实测可达
z, x, y = 12, 3367, 1580
cache = tilecache.TileCache(cache_dir, max_mb=64)
print("\n1) 联网回源并落盘")
online = cache.get_tile(source_key, source, z, x, y)
check("回源返回真实图片", online.status == 200 and online.body[:4] in (b"\x89PNG", b"\xff\xd8"),
f"status={online.status} source={online.source}")
check("标记为 network", online.source == "network", online.source)
check("已写入磁盘缓存", any(cache_dir.rglob("*")), "缓存目录为空")
print("\n2) 断网(把 urllib 换成永远失败)后请求同一瓦片")
real_urlopen = urllib.request.urlopen
def offline(*_args, **_kwargs):
raise OSError("模拟断网:Network is unreachable")
urllib.request.urlopen = offline
try:
cached = cache.get_tile(source_key, source, z, x, y)
check("离线仍返回 200", cached.status == 200, f"status={cached.status}")
check("来源为 cache", cached.source == "cache", cached.source)
check("图片内容与联网时一致", cached.body == online.body,
f"{len(cached.body)}B vs {len(online.body)}B")
print("\n3) 断网 + 未缓存瓦片 → 优雅降级(不抛异常)")
t0 = time.time()
miss = cache.get_tile(source_key, source, z, x, y + 1)
elapsed = time.time() - t0
check("未命中不抛异常", True, "")
check("返回占位图且 status!=200(上层回 404 让前端重试)",
miss.status != 200 and miss.body[:4] == b"\x89PNG",
f"status={miss.status} source={miss.source}")
check("失败快速返回(<5s)", elapsed < 5.0, f"{elapsed:.2f}s")
print("\n4) 缓存统计")
stats = cache.stats
check("命中计数增加", stats["hit"] >= 1, str(stats))
check("统计到离线失败", stats["failed"] >= 1, str(stats))
finally:
urllib.request.urlopen = real_urlopen
print("\n5) 恢复网络后仍可正常回源")
cache2 = tilecache.TileCache(cache_dir, max_mb=64)
other = cache2.get_tile(source_key, source, z, x + 1, y)
check("恢复后回源成功", other.status == 200 and other.body[:4] in (b"\x89PNG", b"\xff\xd8"),
f"status={other.status} source={other.source}")
print("\n6) 容量上限淘汰不破坏可用性")
small = tilecache.TileCache(cache_dir, max_mb=1)
small._sweep()
check("淘汰后仍能读取", small.get_tile(source_key, source, z, x, y).status in (200, 404))
shutil.rmtree(cache_dir, ignore_errors=True)
print(f"\n{'=' * 56}")
print(f"通过 \033[32m{PASS}\033[0m 项,失败 \033[31m{FAIL}\033[0m 项")
for item in FAILURES:
print(" -", item)
return 0 if FAIL == 0 else 1
if __name__ == "__main__":
raise SystemExit(main())