Bug 1926214 part 1: Call CoInitialize for new threads in a11y Python test runner. r=nlapre

Each browser test file is run with a separate instance of shared-head.js and thus a distinct WebSocket connection to the a11y Python runner.
pywebsocket3 is multi-threaded and may thus choose different threads to handle different WebSocket requests.
Python comtypes implicitly initialises COM when it is imported, but COM initialisation is thread specific and Python modules are cached, so this only applies to the thread on which comtypes was imported.
These factors were causing problems such as event timeouts when running multiple IA2 test files because IA2 relies on COM being initialised.
To fix this, add a setup() function to a11y_setup.py which is called when a request is handled.
For Windows, this calls CoInitialize once in each thread in which it is run.

Differential Revision: https://phabricator.services.mozilla.com/D227143
This commit is contained in:
James Teh 2024-10-30 04:46:41 +00:00
parent 8e37a980d5
commit 4cdf0b15c1
3 changed files with 19 additions and 0 deletions

View File

@ -28,6 +28,11 @@ sys.path.pop()
del pyatspiFile
def setup():
# We do all the setup we need at module level.
pass
def getDoc():
"""Get the Accessible for the document being tested."""
# We can compare the parent process ids to find the Firefox started by the

View File

@ -41,6 +41,7 @@ def web_socket_transfer_data(request):
try:
import a11y_setup
a11y_setup.setup()
cleanNamespace = a11y_setup.__dict__
setupExc = None
except Exception:

View File

@ -7,6 +7,7 @@
import ctypes
import os
import threading
from ctypes import POINTER, byref
from ctypes.wintypes import BOOL, HWND, LPARAM, POINT # noqa: F401
from dataclasses import dataclass
@ -62,6 +63,18 @@ uiaClient = comtypes.CoCreateInstance(
clsctx=comtypes.CLSCTX_INPROC_SERVER,
)
_threadLocal = threading.local()
def setup():
if getattr(_threadLocal, "isSetup", False):
return
# We can do most setup at module level. However, because modules are cached
# and pywebsocket3 can serve requests on any thread, we need to do setup for
# each new thread here.
comtypes.CoInitialize()
_threadLocal.isSetup = True
def AccessibleObjectFromWindow(hwnd, objectID=OBJID_CLIENT):
p = POINTER(IAccessible)()