Files
cthulhu/tests/native_libreoffice.py

122 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""Real Calc through the production reader, in a private X11/AT-SPI session."""
import os
from pathlib import Path
import shutil
import signal
import subprocess
import sys
import tempfile
import time
def wait_for(check, message, timeout=15):
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
result = check()
if result:
return result
time.sleep(0.05)
raise RuntimeError(message)
def run_inside():
import gi
gi.require_version("Atspi", "2.0")
from gi.repository import Atspi
with tempfile.TemporaryDirectory(prefix="cthulhu-libreoffice-") as temporary:
root = Path(temporary)
profile = root / "profile" / "user"
profile.mkdir(parents=True)
(profile / "registrymodifications.xcu").write_text(
'<oor:items xmlns:oor="http://openoffice.org/2001/registry">'
'<item oor:path="/org.openoffice.Office.Common/Misc">'
'<prop oor:name="FirstRun" oor:op="fuse"><value>false</value></prop>'
'<prop oor:name="ShowTipOfTheDay" oor:op="fuse"><value>false</value></prop>'
'</item></oor:items>', encoding="utf-8",
)
document = root / "Example.fods"
shutil.copyfile(Path(__file__).with_name("libreoffice.fods"), document)
environment = dict(os.environ, SAL_USE_VCLPLUGIN="gtk3",
LC_ALL="C.UTF-8",
GTK_MODULES="gail:atk-bridge", NO_AT_BRIDGE="0",
XDG_CONFIG_HOME=str(root / "config"),
XDG_CACHE_HOME=str(root / "cache"),
XDG_DATA_HOME=str(root / "data"))
application = subprocess.Popen(
["libreoffice", "-env:UserInstallation=" + (root / "profile").as_uri(),
"--norestore", "--nodefault", "--nofirststartwizard", "--calc", str(document)],
env=environment, start_new_session=True,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
try:
def find_window():
result = subprocess.run(
["xdotool", "search", "--onlyvisible", "--class", "libreoffice-calc"],
capture_output=True, text=True, check=False,
)
return result.stdout.splitlines()[-1] if result.stdout.strip() else None
window = wait_for(find_window, "Calc window missing")
# Wait for the actual accessible sheet, without enumerating its
# millions of cells. Dismiss disposable first-run UI if present.
def ready():
pending = [Atspi.get_desktop(0)]
remaining = 2500
found = False
while pending and remaining:
node = pending.pop()
remaining -= 1
role = node.get_role_name()
if role == "table":
parent = node.get_parent()
found |= parent is not None and parent.get_role_name() == "document spreadsheet"
continue
if role == "dialog" and node.get_name() == "Welcome to LibreOffice!":
subprocess.run(["xdotool", "key", "Escape"], check=True)
return False
if role == "table cell":
continue
for index in range(min(node.get_child_count(), 200)):
child = node.get_child_at_index(index)
if child is not None:
pending.append(child)
return found
wait_for(ready, "Calc accessible sheet missing")
environment.update(CTHULHU_NATIVE_LIBREOFFICE="1", CTHULHU_CALC_WINDOW=window)
environment.setdefault("GOCACHE", "/tmp/cthulhu-go-cache")
subprocess.run(
["go", "test", "./internal/app", "-run", "^TestNativeLibreOfficeCalc$",
"-count=1", "-timeout=50s", "-v"],
cwd=Path(__file__).resolve().parents[1], env=environment,
check=True, timeout=60,
)
finally:
if application.poll() is None:
os.killpg(application.pid, signal.SIGTERM)
application.wait(timeout=5)
def main():
if sys.argv[1:] == ["--inside"]:
run_inside()
return
with tempfile.TemporaryDirectory(prefix="cthulhu-libreoffice-session-") as temporary:
environment = dict(os.environ, XDG_SESSION_TYPE="x11",
XDG_CONFIG_HOME=temporary, XDG_CACHE_HOME=temporary,
XDG_DATA_HOME=temporary)
for name in ("DBUS_SESSION_BUS_ADDRESS", "AT_SPI_BUS_ADDRESS", "WAYLAND_DISPLAY"):
environment.pop(name, None)
subprocess.run(
["xvfb-run", "-a", "dbus-run-session", "--", sys.executable,
str(Path(__file__).resolve()), "--inside"],
env=environment, check=True, timeout=90,
)
if __name__ == "__main__":
main()