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

Side by Side Diff: third_party/WebKit/LayoutTests/webaudio/resources/audit.js

Issue 2435593009: New WebAudio layout testing utility: audit.js (Closed)
Patch Set: more progress Created 4 years, 1 month 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 2016 The Chromium Authors. All rights reserved.
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 WebAudio layout test utility library. Built around W3C's
7 * testharness.js. Includes a test task manager, assertion
8 * utilities.
9 * @dependency testharness.js
10 */
11
12 (function () {
13
14 'use strict';
15
16 // Selected properties from testharness.js
17 let testharnessProperties = [
18 'test', 'async_test', 'promise_test', 'promise_rejects',
19 'generate_tests', 'setup', 'done', 'assert_true', 'assert_false'
20 ];
21
22 // Check if testharness.js is properly loaded. Throw otherwise.
23 for (let name in testharnessProperties) {
24 if (!self.hasOwnProperty(testharnessProperties[name])) {
Raymond Toy 2016/11/08 21:14:18 From our BaseAudioContext experience, do we want h
hongchan 2016/11/08 22:07:29 |self| is the global scope, so there is no inherit
25 throw new Error('Cannot proceed. testharness.js is not loaded.');
26 break;
27 }
28 }
29
30 // Attach style to body > pre element.
31 // TODO(hongchan): is this something we want?
Raymond Toy 2016/11/08 21:14:18 Do we really need this? I think we should let tes
hongchan 2016/11/08 22:07:29 For my use case, this significantly improves the r
hongchan 2016/11/29 20:25:01 I decided to remove this.
32 let style = document.createElement('style');
33 style.textContent = 'pre { font-family: "Roboto Mono", monospace; '
34 + 'font-size: 12px; font-weight: 400; text-rendering: optimizeLegibility; '
35 + 'line-height: 16px; }';
36 document.head.appendChild(style);
37 })();
38
39
40 /**
41 * @class Audit
42 * @description A WebAudio layout test task manager.
43 * @example
44 * let audit = Audit.createTaskRunner();
45 * audit.define('task-1', function (describe, should, report) {
46 * describe('the first task');
47 * should(someValue).beEqualTo(someValue);
48 * report('passed!', 'failed.');
49 * });
50 * audit.run();
51 *
52 * @see webaudio/unit-tests/audit.html for more examples.
53 */
54 var Audit = (function () {
55
56 'use strict';
57
58 function _logPassed (message) {
59 test(function (arg) {
60 assert_true(true);
61 }, message);
62 }
63
64 function _logFailed (message, detail) {
65 test(function () {
66 assert_true(false, detail);
67 }, message);
68 }
69
70 function _throwException (message) {
71 throw new Error(message);
72 }
73
74 // Check the expected value if it is a NaN or has NaN(s) in its content (Array
75 // or Float32Array). Returns a result with indices of NaN.
Raymond Toy 2016/11/08 21:14:18 Update comment because it returns for an indices.
hongchan 2016/11/08 22:07:29 This method is confusing. I thought it's clever to
hongchan 2016/11/29 20:25:00 This method is removed. It's better to use isNaN()
76 function _isOrHasNaN (target) {
Raymond Toy 2016/11/08 21:14:18 isOrHasNan? I misread this at first as missing so
hongchan 2016/11/08 22:07:29 Acknowledged.
77 let isOrHasNaN = false;
78 let detail = null;
79
80 if (Number.isNaN(target)) {
81 isOrHasNaN = true;
82 detail = {
83 type: 'number'
84 };
85 } else if (target instanceof Float32Array || target instanceof Array) {
Raymond Toy 2016/11/08 21:14:18 Should we support Float64Array too?
hongchan 2016/11/29 20:25:00 The method is removed.
86 // Collect indices of NaN element in the array.
87 detail = {
88 type: 'array',
89 indicesNaN: []
90 };
91 for (let index in target) {
92 if (Number.isNaN(target[index]))
93 detail.indicesNaN.push(index);
94 }
95
96 isOrHasNaN = detail.indicesNaN.length > 0;
97 } else {
98 // The target is neither number nor array.
99 detail = {
100 type: typeof target
101 };
102 }
103
104 return {
105 isOrHasNaN: isOrHasNaN,
106 detail: detail
107 };
108 }
109
110 // Generate a descriptive string from a test target value in various types.
111 // TODO(hongchan): this might not be needed.
Raymond Toy 2016/11/08 21:14:18 You're using this, so it's needed right?
hongchan 2016/11/08 22:07:29 This needs more work. There are some stuffs that c
hongchan 2016/11/29 20:25:00 I scaled down to a single case (object/array), but
112 function _generateStringFromTestTarget(target) {
113 let targetString = new String(target);
114
115 switch (typeof target) {
116 case 'boolean':
117 // targetString = '"' + targetString + '"(boolean)';
118 break;
119 case 'number':
120 // targetString = '"' + targetString + '"(number)';
121 break;
122 case 'string':
123 // targetString = '"' + targetString + '"(string)';
124 break;
125 case 'object':
126 // Take care of arrays.
127 if (target instanceof Array || target instanceof Float32Array) {
128 targetString = '[' + targetString + ']';
129 }
130 // } else {
Raymond Toy 2016/11/08 21:14:18 This should be removed.
hongchan 2016/11/08 22:07:28 I am still deciding on this. We need some method t
hongchan 2016/11/29 20:25:01 Removed.
131 // targetString = '' + targetString.split(/[\s\]]/)[1] + '(object)';
132 // }
133 break;
134 case 'function':
135 // let elements = targetString.split(' ');
Raymond Toy 2016/11/08 21:14:18 This should be removed.
hongchan 2016/11/08 22:07:29 Noted. I will sit on this few more days.
hongchan 2016/11/29 20:25:01 Removed.
136 // if (elements[1] !== '()') {
137 // // 1. named function? print function name.
138 // targetString = '"' + elements[1] + '"';
139 // } else {
140 // // 2. anon function? print function content.
141 // targetString = '"' + elements.slice(3, elements.length - 1).join(' ')
142 // + '"';
143 // }
144 break;
145 case 'undefined':
146 // targetString = '"' + targetString + '"(undefined)';
147 break;
148 }
149
150 return targetString;
151 }
152
153
154 /**
155 * @class SubtaskReporter
156 * @description If detailed report/summary on a task is necessary, use this
157 * with a assertion subtask.
Raymond Toy 2016/11/08 21:14:18 "a assertion" -> "an assertion" Not really clear
hongchan 2016/11/08 22:07:29 This one is also a bare bone design of the summary
hongchan 2016/11/29 20:25:01 This class is removed.
158 */
159 class SubtaskReporter {
160
161 constructor (should) {
162 this._assertion = should;
163 }
164
165 configure (options) { }
166
167 report (descActual, descExpected) { }
168
169 }
170
171
172 /**
173 * @class Should
174 * @description Assertion subtask for the Audit task.
175 * @param {Task} parentTask Associated Task object.
176 * @param {Any} actual Target value to be tested.
177 * @param {String} actualDescription String description of the test target.
178 */
179 class Should {
180
181 constructor (parentTask, actual, actualDescription) {
182 this._task = parentTask;
183
184 this._actual = actual;
185 this._expected = null;
186 this._options = {};
187
188 this._result = true;
189 this._actualDescription = (actualDescription || null);
190 this._detail = '';
191
192 this._config = {
193 numberOfErrors: 4,
194 numberOfArrayElements: 8,
195 precision: 8,
196 verbose: false
197 };
198 }
199
200 _buildDetailString () {
201 let actualDescription = (this._actualDescription || this._actual);
202
203 // For the assertion with a single operand.
204 this._detail = this._detail.replace(
205 '${actual}', _generateStringFromTestTarget(actualDescription));
206
207 // If there is a second operand (i.e. expected value), we have to build
208 // the string for it as well.
209 if (this._expected) {
210 this._detail = this._detail.replace(
211 '${expected}', _generateStringFromTestTarget(this._expected));
212 }
213
214 // If there is any property in |_options|, replace the property name
215 // with the value.
216 for (let name in this._options) {
217 this._detail = this._detail.replace(
218 '${' + name + '}',
219 _generateStringFromTestTarget(this._options[name]));
220 }
221 }
222
223 _assert (expression, passDetail, failDetail) {
224 if (!expression) {
225 this._result = false;
226 this._detail = failDetail;
227 } else {
228 this._detail = passDetail;
229 }
230
231 this._buildDetailString();
232 return this._finalize();
233 }
234
235 _finalize () {
236 if (this._result) {
237 _logPassed(' ' + this.detail);
238 } else {
239 _logFailed(' X ' + this.detail);
240 }
241
242 this._task.update(this);
243 return new SubtaskReporter(this);
244 }
245
246 get result () {
247 return this._result;
248 }
249
250 get detail () {
251 return this._detail;
252 }
253
254
255 // should() assertions.
256
257
258 /**
259 * Check if |actual| exists.
260 */
261 exist () {
262 return this._assert(
263 this._actual !== null && this._actual !== undefined,
264 '${actual} does exist.',
265 '${actual} does *NOT* exist.');
266 }
267
268 /**
269 * Check if |actual| operation wrapped in a function throws an exception
270 * with a expected error type correctly. |expected| is optional.
271 */
272 throw (expected) {
273 this._expected = expected;
274 let didThrow = false;
275 let passDetail, failDetail;
276
277 try {
278 // This should throw.
279 this._actual();
280 // Catch did not happen, so the test is failed.
281 failDetail = '${actual} did *NOT* throw an exception.';
282 } catch (error) {
283 if (expected === undefined) {
284 didThrow = true;
285 passDetail = '${actual} threw an exception of type '
286 + error.name + '.';
287 } else if (error.name === expected) {
288 didThrow = true;
289 passDetail = '${actual} threw ${expected} : "' + error.message + '".';
290 } else {
291 didThrow = false;
Raymond Toy 2016/11/08 21:14:18 Why false? This did throw, right? If it didn't,
hongchan 2016/11/08 22:07:29 I should rename the variable to |passed|. The thi
hongchan 2016/11/29 20:25:00 Done with a different variable name.
292 failDetail = '${actual} threw "' + error.name
293 + '" instead of ${expected}.';
294 }
295 }
296
297 return this._assert(didThrow, passDetail, failDetail);
298 }
299
300 /**
301 * Check if |actual| operation wrapped in a function does not throws an
302 * exception correctly.
303 */
304 notThrow () {
305 let didThrow = false;
306 let passDetail, failDetail;
307
308 try {
309 this._actual();
310 passDetail = '${actual} did not throw an exception as expected.';
311 } catch (error) {
312 didThrow = true;
313 failDetail = '${actual} threw ' + error.name + ': '
314 + error.message + '.';
315 }
316
317 return this._assert(!didThrow, passDetail, failDetail);
318 }
319
320 /**
321 * Check if |actual| promise is resolved correctly.
322 */
323 beResolved () {
324 return this._actual.then(function () {
325 this._assert(true, '${actual} resolved correctly.', null);
326 }.bind(this), function (error) {
327 this._assert(false, null,
328 '${actual} rejected incorrectly with ' + error + '.');
329 }.bind(this));
330 }
331
332 /**
333 * Check if |actual| promise is rejected correctly.
334 */
335 beRejected () {
336 return this._actual.then(function () {
337 this._assert(false, null, '${actual} resolved incorrectly.');
338 }.bind(this), function (error) {
339 this._assert(true,
340 '${actual} rejected correctly with "' + error + '".', null);
341
342 }.bind(this));
343 }
344
345 /**
346 * Check if |actual| is a boolean true.
347 */
348 beTrue () {
349 return this._assert(
350 this._actual === true,
351 '${actual} is true.',
352 '${actual} is *NOT* true.');
353 }
354
355 /**
356 * Check if |actual| is a boolean false.
357 */
358 beFalse () {
359 return this._assert(
360 this._actual === false,
361 '${actual} is false.',
362 '${actual} is *NOT* false.');
363 }
364
365 /**
366 * Check if |actual| is not a NaN or does not have a NaN in it.
367 */
368 notBeNaN () {
369 let result = _isOrHasNaN(this._actual);
370 let detail = result.detail;
371 let passDetail, failDetail;
372
373 switch (detail.type) {
374 case 'number':
375 if (result.isOrHasNaN)
376 failDetail = '${actual} is NaN.';
377 else
378 passDetail = '${actual} is not NaN.';
379 break;
380 case 'array':
381 if (result.isOrHasNaN) {
382 failDetail = '${actual} contains ' + detail.indicesNaN.length
383 + ' NaNs at the index of (' + detail.indicesNaN.toString() + ')';
384 } else {
385 passDetail = '${actual} does not contain NaN.';
386 }
387 break;
388 default:
389 passDetail = '${actual} is not NaN. (non-Number or non-Array)';
390 break;
391 }
392
393 return this._assert(
394 !result.isOrHasNaN,
395 passDetail,
396 failDetail
397 );
398 }
399
400 /**
401 * Check if |actual| is strictly equal to |expected|. (no type coercion)
402 */
403 beEqualTo (expected) {
404 this._expected = expected;
405 return this._assert(
406 this._actual === expected,
407 '${actual} is equal to ${expected}.',
408 '${actual} is *NOT* equal to ${expected}.');
409 }
410
411 /**
412 * Check if |actual| is not equal to |expected|.
413 */
414 notBeEqualTo (expected) {
415 this._expected = expected;
416 return this._assert(
417 this._actual !== expected,
418 '${actual} is not equal to ${expected}.',
419 '${actual} should *NOT* be equal to ${expected}.');
420 }
421
422 /**
423 * Check if |actual| is greater than or equal to |expected|.
424 */
425 beGreaterThanOrEqualTo (expected) {
426 this._expected = expected;
427 return this._assert(
428 this._actual >= this._expected,
429 '${actual} is greater than or equal to ${expected}.',
430 '${actual} is *NOT* greater than or equal to ${expected}.'
431 );
432 }
433
434 /**
435 * Check if |actual| is greater than |expected|.
436 */
437 beGreaterThan (expected) {
438 this._expected = expected;
439 return this._assert(
440 this._actual > this._expected,
441 '${actual} is greater than ${expected}.',
442 '${actual} is *NOT* greater than ${expected}.'
443 );
444 }
445
446 /**
447 * Check if |actual| is less than or equal to |expected|.
448 */
449 beLessThanOrEqualTo (expected) {
450 this._expected = expected;
451 return this._assert(
452 this._actual <= this._expected,
453 '${actual} is less than or equal to ${expected}.',
454 '${actual} is *NOT* less than or equal to ${expected}.'
455 );
456 }
457
458 /**
459 * Check if |actual| is less than |expected|.
460 */
461 beLessThan (expected) {
462 this._expected = expected;
463 return this._assert(
464 this._actual < this._expected,
465 '${actual} is less than ${expected}.',
466 '${actual} is *NOT* less than ${expected}.'
467 );
468 }
469
470 /**
471 * Check if |actual| array is filled with a constant |expected| value.
472 */
473 beConstantValueOf (expected) {
474 this._expected = expected;
475 let passed = true;
476 let passDetail, failDetail;
477 let errors = {};
478
479 for (let index in this._actual) {
480 if (this._actual[index] !== this._expected)
481 errors[index] = this._actual[index];
482 }
483
484 let numberOfErrors = Object.keys(errors).length;
485 passed = numberOfErrors === 0;
486
487 if (passed) {
488 passDetail = '${actual} contains only the constant ${expected}.';
489 } else {
490 let counter = 0;
491 failDetail = '${actual} contains ' + numberOfErrors
492 + ' values that are *NOT* equal to ${expected}: ';
493 for (let errorIndex in errors) {
494 failDetail += '<' + errorIndex + ':' + errors[errorIndex] + '> ';
495 if (++counter >= 5) {
Raymond Toy 2016/11/08 21:14:18 Probably want 5 to be settable by the test.
hongchan 2016/11/08 22:07:29 The assertion configuration is another matter. It
496 failDetail += ' and ' + (numberOfErrors - counter)
497 + ' more errors.';
498 break;
499 }
500 }
501 }
502
503 return this._assert(passed, passDetail, failDetail);
504 }
505
506 /**
507 * Check if |actual| array is identical to |expected| array element-wise.
508 */
509 beEqualToArray (expected) {
510 this._expected = expected;
511 let passed = true;
512 let passDetail, failDetail;
513 let errorIndices = [];
514
515 if (this._actual.length !== this._expected.length) {
516 passed = false;
517 failDetail = 'The array length does *NOT* match.';
518 return this._assert(passed, passDetail, failDetail);
519 }
520
521 for (let index in this._actual) {
522 if (this._actual[index] !== this._expected[index])
523 errorIndices.push(index);
524 }
525
526 passed = errorIndices.length === 0;
527
528 if (passed) {
529 passDetail = '${actual} is identical to the array ${expected}.';
530 } else {
531 let counter = 0;
532 failDetail = '${actual} contains ' + errorIndices.length
533 + ' values that are *NOT* equal to ${expected}: ';
534 for (let index of errorIndices) {
535 failDetail += '<' + index + ':' + this._actual[index] + ' != '
536 + this._expected[index] + '> ';
537 if (++counter >= 5) {
538 failDetail += ' and ' + (numberOfErrors - counter)
539 + ' more errors.';
540 break;
541 }
542 }
543 }
544
545 return this._assert(passed, passDetail, failDetail);
546 }
547
548 /**
549 * Check if |target| array is close to |expected| array element-wise within
550 * a certain error bound given by the |options|.
551 *
552 * @param {Array} expected Expected array.
553 * @param {Object} Options
554 * @param {Number} Options.absoluteThreshold Absolute threshold.
555 * @param {Number} Options.relativeThreshold Relative threshold.
556 *
557 * The error criterion is:
558 * abs(actual[k] - expected[k]) < max(abserr, relerr * abs(expected))
559 *
560 * If nothing is given for |options|, then abserr = relerr = 0. If
561 * abserr = 0, then the error criterion is a relative error. A non-zero
562 * abserr value produces a mix intended to handle the case where the
563 * expected value is 0, allowing the target value to differ by abserr from
564 * the expected.
565 */
566 beCloseToArray (expected, options) {
567 this._expected = expected;
568 this._options = options;
569 let passed = true;
570 let passDetail, failDetail;
571
572 // Parsing options.
573 let absErrorThreshold = (this._options.absoluteThreshold || 0);
574 let relErrorThreshold = (this._options.relativeThreshold || 0);
575
576 // A collection of all of the values that satisfy the error criterion.
577 // This holds the absolute difference between the target element and the
578 // expected element.
579 let errors = {};
580
581 // Keep track of the max absolute error found.
582 let maxAbsError = -Infinity, maxAbsErrorIndex = -1;
583
584 // Keep track of the max relative error found, ignoring cases where the
585 // relative error is Infinity because the expected value is 0.
586 let maxRelError = -Infinity, maxRelErrorIndex = -1;
587
588 for (let index in this._expected) {
589 let diff = Math.abs(this._actual[index] - this._expected[index]);
590 let absExpected = Math.abs(this._expected[index]);
591 let relError = diff / absExpected;
592
593 if (diff > Math.max(absErrorThreshold,
594 relErrorThreshold * absExpected)) {
595
596 if (diff > maxAbsError) {
597 maxAbsErrorIndex = index;
598 maxAbsError = diff;
599 }
600
601 if (!isNaN(relError) && relError > maxRelError) {
602 maxRelErrorIndex = index;
603 maxRelError = relError;
604 }
605
606 errors[index] = diff;
607 }
608 }
609
610 let numberOfErrors = Object.keys(errors).length;
611 let maxAllowedErrorDetail = JSON.stringify({
612 absoluteThreshold: absErrorThreshold,
613 relativeThreshold: relErrorThreshold
614 });
615
616 if (numberOfErrors === 0) {
617 passDetail = '${actual} equals ${expected} with an element-wise '
618 + 'tolerance of ' + maxAllowedErrorDetail + '.';
619 } else {
620 passed = false;
621 failDetail = '${actual} does not equal ${expected} with an '
622 + 'element-wise tolerance of ' + maxAllowedErrorDetail + '.';
Raymond Toy 2016/11/08 21:14:18 This seems a regression from the old Audit that pr
hongchan 2016/11/08 22:07:29 beCloseToArray() was an overloaded beast. What typ
Raymond Toy 2016/11/14 17:44:34 Recall we used to print out a short summary and a
623 }
624
625 return this._assert(passed, passDetail, failDetail);
626 }
627
628 /**
629 * Check if |actual| array contains a set of |expected| values in a certain
Raymond Toy 2016/11/08 21:14:18 Need to expand on this to explain what "certain or
hongchan 2016/11/08 22:07:29 Perhaps a 'user-defined' order?
Raymond Toy 2016/11/14 17:44:34 Yes, that's much clearer. Or just say the array c
hongchan 2016/11/29 20:25:01 Done.
630 * order.
631 */
632 containValues (expected) {
633 this._expected = expected;
634 let passed = true;
635 let indexActual = 0, indexExpected = 0;
636
637 while (indexActual < this._actual.length
638 && indexExpected < this._expected.length) {
639 if (this._actual[indexActual] === this._expected[indexExpected]) {
640 indexActual++;
641 } else {
642 indexExpected++;
643 }
644 }
645
646 passed = !(indexActual < this._actual.length - 1
647 || indexExpected < this._expected.length - 1);
648
649 return this._assert(
650 passed,
651 '${actual} contains all the expected values in the correct order: '
652 + '${expected}.',
653 '${actual} contains an unexpected value of '
654 + this._actual[indexActual] + ' at index ' + indexActual + '.');
655 }
656
657 /**
658 * Check if |actual| array does not have any glitches. Note that |threshold|
Raymond Toy 2016/11/08 21:14:18 Needs more detailed explanation of what "glitch" m
hongchan 2016/11/08 22:07:29 Nope. The comments are same. I didn't use the exam
Raymond Toy 2016/11/14 17:44:34 Prefer 1 because it's easier to find; I won't be l
hongchan 2016/11/29 20:25:01 Added examples. Your request on the last comment w
659 * is not optional and is to define the desired threshold value.
660 */
661 notGlitch (threshold) {
662 this._expected = threshold;
663 let passed = true;
664 let passDetail, failDetail;
665
666 for (let index in this._actual) {
667 let diff = Math.abs(this._actual[index - 1] - this._actual[index]);
668 if (diff >= this._expected) {
669 passed = false;
670 failDetail = '${actual} has a glitch at index ' + index + ' of size '
671 + diff + '.';
672 }
673 }
674
675 passDetail =
676 '${actual} has no glitch above the threshold of ${expected}.';
677
678 return this._assert(passed, passDetail, failDetail);
679 }
680
681 /**
682 * Check if |actual| is close to |expected| using the given relative error
683 * |threshold|.
684 */
685 beCloseTo (expected, threshold) {
686 this._expected = expected;
687 this._options.threshold = threshold;
688
689 let absExpected = this._expected ? Math.abs(this._expected) : 1;
690 let error = Math.abs(this._actual - this._expected) / absExpected;
691
692 return this._assert(
693 error < threshold,
694 '${actual} is ${expected} within an error of ${threshold}',
695 '${actual} is not ${expected} within a error of ${threshold}: ' +
696 '${actual} with error of ${threshold}.');
697 }
698 }
699
700
701 /**
702 * @class Task
703 * @description WebAudio testing task. Managed by TaskRunner.
704 */
705 class Task {
706
707 constructor (taskRunner, taskLabel, taskFunction) {
708 this._taskRunner = taskRunner;
709 this._taskFunction = taskFunction;
710 this._label = taskLabel;
711 this._description = '';
712 this._state = 'PENDING';
713 this._result = true;
714
715 this._totalAssertions = 0;
716 this._failedAssertions = 0;
717 }
718
719 // Set the description of this task. This is printed out in the test
720 // result.
721 describe (message) {
722 this._description = message;
723 _logPassed('>> [' + this._label + '] '
724 + this._description);
725 }
726
727 get state () {
728 return this._state;
729 }
730
731 get result () {
732 return this._result;
733 }
734
735 // Start the assertion chain.
736 should (actual, actualDescription) {
737 return new Should(this, actual, actualDescription);
738 }
739
740 // Run this task. |this| task will be passed into the user-supplied test
741 // task function.
742 run () {
743 this._state = 'STARTED';
744 this._taskFunction(
745 this,
746 this.should.bind(this));
747 }
748
749 // Update the task success based on the individual assertion/test inside.
750 update (subTask) {
751 // After one of tests fails within a task, the result is irreversible.
752 if (subTask.result === false) {
753 this._result = false;
754 this._failedAssertions++;
755 }
756
757 this._totalAssertions++;
758 }
759
760 // Finish the current task and start the next one if available.
761 done () {
762 this._state = 'FINISHED';
763
764 let message = '<< [' + this._label + '] ';
765
766 if (this._result) {
767 message += 'All assertion passed. (total ' + this._totalAssertions
768 + ' assertions)';
769 _logPassed(message);
770 } else {
771 message += this._failedAssertions + ' out of ' + this._totalAssertions
772 + ' assertions were failed.'
773 _logFailed(message);
774 }
775
776 this._taskRunner._runNextTask();
777 }
778
779 isPassed () {
780 return this._state === 'FINISHED' && this._result;
781 }
782
783 toString () {
784 return '"' + this._label + '": ' + this._description;
785 }
786
787 }
788
789
790 /**
791 * @class TaskRunner
792 * @description WebAudio testing task runner. Manages tasks.
793 */
794 class TaskRunner {
795
796 constructor () {
797 this._tasks = {};
798 this._taskSequence = [];
799 this._currentTaskIndex = -1;
800
801 // Configure testharness.js for the async operation.
802 setup (new Function (), {
803 explicit_done: true
804 });
805 }
806
807 define (taskLabel, taskFunction) {
808 if (this._tasks.hasOwnProperty(taskLabel)) {
809 _throwException('Audit.define:: Duplicate task definition.');
810 return;
811 }
812
813 this._tasks[taskLabel] = new Task(this, taskLabel, taskFunction);
814 this._taskSequence.push(taskLabel);
815 }
816
817 // Start running all the tasks scheduled.
Raymond Toy 2016/11/08 21:14:18 Probably want to describe the optional args to run
hongchan 2016/11/08 22:07:29 It's being properly handled. See the if statement
hongchan 2016/11/29 20:25:01 Oh, I misunderstood your comment. Added comments a
818 run () {
819 // Display the beginning of the test suite.
820 _logPassed('## AUDIT TASK RUNNER STARTED.');
821
822 // If the argument is specified, override the default task sequence with
823 // the specified one.
824 if (arguments.length > 0) {
825 this._taskSequence = [];
826 for (let i = 0; arguments.length; i++) {
827 let taskLabel = arguments[i];
828 if (!this._tasks.hasOwnProperty(taskLabel)) {
829 _throwException('Audit.run:: undefined task.');
830 } else if (this._taskSequence.includes(taskLabel)) {
831 _throwException('Audit.run:: duplicate task request.');
832 } else {
833 this._taskSequence.push[taskLabel];
834 }
835 }
836 }
837
838 if (this._taskSequence.length === 0) {
839 _throwException('Audit.run:: no task to run.');
840 return;
841 }
842
843 // Start the first task.
844 this._currentTaskIndex = 0;
845 this._runNextTask();
846 }
847
848 _runNextTask () {
849 if (this._currentTaskIndex < this._taskSequence.length) {
850 this._tasks[this._taskSequence[this._currentTaskIndex++]].run();
851 } else {
852 this._finish();
853 }
854 }
855
856 _finish () {
857 let numberOfFailures = 0;
858 for (let taskIndex in this._taskSequence) {
859 let task = this._tasks[this._taskSequence[taskIndex]];
860 numberOfFailures += task.result ? 0 : 1;
861 }
862
863 let prefix = '## AUDIT TASK RUNNER FINISHED: ';
864 if (numberOfFailures > 0) {
865 _logFailed(prefix + numberOfFailures + ' out of '
866 + this._taskSequence.length + ' tasks were failed.');
867 } else {
868 _logPassed(prefix + this._taskSequence.length
869 + ' tasks were successfully executed.');
870 }
871
872 // From testharness.js, report back to the test infrastructure that
873 // the task runner completed all the tasks.
874 done();
875 }
876 }
877
878
879 return {
880
881 createTaskRunner: function () {
882 return new TaskRunner();
883 }
884
885 };
886
887 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698