Cthulhu is no longer a fork. Now it is a screen reader written in Golang.
This commit is contained in:
1 parent
a998fe67de
commit
069fc674dc
708 files changed
+112482
-532
No files matched your search
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Chromium AT-SPI proof 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
|
||||
|
||||
|
||||
BROWSER_PROOFS = {
|
||||
"fragments": ("./internal/review", "^TestNativeBrowserFragmentLines$", 30, 35, "browser-fragments.html"),
|
||||
"positions": ("./internal/atspi", "^TestNativeBrowserSetPositions$", 25, 30, "browser-positions.html"),
|
||||
"tabs": ("./internal/app", "^TestNativeBrowserTabs$", 30, 35),
|
||||
"namedlinks": ("./internal/app", "^TestNativeBrowserNamedLinks$", 25, 30, "browser-named-links.html"),
|
||||
"linkedheadings": ("./internal/app", "^TestNativeLinkedHeadings$", 45, 50, "browser-linked-headings.html"),
|
||||
"catalog": ("./internal/atspi", "^TestNativeBrowserCatalogCapture$", 30, 35, "browser-catalog.html"),
|
||||
"lines": ("./internal/atspi", "^TestNativeBrowserLogicalLines$", 25, 30, "browser-lines.html"),
|
||||
"tables": ("./internal/app", "^TestNativeBrowserTables$", 35, 40),
|
||||
"dynamic": ("./internal/app", "^TestNativeBrowserDynamicNavigation$", 40, 45),
|
||||
"browser": ("./internal/atspi", "^TestNativeBrowser$", 45, 50),
|
||||
"live": ("./internal/atspi", "^TestNativeBrowserLiveRegion$", 30, 35),
|
||||
"document": ("./internal/app", "^TestNativeBrowserDocumentCommands$", 45, 50),
|
||||
"editing": ("./internal/app", "^TestNativeBrowserEditing$", 45, 50),
|
||||
"selection": ("./internal/app", "^TestNativeBrowserSelection$", 35, 40),
|
||||
"modes": ("./internal/app", "^TestNativeBrowserModes$", 45, 50),
|
||||
"keys": ("./internal/platform/xorg", "^TestNativeBrowseKeyTransitions$", 20, 25),
|
||||
}
|
||||
|
||||
|
||||
def selected_proofs():
|
||||
selected = {name.strip() for name in os.environ.get(
|
||||
"CTHULHU_NATIVE_BROWSER_TESTS", ",".join(BROWSER_PROOFS)).split(",") if name.strip()}
|
||||
unknown = selected - BROWSER_PROOFS.keys()
|
||||
if not selected or unknown:
|
||||
raise ValueError("Select browser proofs from: " + ",".join(BROWSER_PROOFS) +
|
||||
("; unknown: " + ",".join(sorted(unknown)) if unknown else ""))
|
||||
return selected
|
||||
|
||||
|
||||
def run_inside():
|
||||
browser = os.environ.get("CTHULHU_BROWSER") or shutil.which("brave")
|
||||
if not browser:
|
||||
raise RuntimeError("Set CTHULHU_BROWSER to a Chromium-family executable")
|
||||
selected = selected_proofs()
|
||||
for name, proof in BROWSER_PROOFS.items():
|
||||
if name in selected:
|
||||
print(f"Running browser proof: {name}", flush=True)
|
||||
# F7 toggles a profile-wide setting. Each proof must start from
|
||||
# the same browser state whether run alone or after editing.
|
||||
run_browser_proof(browser, proof)
|
||||
|
||||
|
||||
def run_browser_proof(browser, proof):
|
||||
with tempfile.TemporaryDirectory(prefix="cthulhu-browser-profile-") as profile:
|
||||
with tempfile.TemporaryFile() as log:
|
||||
process = subprocess.Popen([
|
||||
browser, "--user-data-dir=" + profile, "--no-first-run",
|
||||
"--no-default-browser-check", "--disable-background-networking",
|
||||
"--disable-component-update", "--disable-sync", "--password-store=basic",
|
||||
"--force-renderer-accessibility", "--ozone-platform=x11",
|
||||
"--new-window", Path(__file__).with_name(proof[4] if len(proof) > 4 else "browser.html").resolve().as_uri(),
|
||||
], stdout=log, stderr=log)
|
||||
try:
|
||||
deadline = time.monotonic() + 20
|
||||
while time.monotonic() < deadline:
|
||||
if process.poll() is not None:
|
||||
raise RuntimeError("Browser exited before the probe")
|
||||
window_name = "Cthulhu Linked Headings Probe" if proof[1] == "^TestNativeLinkedHeadings$" else "Cthulhu Browser Probe"
|
||||
result = subprocess.run(
|
||||
["xdotool", "search", "--onlyvisible", "--name", window_name],
|
||||
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("Browser fixture window missing")
|
||||
environment = dict(os.environ, CTHULHU_NATIVE_BROWSER="1")
|
||||
if proof[1] == "^TestNativeLinkedHeadings$":
|
||||
environment["CTHULHU_NATIVE_LINKED_HEADINGS"] = "1"
|
||||
environment.setdefault("GOCACHE", "/tmp/cthulhu-cache")
|
||||
package, expression, testTimeout, runTimeout = proof[:4]
|
||||
subprocess.run(["go", "test", package, "-run", expression,
|
||||
"-count=1", "-v", f"-timeout={testTimeout}s"],
|
||||
env=environment, check=True, timeout=runTimeout,
|
||||
cwd=Path(__file__).resolve().parents[1])
|
||||
finally:
|
||||
process.terminate()
|
||||
try:
|
||||
process.wait(timeout=5)
|
||||
except subprocess.TimeoutExpired:
|
||||
process.kill()
|
||||
process.wait(timeout=5)
|
||||
|
||||
|
||||
def main():
|
||||
selected = selected_proofs()
|
||||
if len(sys.argv) == 2 and sys.argv[1] == "--inside":
|
||||
run_inside()
|
||||
return
|
||||
with tempfile.TemporaryDirectory(prefix="cthulhu-browser-"):
|
||||
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,
|
||||
# Budget each isolated browser startup, bounded Go run and
|
||||
# cleanup instead of imposing one 120-second suite limit.
|
||||
timeout=15 + sum(30 + BROWSER_PROOFS[name][3] for name in selected),
|
||||
)
|
||||
finally:
|
||||
server.terminate()
|
||||
server.wait(timeout=3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user