Files
Aislo/resources/tester/test_249_1_b08_milestone_capture_child.py
T

98 lines
4.0 KiB
Python

"""249-1 B08 거리이정표 자동 그림 = 자식 프로세스 캡처(사용자 148번).
서버(uvicorn · Windows) = WindowsSelectorEventLoopPolicy → 스레드 안 sync_playwright 가
드라이버 하위 프로세스를 못 만듦. 같은 정책 · 스레드에서:
① 옛 길(같은 프로세스 shoot) = 「브라우저 못 띄움」 재현
② make_route_image(자식 파이썬 프로세스) = 실제 Edge 로 PNG 생김 · 키 값 응답 0
③ 자식 시간 넘김 · 자식 죽음 = 까닭 한 줄
카카오 SDK 대신 단색 페이지(이 파일을 자식 스크립트로 · page_html 만 바꿈) — 네트워크 없음.
"""
import asyncio
import json
import sys
import threading
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from B08_Quantity import B08_Quantity_Milestone_Capture as cap # noqa: E402
HTML = (
'<!doctype html><body style="margin:0"><div id="m" style="width:1200px;height:800px;'
'background:#E60046"></div><script>window.__done = true</script></body>'
)
KEY = "fake-js-key-249"
LINES = [[[35.6866, 127.9095], [35.66, 127.85]]]
MARKS = [{"name": "거창군청", "lon": 127.9095, "lat": 35.6866}]
if __name__ == "__main__": # 자식 프로세스 — 카카오 페이지만 단색 페이지로
cap.page_html = lambda key, lines, marks, view=None: HTML
print(json.dumps(cap.capture(json.loads(sys.stdin.buffer.read()))))
sys.exit(0)
import pytest # noqa: E402
pytestmark = pytest.mark.skipif(sys.platform != "win32", reason="Windows Selector 루프 재현")
def in_server_thread(fn):
"""uvicorn 처럼 Selector 정책을 건 채 스레드(asyncio.to_thread 자리)에서 fn()."""
got = {}
def run():
try:
got["value"] = fn()
except BaseException as error: # noqa: BLE001 — 시험에서 그대로 봄
got["error"] = error
before = asyncio.get_event_loop_policy()
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
try:
job = threading.Thread(target=run)
job.start()
job.join(60)
finally:
asyncio.set_event_loop_policy(before)
return got
def test_옛_길_같은_프로세스는_못_띄움():
pytest.importorskip("playwright.sync_api")
got = in_server_thread(lambda: cap.shoot(HTML, cap.DEFAULT_ORIGIN))
assert isinstance(got.get("error"), cap.CaptureError)
assert str(got["error"]) == cap.NO_BROWSER
def test_자식_프로세스로_PNG_생김(tmp_path, monkeypatch):
pytest.importorskip("playwright.sync_api")
monkeypatch.setattr(cap, "CHILD", __file__)
monkeypatch.setenv(cap.KAKAO_JS, KEY)
monkeypatch.delenv(cap.ORIGIN_KEY, raising=False)
out = tmp_path / "auto"
got = in_server_thread(lambda: cap.make_route_image(out, "r1", LINES, MARKS))
assert got.get("value") == {"saved": True, "reason": None}, got
png = (out / "r1.png").read_bytes()
assert png[:8] == b"\x89PNG\r\n\x1a\n" and len(png) > 1000
assert sorted(p.name for p in out.iterdir()) == ["r1.png"] # .new 남지 않음
def test_자식_시간_넘김_죽음_까닭(tmp_path, monkeypatch):
monkeypatch.setenv(cap.KAKAO_JS, KEY)
tail = " — [그림 변경]으로 넣으세요."
head = "경로 그림 자동 캡처 실패 — "
hang = tmp_path / "hang.py"
hang.write_text("import time\ntime.sleep(30)\n", encoding="utf-8")
monkeypatch.setattr(cap, "CHILD", str(hang))
monkeypatch.setattr(cap, "CHILD_TIMEOUT_S", 1)
got = cap.make_route_image(tmp_path / "auto", "r1", LINES, MARKS)
assert got == {"saved": False, "reason": head + "지도 그림이 20초 안에 안 끝남" + tail}
dead = tmp_path / "dead.py"
dead.write_text(f"raise SystemExit('boom {KEY}')\n", encoding="utf-8")
monkeypatch.setattr(cap, "CHILD", str(dead))
got = cap.make_route_image(tmp_path / "auto", "r1", LINES, MARKS)
assert got == {"saved": False, "reason": head + cap.NO_BROWSER + tail}
assert not (tmp_path / "auto").exists()