Finish up edit field accessibility.
This commit is contained in:
1 parent
6f6d4040c4
commit
c5657ee5d4
9 files changed
+361
-39
No files matched your search
@@ -1,4 +1,4 @@
|
||||
"""Exercise a real Wine menu hook against a private D-Bus recording service.
|
||||
"""Exercise Wine menus/editors against a private D-Bus recording service.
|
||||
|
||||
Requires a graphical session, PyGObject, Wine, and MinGW. No speech is sent to
|
||||
the running reader. The fixture window closes automatically.
|
||||
@@ -16,13 +16,19 @@ from gi.repository import Gio, GLib
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--build-dir", default="build")
|
||||
parser.add_argument("--session", action="store_true")
|
||||
parser.add_argument("--fixture", choices=("menu", "edit"), default="menu")
|
||||
parser.add_argument("--fixture", choices=("menu", "edit", "notepad"), default="menu")
|
||||
parser.add_argument("--late-helper", action="store_true")
|
||||
parser.add_argument("--restart-helper", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if (args.late_helper or args.restart_helper) and args.fixture != "edit":
|
||||
parser.error("helper lifecycle checks require --fixture edit")
|
||||
build = Path(args.build_dir).resolve()
|
||||
if not args.session:
|
||||
sys.exit(subprocess.call(["dbus-run-session", "--", sys.executable,
|
||||
__file__, "--session", "--build-dir", str(build),
|
||||
"--fixture", args.fixture]))
|
||||
"--fixture", args.fixture,
|
||||
*(["--late-helper"] if args.late_helper else []),
|
||||
*(["--restart-helper"] if args.restart_helper else [])]))
|
||||
|
||||
interface = "org.stormux.Cthulhu1.Service"
|
||||
object_path = "/org/stormux/Cthulhu1/Service"
|
||||
@@ -47,6 +53,8 @@ with tempfile.TemporaryDirectory(prefix="wine-access-menus-") as temporary:
|
||||
env = os.environ.copy()
|
||||
env.update(WINEPREFIX=str(Path(temporary) / "prefix"), WINEDEBUG="-all",
|
||||
WINEDLLOVERRIDES="mscoree,mshtml=", CTHULHU_WINE_ACCESS_MANAGED="1")
|
||||
ready = Path(temporary) / "ready"
|
||||
env["WINE_ACCESS_FIXTURE_READY"] = "Z:" + str(ready).replace("/", "\\")
|
||||
fixture = Path(temporary) / "menu-fixture.exe"
|
||||
subprocess.run(["x86_64-w64-mingw32-gcc", "-Wall", "-Wextra", "-mwindows",
|
||||
"-o", str(fixture), str(Path(__file__).with_name(args.fixture + "_fixture.c")),
|
||||
@@ -55,6 +63,12 @@ with tempfile.TemporaryDirectory(prefix="wine-access-menus-") as temporary:
|
||||
app = None
|
||||
try:
|
||||
subprocess.run(["wineboot", "-u"], env=env, check=True, timeout=45)
|
||||
if args.late_helper:
|
||||
app = subprocess.Popen(["wine", str(fixture)], env=env)
|
||||
deadline = time.monotonic() + 5
|
||||
while not ready.exists() and app.poll() is None and time.monotonic() < deadline:
|
||||
time.sleep(0.01)
|
||||
assert ready.exists(), "editor did not become ready before helper attachment"
|
||||
helper = subprocess.Popen([str(build / "cthulhu-wine-access.exe"), "--no-controller"], env=env)
|
||||
# Allow the helper to register its bridge before opening the fixture.
|
||||
deadline = time.monotonic() + 1
|
||||
@@ -63,17 +77,43 @@ with tempfile.TemporaryDirectory(prefix="wine-access-menus-") as temporary:
|
||||
pass
|
||||
time.sleep(0.01)
|
||||
assert helper.poll() is None, "helper exited before fixture startup"
|
||||
app = subprocess.Popen(["wine", str(fixture)], env=env)
|
||||
deadline = time.monotonic() + 15
|
||||
if app is None:
|
||||
app = subprocess.Popen(["wine", str(fixture)], env=env)
|
||||
restarted = False
|
||||
deadline = time.monotonic() + 25
|
||||
while app.poll() is None and time.monotonic() < deadline:
|
||||
while GLib.MainContext.default().iteration(False):
|
||||
pass
|
||||
if args.restart_helper and not restarted and any(e[0].endswith(":text") for e in events):
|
||||
helper.terminate()
|
||||
helper.wait(timeout=5)
|
||||
helper = subprocess.Popen([str(build / "cthulhu-wine-access.exe"), "--no-controller"], env=env)
|
||||
restarted = True
|
||||
time.sleep(0.01)
|
||||
assert app.poll() == 0, "fixture failed or timed out"
|
||||
assert helper.poll() is None, "helper exited during the fixture"
|
||||
if args.restart_helper:
|
||||
assert restarted, "fixture never triggered a helper restart"
|
||||
for _ in range(20):
|
||||
while GLib.MainContext.default().iteration(False):
|
||||
pass
|
||||
time.sleep(0.01)
|
||||
focused = None
|
||||
for event in events:
|
||||
if event[1] == "focus":
|
||||
focused = event[0]
|
||||
elif event[1] == "text":
|
||||
assert event[0] == focused, f"text sent without establishing focus: {event!r}"
|
||||
if args.fixture == "notepad":
|
||||
text_events = [e for e in events if e[0].endswith(":text")]
|
||||
spoken = [e[2] for e in text_events]
|
||||
for expected in ("e", "l", "beta", "Notepad second line", "N selected",
|
||||
"N unselected", "N deleted"):
|
||||
assert expected in spoken, f"missing Notepad feedback: {expected!r}; {text_events!r}"
|
||||
assert spoken.count("l") == 2, "Notepad lost repeated characters"
|
||||
assert any(e[3] == "edit" and e[4] == "otepad second line" for e in text_events), "Notepad menu return missing"
|
||||
print("PASS: actual Wine Notepad keyboard characters, words, lines, selection, deletion, and menu return")
|
||||
sys.exit(0)
|
||||
if args.fixture == "edit":
|
||||
text_events = [e for e in events if e[0].endswith(":text")]
|
||||
for event in text_events:
|
||||
@@ -88,6 +128,12 @@ with tempfile.TemporaryDirectory(prefix="wine-access-menus-") as temporary:
|
||||
assert any(e[6] == "password" for e in text_events), "password role missing"
|
||||
assert not any("private-password-fixture" in str(e) for e in events), "password leaked"
|
||||
assert "😀 deleted" not in spoken, "read-only control reported deletion"
|
||||
assert "hello deleted" not in spoken, "replacement misreported as deletion"
|
||||
assert sum(e[1] == "text" and e[2] == "l" for e in text_events) == 3, "rapid repeated letters lost"
|
||||
assert any(e[3] == "edit" and e[4] == "menu return line" for e in text_events), "menu return lost caret line"
|
||||
assert spoken.count("new line deleted") == 2, "CRLF deletion feedback missing"
|
||||
assert any(e[4] == "x" * 1022 for e in text_events), "truncated surrogate discarded line"
|
||||
assert "d" in spoken and "undo restored" in spoken, "Undo left stale text snapshot"
|
||||
print("PASS: editor focus, characters, words, lines, selection, deletion, Unicode, passwords, and read-only controls")
|
||||
sys.exit(0)
|
||||
menus = [event for event in events if ":menu:" in event[0]]
|
||||
|
||||
Reference in new issue
Block a user