OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview This file contains the |MockFeedback| class which is a | 6 * @fileoverview This file contains the |MockFeedback| class which is |
Peter Lundblad
2015/09/24 14:56:27
Please add unit tests for the additions to the moc
dmazzoni
2015/09/28 16:33:05
Done.
| |
7 * combined mock class for speech and braille feedback. A test that uses | 7 * a combined mock class for speech, braille, and earcon feedback. A |
8 * this class may add expectations for speech utterances and braille display | 8 * test that uses this class may add expectations for speech |
9 * content to be output. The |install| method sets appropriate mock classes | 9 * utterances, braille display content to be output, and earcons |
10 * as the |cvox.ChromeVox.tts| and |cvox.ChromeVox.braille| objects, | 10 * played (by name). The |install| method sets appropriate mock |
11 * respectively. Output sent to those objects will then be collected in | 11 * classes as the |cvox.ChromeVox.tts|, |cvox.ChromeVox.braille| and |
12 * an internal queue. | 12 * |cvox.ChromeVox.earcons| objects, respectively. Output sent to |
13 * those objects will then be collected in an internal queue. | |
13 * | 14 * |
14 * Expectations can be added using the |expectSpeech| and |expectBraille| | 15 * Expectations can be added using the |expectSpeech|, |
15 * methods. These methods take either strings or regular expressions to match | 16 * |expectBraille|, and |expectEarcon| methods. These methods take |
16 * against. Strings must match a full utterance (or display content) exactly, | 17 * either strings or regular expressions to match against. Strings |
17 * while a regular expression must match a substring (use anchor operators if | 18 * must match a full utterance (or display content) exactly, while a |
19 * regular expression must match a substring (use anchor operators if | |
18 * needed). | 20 * needed). |
19 * | 21 * |
20 * Function calls may be inserted in the stream of expectations using the | 22 * Function calls may be inserted in the stream of expectations using the |
21 * |call| method. Such callbacks are called after all preceding expectations | 23 * |call| method. Such callbacks are called after all preceding expectations |
22 * have been met, and before any further expectations are matched. Callbacks | 24 * have been met, and before any further expectations are matched. Callbacks |
23 * are called in the order they were added to the mock. | 25 * are called in the order they were added to the mock. |
24 * | 26 * |
25 * The |replay| method starts processing any pending utterances and braille | 27 * The |replay| method starts processing any pending utterances, |
26 * display content and will try to match expectations as new feedback enters | 28 * braille display content, and earcons and will try to match |
27 * the queue asynchronously. When all expectations have been met and callbacks | 29 * expectations as new feedback enters the queue asynchronously. When |
28 * called, the finish callback, if any was provided to the constructor, is | 30 * all expectations have been met and callbacks called, the finish |
29 * called. | 31 * callback, if any was provided to the constructor, is called. |
30 * | 32 * |
31 * This mock class is lean, meaning that feedback that doesn't match | 33 * This mock class is lean, meaning that feedback that doesn't match |
32 * any expectations is silently ignored. | 34 * any expectations is silently ignored. |
33 * | 35 * |
34 * NOTE: for asynchronous tests, the processing will never finish if there | 36 * NOTE: for asynchronous tests, the processing will never finish if there |
35 * are unmet expectations. To help debugging in such situations, the mock | 37 * are unmet expectations. To help debugging in such situations, the mock |
36 * will output its pending state if there are pending expectations and no | 38 * will output its pending state if there are pending expectations and no |
37 * output is received within a few seconds. | 39 * output is received within a few seconds. |
38 * | 40 * |
39 * See mock_feedback_test.js for example usage of this class. | 41 * See mock_feedback_test.js for example usage of this class. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
75 * @private | 77 * @private |
76 */ | 78 */ |
77 this.pendingUtterances_ = []; | 79 this.pendingUtterances_ = []; |
78 /** | 80 /** |
79 * Pending braille output. | 81 * Pending braille output. |
80 * @type {Array<{text: string, callback: (function|undefined)}>} | 82 * @type {Array<{text: string, callback: (function|undefined)}>} |
81 * @private | 83 * @private |
82 */ | 84 */ |
83 this.pendingBraille_ = []; | 85 this.pendingBraille_ = []; |
84 /** | 86 /** |
87 * Pending earcons. | |
88 * @type {Array<{text: string, callback: (function|undefined)}>} | |
89 * @private | |
90 */ | |
91 this.pendingEarcons_ = []; | |
92 /** | |
85 * Handle for the timeout set for debug logging. | 93 * Handle for the timeout set for debug logging. |
86 * @type {number} | 94 * @type {number} |
87 * @private | 95 * @private |
88 */ | 96 */ |
89 this.logTimeoutId_ = 0; | 97 this.logTimeoutId_ = 0; |
90 /** | 98 /** |
91 * @type {cvox.NavBraille} | 99 * @type {cvox.NavBraille} |
92 * @private | 100 * @private |
93 */ | 101 */ |
94 this.lastMatchedBraille_ = null; | 102 this.lastMatchedBraille_ = null; |
(...skipping 16 matching lines...) Expand all Loading... | |
111 | 119 |
112 cvox.ChromeVox.tts = new MockTts(); | 120 cvox.ChromeVox.tts = new MockTts(); |
113 | 121 |
114 var MockBraille = function() {}; | 122 var MockBraille = function() {}; |
115 MockBraille.prototype = { | 123 MockBraille.prototype = { |
116 __proto__: cvox.BrailleInterface.prototype, | 124 __proto__: cvox.BrailleInterface.prototype, |
117 write: this.addBraille_.bind(this) | 125 write: this.addBraille_.bind(this) |
118 }; | 126 }; |
119 | 127 |
120 cvox.ChromeVox.braille = new MockBraille(); | 128 cvox.ChromeVox.braille = new MockBraille(); |
129 | |
130 var MockEarcons = function() {}; | |
131 MockEarcons.prototype = { | |
132 __proto__: cvox.AbstractEarcons.prototype, | |
133 playEarcon: this.addEarcon_.bind(this) | |
134 }; | |
135 | |
136 cvox.ChromeVox.earcons = new MockEarcons(); | |
121 }, | 137 }, |
122 | 138 |
123 /** | 139 /** |
124 * Adds an expectation for one or more spoken utterances. | 140 * Adds an expectation for one or more spoken utterances. |
125 * @param {...(string|RegExp)} var_args One or more utterance to add as | 141 * @param {...(string|RegExp)} var_args One or more utterance to add as |
126 * expectations. | 142 * expectations. |
127 * @return {MockFeedback} |this| for chaining | 143 * @return {MockFeedback} |this| for chaining |
128 */ | 144 */ |
129 expectSpeech: function() { | 145 expectSpeech: function() { |
130 assertFalse(this.replaying_); | 146 assertFalse(this.replaying_); |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
190 return !!match; | 206 return !!match; |
191 }.bind(this), | 207 }.bind(this), |
192 toString: function() { | 208 toString: function() { |
193 return 'Braille \'' + text + '\' ' + JSON.stringify(props); | 209 return 'Braille \'' + text + '\' ' + JSON.stringify(props); |
194 } | 210 } |
195 }); | 211 }); |
196 return this; | 212 return this; |
197 }, | 213 }, |
198 | 214 |
199 /** | 215 /** |
216 * Adds an expectation for a played earcon. | |
217 * @param {string} earconName The name of the earcon. | |
218 * @return {MockFeedback} |this| for chaining | |
219 */ | |
220 expectEarcon: function(earconName, opt_props) { | |
221 assertFalse(this.replaying_); | |
222 this.pendingActions_.push({ | |
223 perform: function() { | |
224 var match = MockFeedback.matchAndConsume_( | |
225 earconName, {}, this.pendingEarcons_); | |
226 return !!match; | |
227 }.bind(this), | |
228 toString: function() { | |
229 return 'Earcon \'' + earconName + '\''; | |
230 } | |
231 }); | |
232 return this; | |
233 }, | |
234 | |
235 /** | |
200 * Arranges for a callback to be invoked when all expectations that were | 236 * Arranges for a callback to be invoked when all expectations that were |
201 * added before this call have been met. Callbacks are called in the | 237 * added before this call have been met. Callbacks are called in the |
202 * order they are added. | 238 * order they are added. |
203 * @param {Function} callback | 239 * @param {Function} callback |
204 * @return {MockFeedback} |this| for chaining | 240 * @return {MockFeedback} |this| for chaining |
205 */ | 241 */ |
206 call: function(callback) { | 242 call: function(callback) { |
207 assertFalse(this.replaying_); | 243 assertFalse(this.replaying_); |
208 this.pendingActions_.push({ | 244 this.pendingActions_.push({ |
209 perform: function() { | 245 perform: function() { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
263 callback: callback}); | 299 callback: callback}); |
264 this.process_(); | 300 this.process_(); |
265 }, | 301 }, |
266 | 302 |
267 /** @private */ | 303 /** @private */ |
268 addBraille_: function(navBraille) { | 304 addBraille_: function(navBraille) { |
269 this.pendingBraille_.push(navBraille); | 305 this.pendingBraille_.push(navBraille); |
270 this.process_(); | 306 this.process_(); |
271 }, | 307 }, |
272 | 308 |
309 /** @private */ | |
310 addEarcon_: function(earconName) { | |
311 this.pendingEarcons_.push({text: earconName}); | |
312 this.process_(); | |
313 }, | |
314 | |
273 /*** @private */ | 315 /*** @private */ |
274 process_: function() { | 316 process_: function() { |
275 if (!this.replaying_ || this.inProcess_) | 317 if (!this.replaying_ || this.inProcess_) |
276 return; | 318 return; |
277 try { | 319 try { |
278 this.inProcess_ = true; | 320 this.inProcess_ = true; |
279 while (this.pendingActions_.length > 0) { | 321 while (this.pendingActions_.length > 0) { |
280 var action = this.pendingActions_[0]; | 322 var action = this.pendingActions_[0]; |
281 if (action.perform()) { | 323 if (action.perform()) { |
282 this.pendingActions_.shift(); | 324 this.pendingActions_.shift(); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
317 var ret = '\'' + i.text + '\''; | 359 var ret = '\'' + i.text + '\''; |
318 if ('startIndex' in i) | 360 if ('startIndex' in i) |
319 ret += ' startIndex=' + i.startIndex; | 361 ret += ' startIndex=' + i.startIndex; |
320 if ('endIndex' in i) | 362 if ('endIndex' in i) |
321 ret += ' endIndex=' + i.endIndex; | 363 ret += ' endIndex=' + i.endIndex; |
322 return ret; | 364 return ret; |
323 }).join('\n ') + '\n '); | 365 }).join('\n ') + '\n '); |
324 } | 366 } |
325 logPending('speech utterances', this.pendingUtterances_); | 367 logPending('speech utterances', this.pendingUtterances_); |
326 logPending('braille', this.pendingBraille_); | 368 logPending('braille', this.pendingBraille_); |
369 logPending('earcons', this.pendingEarcons_); | |
327 this.logTimeoutId_ = 0; | 370 this.logTimeoutId_ = 0; |
328 }, | 371 }, |
329 }; | 372 }; |
330 | 373 |
331 /** | 374 /** |
332 * @param {string} text | 375 * @param {string} text |
333 * @param {Object} props | 376 * @param {Object} props |
334 * @param {Array<{text: (string|RegExp), callback: (function|undefined)}>} | 377 * @param {Array<{text: (string|RegExp), callback: (function|undefined)}>} |
335 * pending | 378 * pending |
336 * @return {Object} | 379 * @return {Object} |
337 * @private | 380 * @private |
338 */ | 381 */ |
339 MockFeedback.matchAndConsume_ = function(text, props, pending) { | 382 MockFeedback.matchAndConsume_ = function(text, props, pending) { |
340 for (var i = 0, candidate; candidate = pending[i]; ++i) { | 383 for (var i = 0, candidate; candidate = pending[i]; ++i) { |
341 var candidateText = candidate.text.toString(); | 384 var candidateText = candidate.text; |
385 if (typeof(candidateText) != 'string') | |
386 candidateText = candidateText.toString(); | |
387 | |
342 if (text === candidateText || | 388 if (text === candidateText || |
343 (text instanceof RegExp && text.test(candidateText))) { | 389 (text instanceof RegExp && text.test(candidateText))) { |
344 var matched = true; | 390 var matched = true; |
345 for (prop in props) { | 391 for (prop in props) { |
346 if (candidate[prop] !== props[prop]) { | 392 if (candidate[prop] !== props[prop]) { |
347 matched = false; | 393 matched = false; |
348 break; | 394 break; |
349 } | 395 } |
350 } | 396 } |
351 if (matched) | 397 if (matched) |
352 break; | 398 break; |
353 } | 399 } |
354 } | 400 } |
355 if (candidate) { | 401 if (candidate) { |
356 var consumed = pending.splice(0, i + 1); | 402 var consumed = pending.splice(0, i + 1); |
357 consumed.forEach(function(item) { | 403 consumed.forEach(function(item) { |
358 if (item.callback) | 404 if (item.callback) |
359 item.callback(); | 405 item.callback(); |
360 }); | 406 }); |
361 } | 407 } |
362 return candidate; | 408 return candidate; |
363 }; | 409 }; |
OLD | NEW |