Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(422)

Side by Side Diff: tools/accessibility/nvda/nvda_chrome_tests.py

Issue 331363004: Add a script to test Chrome with the NVDA screen reader. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix copyright Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/accessibility/nvda/README.txt ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Semi-automated tests of Chrome with NVDA.
7
8 This file performs (semi) automated tests of Chrome with NVDA
9 (NonVisual Desktop Access), a popular open-source screen reader for
10 visually impaired users on Windows. It works by launching Chrome in a
11 subprocess, then launching NVDA in a special environment that simulates
12 speech rather than actually speaking, and ignores all events coming from
13 processes other than a specific Chrome process ID. Each test automates
14 Chrome with a series of actions and asserts that NVDA gives the expected
15 feedback in response.
16
17 The tests are "semi" automated in the sense that they are not intended to be
18 run from any developer machine, or on a buildbot - it requires setting up the
19 environment according to the instructions in README.txt, then running the
20 test script, then filing bugs for any potential failures. If the environment
21 is set up correctly, the actual tests should run automatically and unattended.
22 """
23
24 import os
25 import pywinauto
26 import re
27 import shutil
28 import signal
29 import subprocess
30 import sys
31 import tempfile
32 import time
33 import unittest
34
35 CHROME_PROFILES_PATH = os.path.join(os.getcwd(), 'chrome_profiles')
36 CHROME_PATH = os.path.join(os.environ['USERPROFILE'],
37 'AppData',
38 'Local',
39 'Google',
40 'Chrome SxS',
41 'Application',
42 'chrome.exe')
43 NVDA_PATH = os.path.join(os.getcwd(),
44 'nvdaPortable',
45 'nvda_noUIAccess.exe')
46 NVDA_PROCTEST_PATH = os.path.join(os.getcwd(),
47 'nvda-proctest')
48 NVDA_LOGPATH = os.path.join(os.getcwd(),
49 'nvda_log.txt')
50 WAIT_FOR_SPEECH_TIMEOUT_SECS = 3.0
51
52 class NvdaChromeTest(unittest.TestCase):
53 @classmethod
54 def setUpClass(cls):
55 print 'user data: %s' % CHROME_PROFILES_PATH
56 print 'chrome: %s' % CHROME_PATH
57 print 'nvda: %s' % NVDA_PATH
58 print 'nvda_proctest: %s' % NVDA_PROCTEST_PATH
59
60 print
61 print 'Clearing user data directory and log file from previous runs'
62 if os.access(NVDA_LOGPATH, os.F_OK):
63 os.remove(NVDA_LOGPATH)
64 if os.access(CHROME_PROFILES_PATH, os.F_OK):
65 shutil.rmtree(CHROME_PROFILES_PATH)
66 os.mkdir(CHROME_PROFILES_PATH, 0777)
67
68 def handler(signum, frame):
69 print 'Test interrupted, attempting to kill subprocesses.'
70 self.tearDown()
71 sys.exit()
72 signal.signal(signal.SIGINT, handler)
73
74 def setUp(self):
75 user_data_dir = tempfile.mkdtemp(dir = CHROME_PROFILES_PATH)
76 args = [CHROME_PATH,
77 '--user-data-dir=%s' % user_data_dir,
78 '--no-first-run',
79 'about:blank']
80 print
81 print ' '.join(args)
82 self._chrome_proc = subprocess.Popen(args)
83 self._chrome_proc.poll()
84 if self._chrome_proc.returncode is None:
85 print 'Chrome is running'
86 else:
87 print 'Chrome exited with code', self._chrome_proc.returncode
88 sys.exit()
89 print 'Chrome pid: %d' % self._chrome_proc.pid
90
91 os.environ['NVDA_SPECIFIC_PROCESS'] = str(self._chrome_proc.pid)
92
93 args = [NVDA_PATH,
94 '-m',
95 '-c',
96 NVDA_PROCTEST_PATH,
97 '-f',
98 NVDA_LOGPATH]
99 self._nvda_proc = subprocess.Popen(args)
100 self._nvda_proc.poll()
101 if self._nvda_proc.returncode is None:
102 print 'NVDA is running'
103 else:
104 print 'NVDA exited with code', self._nvda_proc.returncode
105 sys.exit()
106 print 'NVDA pid: %d' % self._nvda_proc.pid
107
108 app = pywinauto.application.Application()
109 app.connect_(process = self._chrome_proc.pid)
110 self._pywinauto_window = app.top_window_()
111
112 try:
113 self._WaitForSpeech(['Address and search bar edit', 'about:blank'])
114 except:
115 self.tearDown()
116
117 def tearDown(self):
118 print
119 print 'Shutting down'
120
121 self._chrome_proc.poll()
122 if self._chrome_proc.returncode is None:
123 print 'Killing Chrome subprocess'
124 self._chrome_proc.kill()
125 else:
126 print 'Chrome already died.'
127
128 self._nvda_proc.poll()
129 if self._nvda_proc.returncode is None:
130 print 'Killing NVDA subprocess'
131 self._nvda_proc.kill()
132 else:
133 print 'NVDA already died.'
134
135 def _GetSpeechFromNvdaLogFile(self):
136 """Return everything NVDA would have spoken as a list of strings.
137
138 Parses lines like this from NVDA's log file:
139 Speaking [LangChangeCommand ('en'), u'Google Chrome', u'window']
140 Speaking character u'slash'
141
142 Returns a single list of strings like this:
143 [u'Google Chrome', u'window', u'slash']
144 """
145 if not os.access(NVDA_LOGPATH, os.F_OK):
146 return []
147 lines = open(NVDA_LOGPATH).readlines()
148 regex = re.compile(r"u'((?:[^\'\\]|\\.)*)\'")
149 result = []
150 for line in lines:
151 for m in regex.finditer(line):
152 speech_with_whitespace = m.group(1)
153 speech_stripped = re.sub(r'\s+', ' ', speech_with_whitespace).strip()
154 result.append(speech_stripped)
155 return result
156
157 def _WaitForSpeech(self, expected):
158 """Block until the last speech in NVDA's log file is the given string(s).
159
160 Repeatedly parses the log file until the last speech line(s) in the
161 log file match the given strings, or it times out.
162
163 Args:
164 expected: string or a list of string - only succeeds if these are the last
165 strings spoken, in order.
166
167 Returns when those strings are spoken, or throws an error if it times out
168 waiting for those strings.
169 """
170 if type(expected) is type(''):
171 expected = [expected]
172 start_time = time.time()
173 while True:
174 lines = self._GetSpeechFromNvdaLogFile()
175 if (lines[-len(expected):] == expected):
176 break
177
178 if time.time() - start_time >= WAIT_FOR_SPEECH_TIMEOUT_SECS:
179 print '** Speech from NVDA so far:'
180 for line in lines:
181 print '"%s"' % line
182 print '** Was waiting for:'
183 for line in expected:
184 print '"%s"' % line
185 raise Exception('Timed out')
186 time.sleep(0.1)
187
188 #
189 # Tests
190 #
191
192 def testTypingInOmnibox(self):
193 # Ctrl+A: Select all.
194 self._pywinauto_window.TypeKeys('^A')
195 self._WaitForSpeech('selecting about:blank')
196
197 # Type three characters.
198 self._pywinauto_window.TypeKeys('xyz')
199 self._WaitForSpeech(['x', 'y', 'z'])
200
201 # Arrow back over two characters.
202 self._pywinauto_window.TypeKeys('{LEFT}')
203 self._WaitForSpeech(['z', 'z', 'unselecting'])
204
205 self._pywinauto_window.TypeKeys('{LEFT}')
206 self._WaitForSpeech('y')
207
208 def testFocusToolbarButton(self):
209 # Alt+Shift+T.
210 self._pywinauto_window.TypeKeys('%+T')
211 self._WaitForSpeech('Reload button Reload this page')
212
213 def testReadAllOnPageLoad(self):
214 # Ctrl+A: Select all
215 self._pywinauto_window.TypeKeys('^A')
216 self._WaitForSpeech('selecting about:blank')
217
218 # Load data url.
219 self._pywinauto_window.TypeKeys('data:text/html,Hello<p>World.')
220 self._WaitForSpeech('dot')
221 self._pywinauto_window.TypeKeys('{ENTER}')
222 self._WaitForSpeech(
223 ['document',
224 'Hello',
225 'World.'])
226
227 if __name__ == '__main__':
228 unittest.main()
229
OLDNEW
« no previous file with comments | « tools/accessibility/nvda/README.txt ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698