Bug 1862534: Groundwork for ATK accessibility API tests. r=eeejay,jmaher

This adds setup code and utility functions for ATK to the Python environment.
I also needed to prevent front-end accessibility checks (AccessibilityUtils) from force disabling accessibility for a11y engine tests.
Although the tests re-enabled it anyway, this seems to break AT-SPI's interaction with our ApplicationAccessible.
Disabling it really doesn't make sense in this case anyway.
This patch includes a simple role test to show all of this working.

Differential Revision: https://phabricator.services.mozilla.com/D192911
This commit is contained in:
James Teh 2023-11-21 02:11:49 +00:00
parent c5503d7f3e
commit a8d36840cb
10 changed files with 152 additions and 6 deletions

View file

@ -32,6 +32,7 @@ DIRS += [
TEST_DIRS += ["tests/mochitest"]
BROWSER_CHROME_MANIFESTS += [
"tests/browser/atk/browser.toml",
"tests/browser/bounds/browser.toml",
"tests/browser/browser.toml",
"tests/browser/e10s/browser.toml",

View file

@ -0,0 +1,64 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""Python environment for ATK a11y browser tests.
"""
import os
import subprocess
import sys
import psutil
# pyatspi can't be installed using pip. Rely on the system installation.
# Get the path to the system installation of pyatspi.
pyatspiFile = subprocess.check_output(
(
os.path.join(sys.base_prefix, "bin", "python3"),
"-c",
"import pyatspi; print(pyatspi.__file__)",
),
encoding="utf-8",
).rstrip()
sys.path.append(os.path.dirname(os.path.dirname(pyatspiFile)))
import pyatspi
sys.path.pop()
del pyatspiFile
def getDoc():
"""Get the Accessible for the document being tested."""
# We can compare the parent process ids to find the Firefox started by the
# test harness.
commonPid = psutil.Process().ppid()
for app in pyatspi.Registry.getDesktop(0):
if (
app.name == "Firefox"
and psutil.Process(app.get_process_id()).ppid() == commonPid
):
break
else:
raise LookupError("Couldn't find Firefox application Accessible")
root = app[0]
for embeds in root.getRelationSet():
if embeds.getRelationType() == pyatspi.RELATION_EMBEDS:
break
else:
raise LookupError("Firefox root doesn't have RELATION_EMBEDS")
doc = embeds.getTarget(0)
child = doc[0]
if child.get_attributes().get("id") == "default-iframe-id":
# This is an iframe or remoteIframe test.
doc = child[0]
return doc
def findByDomId(root, id):
for child in root:
if child.get_attributes().get("id") == id:
return child
descendant = findByDomId(child, id)
if descendant:
return descendant

View file

@ -0,0 +1,15 @@
[DEFAULT]
subsuite = "a11y"
skip-if = [
"os != 'linux'",
"headless",
]
support-files = ["head.js"]
prefs = [
# Enabling the a11y service from XPCOM doesn't seem to be enough to get ATK
# working correctly. Force enable it before the test starts.
"accessibility.force_disabled=-1",
"javascript.options.asyncstack_capture_debuggee_only=false",
]
["browser_role.js"]

View file

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const ATSPI_ROLE_DOCUMENT_WEB = 95;
const ATSPI_ROLE_PARAGRAPH = 73;
addAccessibleTask(
`
<p id="p">p</p>
`,
async function (browser, docAcc) {
let role = await runPython(`
global doc
doc = getDoc()
return doc.getRole()
`);
is(role, ATSPI_ROLE_DOCUMENT_WEB, "doc has correct ATSPI role");
ok(
await runPython(`
global p
p = findByDomId(doc, "p")
return p == doc[0]
`),
"doc's first child is p"
);
role = await runPython(`p.getRole()`);
is(role, ATSPI_ROLE_PARAGRAPH, "p has correct ATSPI role");
},
{ chrome: true, topLevel: true, iframe: true, remoteIframe: true }
);

View file

@ -0,0 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Load the shared-head file first.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
this
);
// Loading and common.js from accessible/tests/mochitest/ for all tests, as
// well as promisified-events.js.
loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
);

View file

@ -4,6 +4,7 @@ subsuite = "a11y"
support-files = [
"!/accessible/tests/mochitest/*.js",
"*.sys.mjs",
"atk/a11y_setup.py",
"head.js",
"python_runner_wsh.py",
"shared-head.js",

View file

@ -30,8 +30,7 @@ def web_socket_transfer_data(request):
if sys.platform == "win32":
testDir = "windows"
elif sys.platform == "linux":
# XXX ATK code goes here.
pass
testDir = "atk"
if testDir:
sys.path.append(
os.path.join(

View file

@ -74,6 +74,7 @@ apt_packages+=('pulseaudio-module-gconf')
apt_packages+=('python-dev')
apt_packages+=('python-pip')
apt_packages+=('python3-pip')
apt_packages+=('python3-pyatspi')
apt_packages+=('qemu-kvm')
apt_packages+=('rlwrap')
apt_packages+=('screen')
@ -158,4 +159,14 @@ rm -rf /usr/share/locale/ /usr/share/locale-langpack/ /usr/share/locales/
# Further cleanup
apt-get autoremove
# We've changed python3 to use 3.7, but binary modules are only installed for
# the distribution's default which is 3.6. Symlink a module we need for 3.7.
ln -s /usr/lib/python3/dist-packages/gi/_gi.cpython-{36m,37m}-x86_64-linux-gnu.so
# The packaged version of pyatspi is not compatible with Python 3.7. Hack it to
# be compatible, since there's no package for 3.7 in this distribution.
# Specifically, rename variables named "async" to "asynchronous", since "async"
# is a reserved keyword in Python 3.7.
sed -i 's/\basync\b/asynchronous/' /usr/lib/python3/dist-packages/pyatspi/registry.py
rm -f "$0"

View file

@ -1217,7 +1217,7 @@ Tester.prototype = {
this.SimpleTest.reset();
// Reset accessibility environment.
this.AccessibilityUtils.reset(this.a11y_checks);
this.AccessibilityUtils.reset(this.a11y_checks, this.currentTest.path);
// Load the tests into a testscope
let currentScope = (this.currentTest.scope = new testScope(

View file

@ -697,13 +697,17 @@ this.AccessibilityUtils = (function () {
gEnv = { ...DEFAULT_ENV };
},
reset(a11yChecks = false) {
reset(a11yChecks = false, testPath = "") {
gA11YChecks = a11yChecks;
const { Services } = SpecialPowers;
// Disable accessibility service if it is running and if a11y checks are
// disabled.
if (!gA11YChecks && Services.appinfo.accessibilityEnabled) {
// disabled. However, don't do this for accessibility engine tests.
if (
!gA11YChecks &&
Services.appinfo.accessibilityEnabled &&
!testPath.startsWith("chrome://mochitests/content/browser/accessible/")
) {
Services.prefs.setIntPref(FORCE_DISABLE_ACCESSIBILITY_PREF, 1);
Services.prefs.clearUserPref(FORCE_DISABLE_ACCESSIBILITY_PREF);
}