44 python loop-tests.py <iterations> [-- <test.bat args>...]
55"""
66
7+ import atexit
78import re
89import shutil
910import subprocess
2930_CURSOR_COL_1 = f"{ _CSI } G" # CHA - move cursor to column 1 (1-based)
3031_HIDE_CURSOR = f"{ _CSI } ?25l" # DECTCEM - hide the cursor
3132_SHOW_CURSOR = f"{ _CSI } ?25h" # DECTCEM - show the cursor
33+ _SGR_RESET = f"{ _CSI } 0m" # SGR - reset all attributes (colors, bold, ...)
3234# Reset the current line: move cursor home then erase, so the next write
3335# starts from a clean slate regardless of what was there before.
3436_RESET_LINE = _CURSOR_COL_1 + _ERASE_LINE
@@ -51,6 +53,7 @@ def __init__(self, total: int):
5153 self ._enabled = sys .stdout .isatty ()
5254 self ._cursor_hidden = False
5355 self ._thread = threading .Thread (target = self ._loop , daemon = True )
56+ atexit .register (self ._safe_restore )
5457
5558 def start (self ) -> None :
5659 self ._thread .start ()
@@ -61,6 +64,7 @@ def stop(self) -> None:
6164 with self ._lock :
6265 self ._clear_locked ()
6366 self ._restore_cursor_locked ()
67+ self ._safe_restore ()
6468
6569 def begin_iteration (self , iteration : int ) -> None :
6670 with self ._lock :
@@ -133,31 +137,26 @@ def _restore_cursor_locked(self) -> None:
133137 sys .stdout .flush ()
134138 self ._cursor_hidden = False
135139
140+ def _safe_restore (self ) -> None :
141+ if not self ._enabled :
142+ return
143+ sys .stdout .write (_SHOW_CURSOR + _SGR_RESET )
144+ sys .stdout .flush ()
136145
137- def _run_wpr (args : list [str ]) -> bool :
138- """Invoke wpr. Returns True on success; on failure prints a brief notice (without wpr's output)."""
139146
147+ def _run_wpr (args : list [str ]) -> None :
148+ """Invoke wpr; raises subprocess.CalledProcessError on non-zero exit."""
140149
141150 si = subprocess .STARTUPINFO ()
142151 si .dwFlags |= subprocess .STARTF_USESHOWWINDOW
143- si .wShowWindow = subprocess .SW_HIDE # 0
144-
152+ si .wShowWindow = subprocess .SW_HIDE # 0
145153
146- result = subprocess .run (
154+ subprocess .run (
147155 ["wpr" , * args ],
148- stdout = subprocess .DEVNULL ,
149- stderr = subprocess .DEVNULL ,
150156 creationflags = subprocess .CREATE_NEW_CONSOLE ,
151- startupinfo = si
152- )
153- if result .returncode == 0 :
154- return True
155- click .secho (
156- f"\n $ wpr { ' ' .join (args )} (exit { result .returncode } )" ,
157- fg = "yellow" ,
158- err = True ,
157+ startupinfo = si ,
158+ check = True ,
159159 )
160- return False
161160
162161
163162def _timestamp_prefix () -> str :
@@ -214,15 +213,8 @@ def _run_iteration(
214213 # Start ETL trace before launching tests.
215214 etl_started = False
216215 if wprp is not None :
217- if _run_wpr (["-start" , str (wprp ), "-filemode" ]):
218- etl_started = True
219- else :
220- with status .pause ():
221- click .secho (
222- f"Iteration { iteration } : failed to start ETL trace; continuing without trace." ,
223- fg = "yellow" ,
224- err = True ,
225- )
216+ _run_wpr (["-start" , str (wprp ), "-filemode" ])
217+ etl_started = True
226218
227219 cmd = [str (test_bat ), * test_args ]
228220 return_code = 1
@@ -326,14 +318,29 @@ def _cancel_active_trace() -> None:
326318 default = str (DEFAULT_WPRP ),
327319 type = click .Path (dir_okay = False ),
328320 show_default = True ,
329- help = "ETL profile passed to `wpr -start`." ,
321+ help = "ETL profile passed to `wpr -start` (passed verbatim; wpr validates it) ." ,
330322)
331323@click .option (
332324 "--no-etl" ,
333325 is_flag = True ,
334326 default = False ,
335327 help = "Skip ETL tracing (useful when wpr is not available or not needed)." ,
336328)
329+ @click .option (
330+ "--platform" ,
331+ "platform_" ,
332+ type = click .Choice (["x64" , "arm64" ], case_sensitive = False ),
333+ default = "x64" ,
334+ show_default = True ,
335+ help = "Target platform subfolder under bin/ where test.bat lives." ,
336+ )
337+ @click .option (
338+ "--target" ,
339+ type = click .Choice (["Debug" , "Release" ], case_sensitive = False ),
340+ default = "Debug" ,
341+ show_default = True ,
342+ help = "Build configuration subfolder under bin/<platform>/ where test.bat lives." ,
343+ )
337344@click .option (
338345 "--stop-on-failure/--continue-on-failure" ,
339346 default = True ,
@@ -352,21 +359,21 @@ def main(
352359 output_dir : str ,
353360 wprp : str ,
354361 no_etl : bool ,
362+ platform_ : str ,
363+ target : str ,
355364 stop_on_failure : bool ,
356365 keep_success_logs : bool ,
357366) -> None :
358- test_bat_path = REPO_ROOT / "bin" / "x64" / "Debug" / "test.bat"
367+ platform_ = platform_ .lower ()
368+ target = target .capitalize ()
369+ test_bat_path = REPO_ROOT / "bin" / platform_ / target / "test.bat"
359370
360371 if not test_bat_path .is_file ():
361372 raise click .ClickException (f"test.bat not found at: { test_bat_path } " )
362373
363374 wprp_path : Path | None = None
364375 if not no_etl :
365376 wprp_path = Path (wprp ).resolve ()
366- if not wprp_path .is_file ():
367- raise click .ClickException (
368- f"ETL profile not found at: { wprp_path } (pass --no-etl to skip tracing)."
369- )
370377
371378 base_dir = Path (output_dir ).resolve ()
372379 run_dir = base_dir / datetime .now ().strftime ("%Y-%m-%d_%H-%M-%S" )
0 commit comments