Add native Wine accessibility bridge

This commit is contained in:
Storm Dragon
2026-07-17 18:17:23 -04:00
parent 0576b6f79f
commit c9f56cb7ed
24 changed files with 1188 additions and 8 deletions
+134
View File
@@ -0,0 +1,134 @@
import unittest
from unittest import mock
from cthulhu.wine_access_manager import PrefixProcess, WineAccessManager
class WineAccessManagerTest(unittest.TestCase):
def test_prefix_prefers_explicit_wineprefix(self):
environment = {
"WINEPREFIX": "/tmp/custom-prefix",
"STEAM_COMPAT_DATA_PATH": "/tmp/compat",
}
self.assertEqual(
WineAccessManager._prefix_from_environ(environment), "/tmp/custom-prefix"
)
def test_prefix_uses_proton_pfx(self):
environment = {"STEAM_COMPAT_DATA_PATH": "/tmp/compat"}
self.assertEqual(WineAccessManager._prefix_from_environ(environment), "/tmp/compat/pfx")
@mock.patch("cthulhu.wine_access_manager.os.access", return_value=True)
@mock.patch(
"cthulhu.wine_access_manager.os.readlink",
return_value="/opt/proton/files/bin/wine64-preloader",
)
def test_preloader_resolves_to_runtime_loader(self, _readlink, _access):
self.assertEqual(
WineAccessManager._loader_for_process(100, {}),
"/opt/proton/files/bin/wine64",
)
@mock.patch("cthulhu.wine_access_manager.subprocess.Popen")
def test_launch_preserves_prefix_and_runtime_loader(self, popen):
process = mock.Mock()
process.poll.return_value = None
popen.return_value = process
manager = WineAccessManager("/usr/bin/cthulhu-wine-access.exe")
state = PrefixProcess()
manager._ensure_running(
"/tmp/prefix",
state,
{"DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus"},
"/opt/proton/files/bin/wine64",
10.0,
)
environment = popen.call_args.kwargs["env"]
self.assertEqual(environment["WINEPREFIX"], "/tmp/prefix")
self.assertEqual(environment["WINELOADER"], "/opt/proton/files/bin/wine64")
self.assertEqual(environment[manager.MANAGED_ENVIRONMENT_KEY], "1")
self.assertEqual(
environment["DBUS_SESSION_BUS_ADDRESS"], "unix:path=/run/user/1000/bus"
)
self.assertEqual(popen.call_args.args[0], ["/usr/bin/cthulhu-wine-access.exe"])
@mock.patch("cthulhu.wine_access_manager.subprocess.Popen")
def test_disabled_surfaces_are_passed_to_helper(self, popen):
process = mock.Mock()
process.poll.return_value = None
popen.return_value = process
manager = WineAccessManager("/usr/bin/cthulhu-wine-access.exe")
manager.dialogReaderEnabled = False
manager.controllerEnabled = False
manager._ensure_running("/tmp/prefix", PrefixProcess(), {}, None, 10.0)
self.assertEqual(
popen.call_args.args[0],
[
"/usr/bin/cthulhu-wine-access.exe",
"--no-dialog-reader",
"--no-controller",
],
)
def test_short_lived_helper_gets_restart_backoff(self):
manager = WineAccessManager("/usr/bin/cthulhu-wine-access.exe")
state = PrefixProcess()
state.process = mock.Mock()
state.process.poll.return_value = 0
state.startedAt = 9.0
manager._record_exit(state, 10.0)
self.assertIsNone(state.process)
self.assertEqual(state.failures, 1)
self.assertEqual(state.nextStart, 12.0)
@mock.patch.object(WineAccessManager, "_loader_for_process", return_value=None)
@mock.patch.object(
WineAccessManager,
"_read_environ",
return_value={"WINEPREFIX": "/tmp/exported-prefix"},
)
@mock.patch("cthulhu.wine_access_manager.Path.iterdir")
@mock.patch("cthulhu.wine_access_manager.os.getuid", return_value=1000)
def test_discovery_ignores_non_wine_processes(
self, _getuid, iterdir, _read_environ, _loader
):
entry = mock.Mock()
entry.name = "123"
entry.stat.return_value.st_uid = 1000
iterdir.return_value = [entry]
self.assertEqual(WineAccessManager("/tmp/helper").discover_prefixes(), {})
@mock.patch.object(WineAccessManager, "_loader_for_process")
@mock.patch.object(
WineAccessManager,
"_read_environ",
return_value={
"WINEPREFIX": "/tmp/prefix",
WineAccessManager.MANAGED_ENVIRONMENT_KEY: "1",
},
)
@mock.patch("cthulhu.wine_access_manager.Path.iterdir")
@mock.patch("cthulhu.wine_access_manager.os.getuid", return_value=1000)
def test_discovery_ignores_managed_helper_processes(
self, _getuid, iterdir, _read_environ, loader
):
entry = mock.Mock()
entry.name = "123"
entry.stat.return_value.st_uid = 1000
iterdir.return_value = [entry]
self.assertEqual(WineAccessManager("/tmp/helper").discover_prefixes(), {})
loader.assert_not_called()
if __name__ == "__main__":
unittest.main()