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

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/testing/mock_feedback.js

Issue 1302763002: Add tests for braille commands. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@js2gtesterr
Patch Set: Rebase Created 5 years, 4 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
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
dmazzoni 2015/08/24 19:05:07 2015?
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview This file contains the |MockFeedback| class which is a
dmazzoni 2015/08/24 19:05:06 Could you document what happens to initial speech
7 * combined mock class for speech and braille feedback. A test that uses
8 * this class may add expectations for speech utterances and braille display
9 * content to be output. The |install| method sets appropriate mock classes
10 * as the |cvox.ChromeVox.tts| and |cvox.ChromeVox.braille| objects,
11 * respectively. Output sent to those objects will then be collected in
12 * an internal queue.
13 *
14 * Expectaations can be added using the |expectSpeech| and |expectBraille|
dmazzoni 2015/08/24 19:05:06 Expectaations -> Expectations
15 * methods. These methods take either strings or regular expressions to match
16 * against. Strings must match a full utterance (or display content) exactly,
17 * while a regular expression must match a substring (use anchor operators if
18 * needed).
19 *
20 * Function calls may be inserted in the stream of expectations using the
21 * |call| method. Such callbacks are called after all preceding expectations
22 * have been met, and before any further expectations are matched. Callbacks
23 * are called in the order they were added to the mock.
24 *
25 * The |replay| method starts processing any pending utterances and braille
26 * display content and will try to match expectations as new feedback enters
27 * the queue asynchronously. When all expectations have been met and callbacks
28 * called, the finish callback, if any was provided to the constructor, is
29 * called. The mock is lean, meaning that feedback that doesn't match
30 * any expectations is silently ignored.
31 *
32 * NOTE: for asynchronous tests, the processing will never finish if there
33 * are unmet expectations. To help debugging in such situations, the mock
34 * will output its pending state if there are pending expectations and no
35 * output is received within a few seconds.
36 *
37 * See mock_feedback_test.js for example usage of this class.
38 */
39
40 /**
41 * Combined mock class for braille and speech output.
42 * @param {function=} opt_finishedCallback Called when all expectaions have
dmazzoni 2015/08/24 19:05:06 expectations
43 * been met.
44 * @constructor
45 */
46 var MockFeedback = function(opt_finishedCallback) {
47 /**
48 * @type {function}
49 * @private
50 */
51 this.finishedCallback_ = opt_finishedCallback || null;
52 /**
53 * True when |replay| has been called and actions are being replayed.
54 * @type {boolean}
55 * @private
56 */
57 this.replaying_ = false;
58 /**
59 * True when inside the |process| function to prevent nested calls.
60 * @type {boolean}
61 * @private
62 */
63 this.inProcess_ = false;
64 /**
65 * Pending expectations and callbacks.
66 * @type {Array<{perform: function(): boolean, describe: function(): string}>}
67 * @private
68 */
69 this.pendingActions_ = [];
70 /**
71 * Pending speech utterances.
72 * @type {Array<{text: string, callback: (function|undefined)}>}
73 * @private
74 */
75 this.pendingUtterances_ = [];
76 /**
77 * Pending braille output.
78 * @type {Array<{text: string, callback: (function|undefined)}>}
79 * @private
80 */
81 this.pendingBraille_ = [];
82 /**
83 * Handle for the timeout set for debug logging.
84 * @type {number}
85 * @private
86 */
87 this.logTimeoutId_ = 0;
88 /**
89 * @type {cvox.NavBraille}
90 * @private
91 */
92 this.lastMatchedBraille_ = null;
93 };
94
95 MockFeedback.prototype = {
96
97 /**
98 * Install mock objects as |cvox.CrhomeVox.tts| and |cvox.ChromeVox.braille|
dmazzoni 2015/08/24 19:05:07 ChromeVox
99 * to collect feedback.
100 */
101 install: function() {
102 assertFalse(this.replaying_);
103 function MockTts() {}
dmazzoni 2015/08/24 19:05:07 I know this is legal syntax but it looks funny, li
104 MockTts.prototype = {
105 __proto__: cvox.TtsInterface.prototype,
106 speak: this.addUtterance_.bind(this)
107 };
108 cvox.ChromeVox.tts = new MockTts();
109 function MockBraille() {
110 }
111 MockBraille.prototype = {
112 __proto__: cvox.BrailleInterface.prototype,
113 write: this.addBraille_.bind(this)
114 };
115 cvox.ChromeVox.braille = new MockBraille();
116 },
117
118 /**
119 * Adds an expectation for a spoken utterance.
dmazzoni 2015/08/24 19:05:06 Or utterances?
120 * @param {...string} var_args
dmazzoni 2015/08/24 19:05:06 Document this, i.e. clarify that each argument is
121 * @return {MockFeedback} |this| for chaining
122 */
123 expectSpeech: function() {
124 assertFalse(this.replaying_);
125 Array.prototype.forEach.call(arguments, function(text) {
126 this.pendingActions_.push({
127 perform: function() {
128 return !!MockFeedback.matchAndConsume_(
129 text, {}, this.pendingUtterances_);
130 }.bind(this),
131 describe: function() { return 'Speak \'' + text + '\''; }
dmazzoni 2015/08/24 19:05:06 Could this be toString()?
132 });
133 }.bind(this));
134 return this;
135 },
136
137 /**
138 * Adds an expectation for braille output.
139 * @param {string|RegExp} text
140 * @param {Object=} opt_props Additional properties to match in the
141 * |NavBraille|
142 * @return {MockFeedback} |this| for chaining
143 */
144 expectBraille: function(text, opt_props) {
145 assertFalse(this.replaying_);
146 var props = opt_props || {};
147 this.pendingActions_.push({
148 perform: function() {
149 var match = MockFeedback.matchAndConsume_(
150 text, props, this.pendingBraille_);
151 if (match)
152 this.lastMatchedBraille_ = match;
153 return !!match;
154 }.bind(this),
155 describe: function() {
156 return 'Braille \'' + text + '\' ' + JSON.stringify(props);
157 }
158 });
159 return this;
160 },
161
162 /**
163 * Arranges for a callback to be invoked when all expectations that were
164 * added before this call have been met. Callbacks are called in the
165 * order they are added.
166 * @param {Function} callback
167 * @return {MockFeedback} |this| for chaining
168 */
169 call: function(callback) {
170 assertFalse(this.replaying_);
171 this.pendingActions_.push({
172 perform: function() {
173 callback();
174 return true;
175 },
176 describe: function() {
177 return 'Callback';
178 }
179 });
180 return this;
181 },
182
183 /**
184 * Processes any feedback that has been received so far and treis to
185 * satisfy the registered expectations. Any feedback that is received
186 * after this call (via the installed mock objects) is processed immediately.
187 * When all expectations are satisfied and registered callbacks called,
188 * the finish callbcak, if any, is called.
189 * This function may only be called once.
190 */
191 replay: function() {
dmazzoni 2015/08/24 19:05:06 Do you think it would be possible to raise an exce
192 assertFalse(this.replaying_);
193 this.replaying_ = true;
194 this.process_();
195 },
196
197 /**
198 * Returns the |NavBraille| that matched an expectation. This is
199 * intended to be used by a callback to invoke braille commands that
200 * depend on display contents.
201 * @type {cvox.NavBraille}
202 */
203 get lastMatchedBraille() {
204 assertTrue(this.replaying_);
205 return this.lastMatchedBraille_;
206 },
207
208 /**
209 * @param {string} textSTring
dmazzoni 2015/08/24 19:05:07 textString
210 * @param {cvox.QueueMode} queueMode
211 * @param {Object=} properties
212 * @private
213 */
214 addUtterance_: function(textString, queueMode, properties) {
215 this.pendingUtterances_.push(
216 {text: textString,
217 callback: properties ? properties.endCallback : undefined});
dmazzoni 2015/08/24 19:05:07 Do you need to handle startCallback too? If we wa
218 this.process_();
219 },
220
221 /** @private */
222 addBraille_: function(navBraille) {
223 this.pendingBraille_.push(navBraille);
224 this.process_();
225 },
226
227 /*** @private */
228 process_: function() {
229 if (!this.replaying_ || this.inProcess_)
230 return;
231 try {
232 this.inProcess_ = true;
233 while (this.pendingActions_.length > 0) {
234 var action = this.pendingActions_[0];
235 if (action.perform()) {
236 this.pendingActions_.shift();
237 if (this.logTimeoutId_) {
238 window.clearTimeout(this.logTimeoutId_);
239 this.logTimeoutId_ = 0;
240 }
241 } else {
242 break;
243 }
244 }
245 if (this.pendingActions_.length == 0) {
246 if (this.finishedCallback_) {
247 this.finishedCallback_();
248 this.finishedCallback_ = null;
249 }
250 } else {
251 // If there are pending actions and no matching feedback for a few
252 // seconds, log the pending state to ease debugging.
253 if (!this.logTimeoutId_) {
254 this.logTimeoutId_ = window.setTimeout(
255 this.logPendingState_.bind(this), 2000);
256 }
257 }
258 } finally {
259 this.inProcess_ = false;
260 }
261 },
262
263 /** @private */
264 logPendingState_: function() {
265 if (this.pendingActions_.length > 0)
266 console.log('Still waiting for ' + this.pendingActions_[0].describe());
267 function logPending(desc, list) {
268 if (list.length > 0)
269 console.log('Pending ' + desc + ':\n ' +
270 list.map(function(i) {
271 var ret = '\'' + i.text + '\'';
272 if ('startIndex' in i)
273 ret += ' startIndex=' + i.startIndex;
274 if ('endIndex' in i)
275 ret += ' endIndex=' + i.endIndex;
276 return ret;
277 }).join('\n ') + '\n ');
278 }
279 logPending('speech utterances', this.pendingUtterances_);
280 logPending('braille', this.pendingBraille_);
281 this.logTimeoutId_ = 0;
282 },
283 };
284
285 /**
286 * @param {string} text
287 * @param {Object} props
288 * @param {Array<{text: (string|RegExp), callback: (function|undefined)}>}
289 * pending
290 * @return {Object}
291 * @private
292 */
293 MockFeedback.matchAndConsume_ = function(text, props, pending) {
294 for (var i = 0, candidate; candidate = pending[i]; ++i) {
dmazzoni 2015/08/24 19:05:07 This seems to allow additional utterances - for ex
295 var candidateText = candidate.text.toString();
296 if (text === candidateText ||
297 (text instanceof RegExp && text.test(candidateText))) {
298 var matched = true;
299 for (prop in props) {
300 if (candidate[prop] !== props[prop]) {
301 matched = false;
302 break;
303 }
304 }
305 if (matched)
306 break;
307 }
308 }
309 if (candidate) {
310 var consumed = pending.splice(0, i + 1);
311 consumed.forEach(function(item) {
312 if (item.callback)
313 item.callback();
314 });
315 }
316 return candidate;
317 };
318
319 /**
320 * Returns its first argumennt. Moved out of the cosntructor to satisfy the
dmazzoni 2015/08/24 19:05:06 constructor
321 * js linter.
322 * @param {*} a
323 * @return {*}
324 * @private
325 */
326 MockFeedback.identityFunction_ = function(a) {
327 return a;
328 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698