fix: exe 점검이 본 화면 대신 자막 오버레이를 보고 통과하던 것
Some checks failed
Windows / verify (push) Has been cancelled
Some checks failed
Windows / verify (push) Has been cancelled
"아무 창이나 뜨면 통과" 로 둔 탓에, 자막 오버레이 창(제목이 앱 이름뿐이다)이 잡혀서 본 화면이 안 떠도 통과할 수 있었다. 지난 두 번의 오보가 전부 같은 형태였다 — 느슨한 통과 조건. 어떤 창을 기다리는지 호출하는 쪽이 지정하게 바꾼다. 첫 실행은 저장 위치 화면, 두 번째 실행은 본 화면 제목에만 있는 표어를 기다린다. 찾은 창 목록을 전부 기록해 나중에 되짚을 수 있게 한다.
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user