#!/usr/bin/env python3 """Firefox AT-SPI baseline on a private X server, bus, and browser profile.""" import os from pathlib import Path import shutil import subprocess import sys import tempfile import time ROOT = Path(__file__).resolve().parents[1] PAGE = Path(__file__).with_name("firefox-baseline.html") def run_inside(): linked = os.environ.get("CTHULHU_FIREFOX_LINKED") == "1" page = Path(__file__).with_name("browser-linked-headings.html") if linked else PAGE browser = os.environ.get("CTHULHU_FIREFOX") or shutil.which("firefox") if not browser: raise RuntimeError("Firefox executable not found") with tempfile.TemporaryDirectory(prefix="cthulhu-firefox-profile-") as profile: Path(profile, "user.js").write_text( 'user_pref("datareporting.policy.firstRunURL", "");\n' 'user_pref("browser.startup.homepage_override.mstone", "ignore");\n', encoding="utf-8", ) with tempfile.TemporaryFile() as log: environment = dict(os.environ, MOZ_ACCESSIBILITY_ENABLED="1") process = subprocess.Popen( [browser, "--no-remote", "--profile", profile, "--new-window", page.resolve().as_uri()], env=environment, stdout=log, stderr=log, ) try: deadline = time.monotonic() + 25 while time.monotonic() < deadline: if process.poll() is not None: log.seek(0) raise RuntimeError("Firefox exited before the probe: " + log.read().decode(errors="replace")) result = subprocess.run( ["xdotool", "search", "--onlyvisible", "--name", "Cthulhu Linked Headings Probe" if linked else "Cthulhu Firefox Baseline"], capture_output=True, text=True, ) if result.stdout.strip(): window = result.stdout.splitlines()[0] subprocess.run(["xdotool", "windowfocus", window], check=True) break time.sleep(0.1) else: raise RuntimeError("Firefox fixture window missing") environment["CTHULHU_NATIVE_FIREFOX"] = "1" environment.setdefault("GOCACHE", "/tmp/cthulhu-cache") if linked: environment["CTHULHU_NATIVE_LINKED_HEADINGS"] = "1" subprocess.run( ["go", "test", "./internal/app", "-run", "^TestNativeLinkedHeadings$", "-count=1", "-v", "-timeout=45s"], cwd=ROOT, env=environment, check=True, timeout=50, ) return subprocess.run( ["go", "test", "./internal/atspi", "-run", "^TestNativeFirefoxBaseline$", "-count=1", "-v", "-timeout=45s"], cwd=ROOT, env=environment, check=True, timeout=55, ) subprocess.run( ["go", "test", "./internal/app", "-run", "^TestNativeBrowserTabs$", "-count=1", "-v", "-timeout=30s"], cwd=ROOT, env=environment, check=True, timeout=35, ) finally: process.terminate() try: process.wait(timeout=5) except subprocess.TimeoutExpired: process.kill() process.wait(timeout=5) def main(): if len(sys.argv) == 2 and sys.argv[1] == "--inside": run_inside() return read_fd, write_fd = os.pipe() server = subprocess.Popen( ["Xvfb", "-displayfd", str(write_fd), "-screen", "0", "800x600x24", "-nolisten", "tcp"], pass_fds=(write_fd,), stderr=subprocess.DEVNULL, ) os.close(write_fd) try: with os.fdopen(read_fd) as reader: display = reader.readline().strip() if not display: raise RuntimeError("Xvfb failed to start") environment = dict(os.environ, DISPLAY=":" + display, XDG_SESSION_TYPE="x11") environment.pop("DBUS_SESSION_BUS_ADDRESS", None) environment.pop("AT_SPI_BUS_ADDRESS", None) environment.pop("WAYLAND_DISPLAY", None) subprocess.run( ["dbus-run-session", "--", sys.executable, str(Path(__file__).resolve()), "--inside"], env=environment, check=True, timeout=90, ) finally: server.terminate() server.wait(timeout=3) if __name__ == "__main__": main()