forked from mirrors/gecko-dev
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:
parent
c5503d7f3e
commit
a8d36840cb
10 changed files with 152 additions and 6 deletions
|
|
@ -32,6 +32,7 @@ DIRS += [
|
||||||
TEST_DIRS += ["tests/mochitest"]
|
TEST_DIRS += ["tests/mochitest"]
|
||||||
|
|
||||||
BROWSER_CHROME_MANIFESTS += [
|
BROWSER_CHROME_MANIFESTS += [
|
||||||
|
"tests/browser/atk/browser.toml",
|
||||||
"tests/browser/bounds/browser.toml",
|
"tests/browser/bounds/browser.toml",
|
||||||
"tests/browser/browser.toml",
|
"tests/browser/browser.toml",
|
||||||
"tests/browser/e10s/browser.toml",
|
"tests/browser/e10s/browser.toml",
|
||||||
|
|
|
||||||
64
accessible/tests/browser/atk/a11y_setup.py
Normal file
64
accessible/tests/browser/atk/a11y_setup.py
Normal 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
|
||||||
15
accessible/tests/browser/atk/browser.toml
Normal file
15
accessible/tests/browser/atk/browser.toml
Normal 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"]
|
||||||
33
accessible/tests/browser/atk/browser_role.js
Normal file
33
accessible/tests/browser/atk/browser_role.js
Normal 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 }
|
||||||
|
);
|
||||||
18
accessible/tests/browser/atk/head.js
Normal file
18
accessible/tests/browser/atk/head.js
Normal 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 }
|
||||||
|
);
|
||||||
|
|
@ -4,6 +4,7 @@ subsuite = "a11y"
|
||||||
support-files = [
|
support-files = [
|
||||||
"!/accessible/tests/mochitest/*.js",
|
"!/accessible/tests/mochitest/*.js",
|
||||||
"*.sys.mjs",
|
"*.sys.mjs",
|
||||||
|
"atk/a11y_setup.py",
|
||||||
"head.js",
|
"head.js",
|
||||||
"python_runner_wsh.py",
|
"python_runner_wsh.py",
|
||||||
"shared-head.js",
|
"shared-head.js",
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,7 @@ def web_socket_transfer_data(request):
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
testDir = "windows"
|
testDir = "windows"
|
||||||
elif sys.platform == "linux":
|
elif sys.platform == "linux":
|
||||||
# XXX ATK code goes here.
|
testDir = "atk"
|
||||||
pass
|
|
||||||
if testDir:
|
if testDir:
|
||||||
sys.path.append(
|
sys.path.append(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ apt_packages+=('pulseaudio-module-gconf')
|
||||||
apt_packages+=('python-dev')
|
apt_packages+=('python-dev')
|
||||||
apt_packages+=('python-pip')
|
apt_packages+=('python-pip')
|
||||||
apt_packages+=('python3-pip')
|
apt_packages+=('python3-pip')
|
||||||
|
apt_packages+=('python3-pyatspi')
|
||||||
apt_packages+=('qemu-kvm')
|
apt_packages+=('qemu-kvm')
|
||||||
apt_packages+=('rlwrap')
|
apt_packages+=('rlwrap')
|
||||||
apt_packages+=('screen')
|
apt_packages+=('screen')
|
||||||
|
|
@ -158,4 +159,14 @@ rm -rf /usr/share/locale/ /usr/share/locale-langpack/ /usr/share/locales/
|
||||||
# Further cleanup
|
# Further cleanup
|
||||||
apt-get autoremove
|
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"
|
rm -f "$0"
|
||||||
|
|
|
||||||
|
|
@ -1217,7 +1217,7 @@ Tester.prototype = {
|
||||||
|
|
||||||
this.SimpleTest.reset();
|
this.SimpleTest.reset();
|
||||||
// Reset accessibility environment.
|
// Reset accessibility environment.
|
||||||
this.AccessibilityUtils.reset(this.a11y_checks);
|
this.AccessibilityUtils.reset(this.a11y_checks, this.currentTest.path);
|
||||||
|
|
||||||
// Load the tests into a testscope
|
// Load the tests into a testscope
|
||||||
let currentScope = (this.currentTest.scope = new testScope(
|
let currentScope = (this.currentTest.scope = new testScope(
|
||||||
|
|
|
||||||
|
|
@ -697,13 +697,17 @@ this.AccessibilityUtils = (function () {
|
||||||
gEnv = { ...DEFAULT_ENV };
|
gEnv = { ...DEFAULT_ENV };
|
||||||
},
|
},
|
||||||
|
|
||||||
reset(a11yChecks = false) {
|
reset(a11yChecks = false, testPath = "") {
|
||||||
gA11YChecks = a11yChecks;
|
gA11YChecks = a11yChecks;
|
||||||
|
|
||||||
const { Services } = SpecialPowers;
|
const { Services } = SpecialPowers;
|
||||||
// Disable accessibility service if it is running and if a11y checks are
|
// Disable accessibility service if it is running and if a11y checks are
|
||||||
// disabled.
|
// disabled. However, don't do this for accessibility engine tests.
|
||||||
if (!gA11YChecks && Services.appinfo.accessibilityEnabled) {
|
if (
|
||||||
|
!gA11YChecks &&
|
||||||
|
Services.appinfo.accessibilityEnabled &&
|
||||||
|
!testPath.startsWith("chrome://mochitests/content/browser/accessible/")
|
||||||
|
) {
|
||||||
Services.prefs.setIntPref(FORCE_DISABLE_ACCESSIBILITY_PREF, 1);
|
Services.prefs.setIntPref(FORCE_DISABLE_ACCESSIBILITY_PREF, 1);
|
||||||
Services.prefs.clearUserPref(FORCE_DISABLE_ACCESSIBILITY_PREF);
|
Services.prefs.clearUserPref(FORCE_DISABLE_ACCESSIBILITY_PREF);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue