diff --git a/packaging/windows_smoke.py b/packaging/windows_smoke.py index 567caba..64c281d 100644 --- a/packaging/windows_smoke.py +++ b/packaging/windows_smoke.py @@ -473,13 +473,19 @@ def _terminate_tree(proc: subprocess.Popen) -> None: child.kill() -def _launch_and_wait_for_window(exe: Path, appdata: Path, timeout_s: int = 90) -> str: - """exe 를 띄우고 **실제 창이 뜰 때까지** 기다린다. +def _launch_and_wait_for_window( + exe: Path, appdata: Path, require: str, timeout_s: int = 90 +) -> str: + """exe 를 띄우고 **기다리던 창이 뜰 때까지** 기다린다. 예전에는 "8초 뒤에도 프로세스가 살아 있으면 통과" 였다. 그런데 진입 스크립트가 터지면 PyInstaller 가 오류창을 띄우고 그대로 서 있는다 — 프로세스는 멀쩡히 살아 있으므로 통과로 보고됐다. 실제로 그 상태의 exe 가 - 릴리스까지 나갔다. 살아 있다는 것은 떴다는 뜻이 아니다. 창을 봐야 한다. + 릴리스까지 나갔다. 살아 있다는 것은 떴다는 뜻이 아니다. + + "아무 창이나 뜨면 통과" 도 부족하다. 자막 오버레이는 제목이 앱 이름 + 그대로라, 본 화면이 안 떠도 그게 잡혀 통과해 버린다. 그래서 어떤 창을 + 기다리는지(`require`)를 호출하는 쪽이 지정한다. """ env = dict(os.environ) env["APPDATA"] = str(appdata) @@ -493,14 +499,14 @@ def _launch_and_wait_for_window(exe: Path, appdata: Path, timeout_s: int = 90) - err = (proc.stderr.read() or b"").decode("utf-8", "replace")[-800:] raise RuntimeError(f"창이 뜨기 전에 종료 (코드 {proc.returncode}): {err}") - for title in _visible_windows(_process_tree(proc.pid)): - low = title.lower() - if any(marker in low for marker in _CRASH_MARKERS): + titles = _visible_windows(_process_tree(proc.pid)) + for title in titles: + if any(marker in title.lower() for marker in _CRASH_MARKERS): raise RuntimeError(f"오류창이 떴습니다: {title!r}") - if "livesub" in low: - return title + if any(require in title for title in titles): + return " / ".join(titles) time.sleep(1) - raise RuntimeError(f"{timeout_s}초 안에 창이 뜨지 않았습니다") + raise RuntimeError(f"{timeout_s}초 안에 {require!r} 창이 뜨지 않았습니다") finally: _terminate_tree(proc) @@ -536,8 +542,8 @@ def run_exe_check(exe: Path) -> None: def _first_run(): # APPDATA 를 빈 폴더로 돌려 "처음 켜는 사람" 을 그대로 재현한다. with _scratch_appdata() as tmp: - title = _launch_and_wait_for_window(exe, appdata=tmp) - return f"창 제목 {title!r}" + titles = _launch_and_wait_for_window(exe, appdata=tmp, require="저장 위치") + return f"창 {titles!r}" @check("exe 두 번째 실행 — 본 화면") def _second_run(): @@ -555,10 +561,14 @@ def run_exe_check(exe: Path) -> None: (data / "config.json").write_text( json.dumps({"models": {"preload_on_start": False}}), encoding="utf-8" ) - title = _launch_and_wait_for_window(exe, appdata=tmp) - if "저장 위치" in title: - raise RuntimeError(f"위치를 이미 골랐는데 또 물어봤습니다: {title!r}") - return f"창 제목 {title!r}" + # 자막 오버레이는 제목이 앱 이름뿐이라 그것만으로는 본 화면이 떴다는 + # 증거가 못 된다. 본 화면 제목에만 있는 표어를 기다린다. + from livesub.constants import APP_SLOGAN + + titles = _launch_and_wait_for_window(exe, appdata=tmp, require=APP_SLOGAN) + if any("저장 위치" in t for t in titles.split(" / ")): + raise RuntimeError(f"위치를 이미 골랐는데 또 물어봤습니다: {titles!r}") + return f"창 {titles!r}" def main() -> int: