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

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

Issue 2435593009: New WebAudio layout testing utility: audit.js (Closed)
Patch Set: Fixing nits after l-g-t-m Created 4 years 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 | « no previous file | third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 /**
7 * @fileOverview WebAudio layout test utility library. Built around W3C's
8 * testharness.js. Includes asynchronous test task manager,
9 * assertion utilities.
10 * @dependency testharness.js
11 * @seealso webaudio/unit-tests/audit.html for actual examples.
12 */
13
14
15 (function () {
16
17 'use strict';
18
19 // Selected properties from testharness.js
20 let testharnessProperties = [
21 'test', 'async_test', 'promise_test', 'promise_rejects',
22 'generate_tests', 'setup', 'done', 'assert_true', 'assert_false'
23 ];
24
25 // Check if testharness.js is properly loaded. Throw otherwise.
26 for (let name in testharnessProperties) {
27 if (!self.hasOwnProperty(testharnessProperties[name])) {
28 throw new Error('Cannot proceed. testharness.js is not loaded.');
29 break;
30 }
31 }
32 })();
33
34
35 /**
36 * @class Audit
37 * @description A WebAudio layout test task manager.
38 * @example
39 * let audit = Audit.createTaskRunner();
40 * audit.define('first-task', function (task, should) {
41 * task.describe('the first task');
42 * should(someValue).beEqualTo(someValue);
43 * task.done();
44 * });
45 * audit.run();
46 */
47 window.Audit = (function () {
48
49 'use strict';
50
51 function _logPassed (message) {
52 test(function (arg) {
53 assert_true(true);
54 }, message);
55 }
56
57 function _logFailed (message, detail) {
58 test(function () {
59 assert_true(false, detail);
60 }, message);
61 }
62
63 function _throwException (message) {
64 throw new Error(message);
65 }
66
67 // Generate a descriptive string from a target value in various types.
68 function _generateDescription (target, options) {
69 let targetString;
70
71 switch (typeof target) {
72 case 'object':
73 // Handle Arrays.
74 if (target instanceof Array || target instanceof Float32Array ||
75 target instanceof Float64Array) {
76 let arrayElements = target.length < options.numberOfArrayElements
77 ? String(target)
78 : String(target.slice(0, options.numberOfArrayElements)) + '...';
79 targetString = '[' + arrayElements + ']';
80 } else {
81 targetString = '' + String(targetString).split(/[\s\]]/)[1];
82 }
83 break;
84 default:
85 targetString = String(target);
86 break;
87 }
88
89 return targetString;
90 }
91
92
93 /**
94 * @class Should
95 * @description Assertion subtask for the Audit task.
96 * @param {Task} parentTask Associated Task object.
97 * @param {Any} actual Target value to be tested.
98 * @param {String} actualDescription String description of the test target.
99 */
100 class Should {
101
102 constructor (parentTask, actual, actualDescription) {
103 this._task = parentTask;
104
105 this._actual = actual;
106 this._actualDescription = (actualDescription || null);
107 this._expected = null;
108 this._expectedDescription = null;
109
110 this._result = true;
111 this._detail = '';
112
113 /**
114 * @param {Number} numberOfErrors Number of errors to be printed.
115 * @param {Number} numberOfArrayElements Number of array elements to be
116 * printed in the test log.
117 * @param {Boolean} verbose Verbose output from the assertion.
118 */
119 this._options = {
120 numberOfErrors: 4,
121 numberOfArrayElements: 16,
122 verbose: false
123 };
124 }
125
126 _processArguments (args) {
127 if (args.length === 0)
128 return;
129
130 if (args.length > 0)
131 this._expected = args[0];
132
133 if (typeof args[1] === 'string') {
134 // case 1: (expected, description, options)
135 this._expectedDescription = args[1];
136 Object.assign(this._options, args[2]);
137 } else if (typeof args[1] === 'object') {
138 // case 2: (expected, options)
139 Object.assign(this._options, args[1]);
140 }
141 }
142
143 _buildResultText () {
144 if (!this._actualDescription) {
145 this._actualDescription =
146 _generateDescription(this._actual, this._options);
147 }
148
149 if (!this._expectedDescription) {
150 this._expectedDescription =
151 _generateDescription(this._expected, this._options);
152 }
153
154 // For the assertion with a single operand.
155 this._detail = this._detail.replace('${actual}', this._actualDescription);
156
157 // If there is a second operand (i.e. expected value), we have to build
158 // the string for it as well.
159 if (this._expected) {
160 this._detail = this._detail.replace(
161 '${expected}', this._expectedDescription);
162 }
163
164 // If there is any property in |_options|, replace the property name
165 // with the value.
166 for (let name in this._options) {
167 this._detail = this._detail.replace(
168 '${' + name + '}',
169 _generateDescription(this._options[name]));
170 }
171 }
172
173 _finalize () {
174 if (this._result) {
175 _logPassed(' ' + this._detail);
176 } else {
177 _logFailed('X ' + this._detail);
178 }
179
180 // This assertion is finished, so update the parent task accordingly.
181 this._task.update(this);
182
183 // TODO(hongchan): configurable 'detail' message.
184 }
185
186 _assert (condition, passDetail, failDetail) {
187 this._result = Boolean(condition);
188 this._detail = this._result ? passDetail : failDetail;
189 this._buildResultText();
190 return this._finalize();
191 }
192
193 get result () {
194 return this._result;
195 }
196
197 get detail () {
198 return this._detail;
199 }
200
201 /**
202 * should() assertions.
203 *
204 * @example All the assertions can have 1, 2 or 3 arguments:
205 * should().doAssert(expected);
206 * should().doAssert(expected, options);
207 * should().doAssert(expected, expectedDescription, options);
208 *
209 * @param {Any} expected Expected value of the assertion.
210 * @param {String} expectedDescription Description of expected value.
211 * @param {Object} options Options for assertion.
212 * @param {Number} options.numberOfErrors Number of errors to be printed.
213 * (if applicable)
214 * @param {Number} options.numberOfArrayElements Number of array elements
215 * to be printed. (if
216 * applicable)
217 * @notes Some assertions can have additional options for their specific
218 * testing.
219 */
220
221 /**
222 * Check if |actual| exists.
223 *
224 * @example
225 * should({}, 'An empty object').exist();
226 * @result
227 * "PASS An empty object does exist."
228 */
229 exist () {
230 return this._assert(
231 this._actual !== null && this._actual !== undefined,
232 '${actual} does exist.',
233 '${actual} does *NOT* exist.');
234 }
235
236 /**
237 * Check if |actual| operation wrapped in a function throws an exception
238 * with a expected error type correctly. |expected| is optional.
239 *
240 * @example
241 * should(() => { let a = b; }, 'A bad code').throw();
242 * should(() => { let c = d; }, 'Assigning d to c.')
243 * .throw('ReferenceError');
244 *
245 * @result
246 * "PASS A bad code threw an exception of ReferenceError."
247 * "PASS Assigning d to c threw ReferenceError."
248 */
249 throw () {
250 this._processArguments(arguments);
251
252 let didThrowCorrectly = false;
253 let passDetail, failDetail;
254
255 try {
256 // This should throw.
257 this._actual();
258 // Catch did not happen, so the test is failed.
259 failDetail = '${actual} did *NOT* throw an exception.';
260 } catch (error) {
261 if (this._expected === undefined) {
262 didThrowCorrectly = true;
263 passDetail = '${actual} threw an exception of ' + error.name + '.';
264 } else if (error.name === this._expected) {
265 didThrowCorrectly = true;
266 passDetail = '${actual} threw ${expected} : "' + error.message + '".';
267 } else {
268 didThrowCorrectly = false;
269 failDetail = '${actual} threw "' + error.name
270 + '" instead of ${expected}.';
271 }
272 }
273
274 return this._assert(didThrowCorrectly, passDetail, failDetail);
275 }
276
277 /**
278 * Check if |actual| operation wrapped in a function does not throws an
279 * exception correctly.
280 *
281 * @example
282 * should(() => { let foo = 'bar'; }, 'let foo = "bar"').notThrow();
283 *
284 * @result
285 * "PASS let foo = "bar" did not throw an exception."
286 */
287 notThrow () {
288 let didThrowCorrectly = false;
289 let passDetail, failDetail;
290
291 try {
292 this._actual();
293 passDetail = '${actual} did not throw an exception.';
294 } catch (error) {
295 didThrowCorrectly = true;
296 failDetail = '${actual} threw ' + error.name + ': '
297 + error.message + '.';
298 }
299
300 return this._assert(!didThrowCorrectly, passDetail, failDetail);
301 }
302
303 /**
304 * Check if |actual| promise is resolved correctly.
305 *
306 * @example
307 * should('My promise', promise).beResolve().then(nextStuff);
308 *
309 * @result
310 * "PASS My promise resolved correctly."
311 * "FAIL X My promise rejected *INCORRECTLY* with _ERROR_."
312 */
313 beResolved () {
314 return this._actual.then(function () {
315 this._assert(true, '${actual} resolved correctly.', null);
316 }.bind(this), function (error) {
317 this._assert(false, null,
318 '${actual} rejected *INCORRECTLY* with ' + error + '.');
319 }.bind(this));
320 }
321
322 /**
323 * Check if |actual| promise is rejected correctly.
324 *
325 * @example
326 * should('My promise', promise).beRejected().then(nextStuff);
327 *
328 * @result
329 * "PASS My promise rejected correctly (with _ERROR_)."
330 * "FAIL X My promise resolved *INCORRECTLY*."
331 */
332 beRejected () {
333 return this._actual.then(function () {
334 this._assert(false, null, '${actual} resolved *INCORRECTLY*.');
335 }.bind(this), function (error) {
336 this._assert(true,
337 '${actual} rejected correctly with ' + error + '.', null);
338 }.bind(this));
339 }
340
341 /**
342 * Check if |actual| is a boolean true.
343 *
344 * @example
345 * should(3 < 5, '3 < 5').beTrue();
346 *
347 * @result
348 * "PASS 3 < 5 is true."
349 */
350 beTrue () {
351 return this._assert(
352 this._actual === true,
353 '${actual} is true.',
354 '${actual} is *NOT* true.');
355 }
356
357 /**
358 * Check if |actual| is a boolean false.
359 *
360 * @example
361 * should(3 > 5, '3 > 5').beFalse();
362 *
363 * @result
364 * "PASS 3 > 5 is false."
365 */
366 beFalse () {
367 return this._assert(
368 this._actual === false,
369 '${actual} is false.',
370 '${actual} is *NOT* false.');
371 }
372
373 /**
374 * Check if |actual| is strictly equal to |expected|. (no type coercion)
375 *
376 * @example
377 * should(1).beEqualTo(1);
378 *
379 * @result
380 * "PASS 1 is equal to 1."
381 */
382 beEqualTo () {
383 this._processArguments(arguments);
384 return this._assert(
385 this._actual === this._expected,
386 '${actual} is equal to ${expected}.',
387 '${actual} is *NOT* equal to ${expected}.');
388 }
389
390 /**
391 * Check if |actual| is not equal to |expected|.
392 *
393 * @example
394 * should(1).notBeEqualTo(2);
395 *
396 * @result
397 * "PASS 1 is *NOT* equal to 2."
398 */
399 notBeEqualTo () {
400 this._processArguments(arguments);
401 return this._assert(
402 this._actual !== this._expected,
403 '${actual} is not equal to ${expected}.',
404 '${actual} should *NOT* be equal to ${expected}.');
405 }
406
407 /**
408 * Check if |actual| is greater than |expected|.
409 *
410 * @example
411 * should(2).beGreaterThanOrEqualTo(2);
412 *
413 * @result
414 * "PASS 2 is greater than or equal to 2."
415 */
416 beGreaterThan () {
417 this._processArguments(arguments);
418 return this._assert(
419 this._actual > this._expected,
420 '${actual} is greater than ${expected}.',
421 '${actual} is *NOT* greater than ${expected}.'
422 );
423 }
424
425 /**
426 * Check if |actual| is greater than or equal to |expected|.
427 *
428 * @example
429 * should(2).beGreaterThan(1);
430 *
431 * @result
432 * "PASS 2 is greater than 1."
433 */
434 beGreaterThanOrEqualTo () {
435 this._processArguments(arguments);
436 return this._assert(
437 this._actual >= this._expected,
438 '${actual} is greater than or equal to ${expected}.',
439 '${actual} is *NOT* greater than or equal to ${expected}.'
440 );
441 }
442
443 /**
444 * Check if |actual| is less than |expected|.
445 *
446 * @example
447 * should(1).beLessThan(2);
448 *
449 * @result
450 * "PASS 1 is less than 2."
451 */
452 beLessThan () {
453 this._processArguments(arguments);
454 return this._assert(
455 this._actual < this._expected,
456 '${actual} is less than ${expected}.',
457 '${actual} is *NOT* less than ${expected}.'
458 );
459 }
460
461 /**
462 * Check if |actual| is less than or equal to |expected|.
463 *
464 * @example
465 * should(1).beLessThanOrEqualTo(1);
466 *
467 * @result
468 * "PASS 1 is less than or equal to 1."
469 */
470 beLessThanOrEqualTo () {
471 this._processArguments(arguments);
472 return this._assert(
473 this._actual <= this._expected,
474 '${actual} is less than or equal to ${expected}.',
475 '${actual} is *NOT* less than or equal to ${expected}.'
476 );
477 }
478
479 /**
480 * Check if |actual| array is filled with a constant |expected| value.
481 *
482 * @example
483 * should([1, 1, 1]).beConstantValueOf(1);
484 *
485 * @result
486 * "PASS [1,1,1] contains only the constant 1."
487 */
488 beConstantValueOf () {
489 this._processArguments(arguments);
490
491 let passed = true;
492 let passDetail, failDetail;
493 let errors = {};
494
495 for (let index in this._actual) {
496 if (this._actual[index] !== this._expected)
497 errors[index] = this._actual[index];
498 }
499
500 let numberOfErrors = Object.keys(errors).length;
501 passed = numberOfErrors === 0;
502
503 if (passed) {
504 passDetail = '${actual} contains only the constant ${expected}.';
505 } else {
506 let counter = 0;
507 failDetail = '${actual} contains ' + numberOfErrors
508 + ' values that are *NOT* equal to ${expected}: ';
509 failDetail += '\n\tIndex\tActual';
510 for (let errorIndex in errors) {
511 failDetail += '\n\t[' + errorIndex + ']'
512 + '\t' + errors[errorIndex];
513 if (++counter >= this._options.numberOfErrors) {
514 failDetail += '\n\t...and ' + (numberOfErrors - counter)
515 + ' more errors.';
516 break;
517 }
518 }
519 }
520
521 return this._assert(passed, passDetail, failDetail);
522 }
523
524 /**
525 * Check if |actual| array is identical to |expected| array element-wise.
526 *
527 * @example
528 * should([1, 2, 3]).beEqualToArray([1, 2, 3]);
529 *
530 * @result
531 * "[1,2,3] is identical to the array [1,2,3]."
532 */
533 beEqualToArray () {
534 this._processArguments(arguments);
535
536 let passed = true;
537 let passDetail, failDetail;
538 let errorIndices = [];
539
540 if (this._actual.length !== this._expected.length) {
541 passed = false;
542 failDetail = 'The array length does *NOT* match.';
543 return this._assert(passed, passDetail, failDetail);
544 }
545
546 for (let index in this._actual) {
547 if (this._actual[index] !== this._expected[index])
548 errorIndices.push(index);
549 }
550
551 passed = errorIndices.length === 0;
552
553 if (passed) {
554 passDetail = '${actual} is identical to the array ${expected}.';
555 } else {
556 let counter = 0;
557 failDetail = '${actual} contains ' + errorIndices.length
558 + ' values that are *NOT* equal to ${expected}: ';
559 failDetail += '\n\tIndex\tActual\t\t\tExpected';
560 for (let index of errorIndices) {
561 failDetail += '\n\t[' + index + ']'
562 + '\t' + this._actual[index].toExponential(16)
563 + '\t' + this._expected[index].toExponential(16);
564 if (++counter >= this._options.numberOfErrors) {
565 failDetail += '\n\t...and ' + (numberOfErrors - counter)
566 + ' more errors.';
567 break;
568 }
569 }
570 }
571
572 return this._assert(passed, passDetail, failDetail);
573 }
574
575 /**
576 * Check if |actual| array contains only the values in |expected| in the
577 * order of values in |expected|.
578 *
579 * @example
580 * Should([1, 1, 3, 3, 2], 'My random array').containValues([1, 3, 2]);
581 *
582 * @result
583 * "PASS [1,1,3,3,2] contains all the expected values in the correct
584 * order: [1,3,2].
585 */
586 containValues () {
587 this._processArguments(arguments);
588
589 let passed = true;
590 let indexActual = 0, indexExpected = 0;
591
592 while (indexActual < this._actual.length
593 && indexExpected < this._expected.length) {
594 if (this._actual[indexActual] === this._expected[indexExpected]) {
595 indexActual++;
596 } else {
597 indexExpected++;
598 }
599 }
600
601 passed = !(indexActual < this._actual.length - 1
602 || indexExpected < this._expected.length - 1);
603
604 return this._assert(
605 passed,
606 '${actual} contains all the expected values in the correct order: '
607 + '${expected}.',
608 '${actual} contains an unexpected value of '
609 + this._actual[indexActual] + ' at index ' + indexActual + '.');
610 }
611
612 /**
613 * Check if |actual| array does not have any glitches. Note that |threshold|
614 * is not optional and is to define the desired threshold value.
615 *
616 * @example
617 * should([0.5, 0.5, 0.55, 0.5, 0.45, 0.5]).notGlitch(0.06);
618 *
619 * @result
620 * "PASS [0.5,0.5,0.55,0.5,0.45,0.5] has no glitch above the threshold
621 * of 0.06."
622 *
623 */
624 notGlitch () {
625 this._processArguments(arguments);
626
627 let passed = true;
628 let passDetail, failDetail;
629
630 for (let index in this._actual) {
631 let diff = Math.abs(this._actual[index - 1] - this._actual[index]);
632 if (diff >= this._expected) {
633 passed = false;
634 failDetail = '${actual} has a glitch at index ' + index + ' of size '
635 + diff + '.';
636 }
637 }
638
639 passDetail =
640 '${actual} has no glitch above the threshold of ${expected}.';
641
642 return this._assert(passed, passDetail, failDetail);
643 }
644
645 /**
646 * Check if |actual| is close to |expected| using the given relative error
647 * |threshold|.
648 *
649 * @example
650 * should(2.3).beCloseTo(2, 0.3);
651 *
652 * @result
653 * "PASS 2.3 is 2 within an error of 0.3."
654 *
655 * @param {Number} options.threshold Threshold value for the comparison.
656 */
657 beCloseTo () {
658 this._processArguments(arguments);
659
660 let absExpected = this._expected ? Math.abs(this._expected) : 1;
661 let error = Math.abs(this._actual - this._expected) / absExpected;
662
663 return this._assert(
664 error < this._options.threshold,
665 '${actual} is ${expected} within an error of ${threshold}',
666 '${actual} is not ${expected} within a error of ${threshold}: ' +
667 '${actual} with error of ${threshold}.');
668 }
669
670 /**
671 * Check if |target| array is close to |expected| array element-wise within
672 * a certain error bound given by the |options|.
673 *
674 * The error criterion is:
675 * abs(actual[k] - expected[k]) < max(absErr, relErr * abs(expected))
676 *
677 * If nothing is given for |options|, then absErr = relErr = 0. If
678 * absErr = 0, then the error criterion is a relative error. A non-zero
679 * absErr value produces a mix intended to handle the case where the
680 * expected value is 0, allowing the target value to differ by absErr from
681 * the expected.
682 *
683 * @param {Number} options.absoluteThreshold Absolute threshold.
684 * @param {Number} options.relativeThreshold Relative threshold.
685 */
686 beCloseToArray () {
687 this._processArguments(arguments);
688
689 let passed = true;
690 let passDetail, failDetail;
691
692 // Parsing options.
693 let absErrorThreshold = (this._options.absoluteThreshold || 0);
694 let relErrorThreshold = (this._options.relativeThreshold || 0);
695
696 // A collection of all of the values that satisfy the error criterion.
697 // This holds the absolute difference between the target element and the
698 // expected element.
699 let errors = {};
700
701 // Keep track of the max absolute error found.
702 let maxAbsError = -Infinity, maxAbsErrorIndex = -1;
703
704 // Keep track of the max relative error found, ignoring cases where the
705 // relative error is Infinity because the expected value is 0.
706 let maxRelError = -Infinity, maxRelErrorIndex = -1;
707
708 for (let index in this._expected) {
709 let diff = Math.abs(this._actual[index] - this._expected[index]);
710 let absExpected = Math.abs(this._expected[index]);
711 let relError = diff / absExpected;
712
713 if (diff > Math.max(absErrorThreshold,
714 relErrorThreshold * absExpected)) {
715
716 if (diff > maxAbsError) {
717 maxAbsErrorIndex = index;
718 maxAbsError = diff;
719 }
720
721 if (!isNaN(relError) && relError > maxRelError) {
722 maxRelErrorIndex = index;
723 maxRelError = relError;
724 }
725
726 errors[index] = diff;
727 }
728 }
729
730 let numberOfErrors = Object.keys(errors).length;
731 let maxAllowedErrorDetail = JSON.stringify({
732 absoluteThreshold: absErrorThreshold,
733 relativeThreshold: relErrorThreshold
734 });
735
736 if (numberOfErrors === 0) {
737 // The assertion was successful.
738 passDetail = '${actual} equals ${expected} with an element-wise '
739 + 'tolerance of ' + maxAllowedErrorDetail + '.';
740 } else {
741 // Failed. Prepare the detailed failure log.
742 passed = false;
743 failDetail = '${actual} does not equal ${expected} with an '
744 + 'element-wise tolerance of ' + maxAllowedErrorDetail + '.\n';
745
746 // Print out actual, expected, absolute error, and relative error.
747 let counter = 0;
748 failDetail += '\tIndex\tActual\t\t\tExpected\t\tAbsError'
749 + '\t\tRelError\t\tTest threshold';
750 for (let index in errors) {
751 failDetail += '\n\t[' + index + ']\t'
752 + this._actual[index].toExponential(16) + '\t'
753 + this._expected[index].toExponential(16) + '\t'
754 + errors[index].toExponential(16) + '\t'
755 + (errors[index] / Math.abs(this._expected[index]))
756 .toExponential(16) + '\t'
757 + Math.max(absErrorThreshold,
758 relErrorThreshold * Math.abs(this._expected[index]))
759 .toExponential(16);
760 if (++counter > this._options.numberOfErrors)
761 break;
762 }
763
764 // Finalize the error log: print out the location of both the maxAbs
765 // error and the maxRel error so we can adjust thresholds appropriately
766 // in the test.
767 failDetail += '\n'
768 + '\tMax AbsError of ' + maxAbsError.toExponential(16)
769 + ' at index of ' + maxAbsErrorIndex + '.\n'
770 + '\tMax RelError of ' + maxRelError.toExponential(16)
771 + ' at index of ' + maxRelErrorIndex + '.';
772 }
773
774 return this._assert(passed, passDetail, failDetail);
775 }
776
777 }
778
779
780 // Task Class state enum.
781 const TaskState = {
782 PENDING: 0,
783 STARTED: 1,
784 FINISHED: 2
785 };
786
787
788 /**
789 * @class Task
790 * @description WebAudio testing task. Managed by TaskRunner.
791 */
792 class Task {
793
794 constructor (taskRunner, taskLabel, taskFunction) {
795 this._taskRunner = taskRunner;
796 this._taskFunction = taskFunction;
797 this._label = taskLabel;
798 this._description = '';
799 this._state = TaskState.PENDING;
800 this._result = true;
801
802 this._totalAssertions = 0;
803 this._failedAssertions = 0;
804 }
805
806 // Set the description of this task. This is printed out in the test
807 // result.
808 describe (message) {
809 this._description = message;
810 _logPassed('> [' + this._label + '] '
811 + this._description);
812 }
813
814 get state () {
815 return this._state;
816 }
817
818 get result () {
819 return this._result;
820 }
821
822 // Start the assertion chain.
823 should (actual, actualDescription) {
824 return new Should(this, actual, actualDescription);
825 }
826
827 // Run this task. |this| task will be passed into the user-supplied test
828 // task function.
829 run () {
830 this._state = TaskState.STARTED;
831 this._taskFunction(
832 this,
833 this.should.bind(this));
834 }
835
836 // Update the task success based on the individual assertion/test inside.
837 update (subTask) {
838 // After one of tests fails within a task, the result is irreversible.
839 if (subTask.result === false) {
840 this._result = false;
841 this._failedAssertions++;
842 }
843
844 this._totalAssertions++;
845 }
846
847 // Finish the current task and start the next one if available.
848 done () {
849 this._state = TaskState.FINISHED;
850
851 let message = '< [' + this._label + '] ';
852
853 if (this._result) {
854 message += 'All assertion passed. (total ' + this._totalAssertions
855 + ' assertions)';
856 _logPassed(message);
857 } else {
858 message += this._failedAssertions + ' out of ' + this._totalAssertions
859 + ' assertions were failed.'
860 _logFailed(message);
861 }
862
863 this._taskRunner._runNextTask();
864 }
865
866 isPassed () {
867 return this._state === TaskState.FINISHED && this._result;
868 }
869
870 toString () {
871 return '"' + this._label + '": ' + this._description;
872 }
873
874 }
875
876
877 /**
878 * @class TaskRunner
879 * @description WebAudio testing task runner. Manages tasks.
880 */
881 class TaskRunner {
882
883 constructor () {
884 this._tasks = {};
885 this._taskSequence = [];
886 this._currentTaskIndex = -1;
887
888 // Configure testharness.js for the async operation.
889 setup (new Function (), {
890 explicit_done: true
891 });
892 }
893
894 _runNextTask () {
895 if (this._currentTaskIndex < this._taskSequence.length) {
896 this._tasks[this._taskSequence[this._currentTaskIndex++]].run();
897 } else {
898 this._finish();
899 }
900 }
901
902 _finish () {
903 let numberOfFailures = 0;
904 for (let taskIndex in this._taskSequence) {
905 let task = this._tasks[this._taskSequence[taskIndex]];
906 numberOfFailures += task.result ? 0 : 1;
907 }
908
909 let prefix = '# AUDIT TASK RUNNER FINISHED: ';
910 if (numberOfFailures > 0) {
911 _logFailed(prefix + numberOfFailures + ' out of '
912 + this._taskSequence.length + ' tasks were failed.');
913 } else {
914 _logPassed(prefix + this._taskSequence.length
915 + ' tasks ran successfully.');
916 }
917
918 // From testharness.js, report back to the test infrastructure that
919 // the task runner completed all the tasks.
920 done();
921 }
922
923 define (taskLabel, taskFunction) {
924 if (this._tasks.hasOwnProperty(taskLabel)) {
925 _throwException('Audit.define:: Duplicate task definition.');
926 return;
927 }
928
929 this._tasks[taskLabel] = new Task(this, taskLabel, taskFunction);
930 this._taskSequence.push(taskLabel);
931 }
932
933 // Start running all the tasks scheduled. Multiple task names can be passed
934 // to execute them sequentially. Zero argument will perform all defined
935 // tasks in the order of definition.
936 run () {
937 // Display the beginning of the test suite.
938 _logPassed('# AUDIT TASK RUNNER STARTED.');
939
940 // If the argument is specified, override the default task sequence with
941 // the specified one.
942 if (arguments.length > 0) {
943 this._taskSequence = [];
944 for (let i = 0; arguments.length; i++) {
945 let taskLabel = arguments[i];
946 if (!this._tasks.hasOwnProperty(taskLabel)) {
947 _throwException('Audit.run:: undefined task.');
948 } else if (this._taskSequence.includes(taskLabel)) {
949 _throwException('Audit.run:: duplicate task request.');
950 } else {
951 this._taskSequence.push[taskLabel];
952 }
953 }
954 }
955
956 if (this._taskSequence.length === 0) {
957 _throwException('Audit.run:: no task to run.');
958 return;
959 }
960
961 // Start the first task.
962 this._currentTaskIndex = 0;
963 this._runNextTask();
964 }
965
966 }
967
968
969 return {
970
971 /**
972 * Creates an instance of Audit task runner.
973 */
974 createTaskRunner: function () {
975 return new TaskRunner();
976 }
977
978 };
979
980 })();
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698