Files
orca/native/computer-use-linux/runtime_accessibility_bus_test.py
T
Kelvin Amoaba d6ca3aae25 fix(computer): report an unreachable Linux accessibility bus instead of crashing (#42642)
* test(computer): run the Linux bridge against an unreachable accessibility bus

* fix(computer): report an unreachable AT-SPI bus instead of aborting the Linux bridge

* test(computer): read the bridge response past the registry banner

* test(computer): keep the healthy-bus check off the desktop's X display
2026-11-15 07:39:44 +00:00

53 lines
2.6 KiB
Python

import json
import os
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
RUNTIME = Path(__file__).with_name("runtime.py")
@unittest.skipUnless(sys.platform.startswith("linux"), "drives the real libatspi")
class AccessibilityBusTest(unittest.TestCase):
"""Runs the bridge against real libatspi in a private runtime dir, never the desktop's buses."""
def run_list_apps(self, runtime_dir, env, prefix=()):
operation = os.path.join(runtime_dir, "operation.json")
with open(operation, "w", encoding="utf-8") as handle:
json.dump({"tool": "list_apps"}, handle)
env = {**env, "XDG_RUNTIME_DIR": runtime_dir}
# Why files, not pipes: bus daemons the session activates inherit them and can outlive the bridge.
with tempfile.TemporaryFile("w+") as stdout, tempfile.TemporaryFile("w+") as stderr:
result = subprocess.run([*prefix, sys.executable, str(RUNTIME), operation],
env=env, stdout=stdout, stderr=stderr, timeout=60)
stdout.seek(0)
stderr.seek(0)
return subprocess.CompletedProcess(result.args, result.returncode, stdout.read(), stderr.read())
def test_unreachable_bus_returns_an_error_instead_of_aborting(self):
with tempfile.TemporaryDirectory() as runtime_dir:
missing = "unix:path=" + os.path.join(runtime_dir, "missing-bus")
result = self.run_list_apps(runtime_dir, {**os.environ, "DBUS_SESSION_BUS_ADDRESS": missing,
"AT_SPI_BUS_ADDRESS": missing})
self.assertEqual(result.returncode, 0, result.stderr)
response = json.loads(result.stdout)
self.assertFalse(response["ok"])
self.assertTrue(response["error"].startswith("unsupported_capability:"), response["error"])
@unittest.skipUnless(shutil.which("dbus-run-session"), "needs dbus-run-session")
def test_reachable_bus_lists_apps(self):
env = {name: value for name, value in os.environ.items()
if name not in ("DBUS_SESSION_BUS_ADDRESS", "AT_SPI_BUS_ADDRESS", "DISPLAY", "WAYLAND_DISPLAY")}
with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as runtime_dir:
result = self.run_list_apps(runtime_dir, env, ("dbus-run-session", "--"))
# Why the last line: the registry daemon the session activates shares stdout and prints a banner.
response = result.stdout.strip().splitlines()[-1:]
self.assertEqual((result.returncode, response), (0, ['{"ok":true,"apps":[]}']), result.stdout + result.stderr)
if __name__ == "__main__":
unittest.main()