blob: 0fa3fdc8e9f2cd9c40ed48b5d73de34fca74c5e0 [file] [edit]
#!/usr/bin/env python3
"""
WebAssembly Debugger Test Runner
"""
import argparse
import os
import sys
import time
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from lib.session import DebugSession
from lib.environment import WebKitEnvironment
from lib.utils import log_pass, log_fail, log_info, log_header, set_verbose
from tests.tests import ALL_TESTS
from tests.jsc import JavaScriptCoreTestCase
def get_optimal_worker_count():
cpu = os.cpu_count() or 4
if cpu <= 2:
return cpu
elif cpu <= 4:
return cpu + 1
elif cpu <= 8:
return cpu + 2
else:
return min(cpu + 4, 16)
def calculate_smart_workers(test_count, requested=None):
if requested is not None:
return requested
return min(test_count, get_optimal_worker_count())
def run_one(cls, port, env, verbose, verbose_wasm_debugger):
"""Run a single test class. Returns True on pass, False on fail."""
start = time.monotonic()
# JavaScriptCoreTestCase runs a subprocess directly — no DebugSession.
if getattr(cls, "test_file", None) is None:
try:
cls(env).execute()
log_pass(cls.__name__, time.monotonic() - start)
return True
except Exception as e:
log_fail(cls.__name__, time.monotonic() - start, e)
return False
extra = list(getattr(cls, "extra_jsc_options", None) or [])
if verbose_wasm_debugger:
extra.append("--verboseWasmDebugger=1")
try:
with DebugSession(
cls.test_file, port,
str(env.jsc_path), env.lldb_path, env.env,
extra, verbose, cls.__name__,
) as session:
instance = cls()
instance.session = session
instance.execute()
log_pass(cls.__name__, time.monotonic() - start)
return True
except Exception as e:
log_fail(cls.__name__, time.monotonic() - start, e)
return False
def main():
parser = argparse.ArgumentParser(description="WebAssembly Debugger Test Runner")
parser.add_argument("--verbose", "-v", action="store_true",
help="Print all LLDB/JSC output")
parser.add_argument("--verbose-wasm-debugger", action="store_true",
help="Pass --verboseWasmDebugger=1 to JSC")
parser.add_argument("--pid-tid", action="store_true",
help="Prefix output with [TestName] (implied by --verbose)")
parser.add_argument("--test", "-t", action="append", metavar="NAME",
help="Run specific test(s); may be repeated")
parser.add_argument("--list", "-l", action="store_true",
help="List available test cases and exit")
parser.add_argument("--parallel", "-p", type=int, nargs="?", const=-1, metavar="N",
help="Parallel workers (omit N for auto-detect)")
build_group = parser.add_mutually_exclusive_group()
build_group.add_argument("--debug", action="store_true", help="Use Debug build")
build_group.add_argument("--release", action="store_true", help="Use Release build")
args = parser.parse_args()
# --pid-tid and --verbose-wasm-debugger both imply verbose output
verbose = args.verbose or args.pid_tid or args.verbose_wasm_debugger
set_verbose(verbose)
build_config = "Debug" if args.debug else "Release" if args.release else None
env = WebKitEnvironment(Path(__file__), build_config)
all_tests = list(ALL_TESTS) + [JavaScriptCoreTestCase]
if args.list:
for cls in all_tests:
print(cls.__name__)
return
# Filter by --test names if given
if args.test:
requested = set(args.test)
tests = [cls for cls in all_tests if cls.__name__ in requested]
missing = requested - {cls.__name__ for cls in tests}
for name in sorted(missing):
log_info(f"Unknown test: {name}")
if not tests:
log_info("No matching tests found.")
sys.exit(1)
else:
tests = all_tests
# Assign stable ports: index in all_tests (not filtered list) so ports
# don't shift when --test filters a subset.
port_map = {cls: 12340 + i for i, cls in enumerate(all_tests)}
tasks = [(cls, port_map[cls], env, verbose, args.verbose_wasm_debugger)
for cls in tests]
parallel = args.parallel is not None
requested_workers = None if args.parallel == -1 else args.parallel
log_header(f"Running {len(tests)} test(s)" +
(f" — parallel={calculate_smart_workers(len(tests), requested_workers)}"
if parallel else " — sequential"))
if parallel:
workers = calculate_smart_workers(len(tests), requested_workers)
with ThreadPoolExecutor(max_workers=workers) as pool:
results = list(pool.map(lambda a: run_one(*a), tasks))
else:
results = [run_one(*a) for a in tasks]
passed = sum(results)
failed = len(results) - passed
log_header(f"{passed}/{len(results)} passed" + (f", {failed} failed" if failed else ""))
sys.exit(1 if failed else 0)
if __name__ == "__main__":
main()