| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 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 /** | 6 /** |
| 7 * @fileOverview WebAudio layout test utility library. Built around W3C's | 7 * @fileOverview WebAudio layout test utility library. Built around W3C's |
| 8 * testharness.js. Includes asynchronous test task manager, | 8 * testharness.js. Includes asynchronous test task manager, |
| 9 * assertion utilities. | 9 * assertion utilities. |
| 10 * @dependency testharness.js | 10 * @dependency testharness.js |
| 11 */ | 11 */ |
| 12 | 12 |
| 13 | 13 |
| 14 (function () { | 14 (function() { |
| 15 | 15 |
| 16 'use strict'; | 16 'use strict'; |
| 17 | 17 |
| 18 // Selected methods from testharness.js. | 18 // Selected methods from testharness.js. |
| 19 let testharnessProperties = [ | 19 let testharnessProperties = [ |
| 20 'test', 'async_test', 'promise_test', 'promise_rejects', | 20 'test', 'async_test', 'promise_test', 'promise_rejects', 'generate_tests', |
| 21 'generate_tests', 'setup', 'done', 'assert_true', 'assert_false' | 21 'setup', 'done', 'assert_true', 'assert_false' |
| 22 ]; | 22 ]; |
| 23 | 23 |
| 24 // Check if testharness.js is properly loaded. Throw otherwise. | 24 // Check if testharness.js is properly loaded. Throw otherwise. |
| 25 for (let name in testharnessProperties) { | 25 for (let name in testharnessProperties) { |
| 26 if (!self.hasOwnProperty(testharnessProperties[name])) | 26 if (!self.hasOwnProperty(testharnessProperties[name])) |
| 27 throw new Error('Cannot proceed. testharness.js is not loaded.'); | 27 throw new Error('Cannot proceed. testharness.js is not loaded.'); |
| 28 } | 28 } |
| 29 })(); | 29 })(); |
| 30 | 30 |
| 31 | 31 |
| 32 window.Audit = (function () { | 32 window.Audit = (function() { |
| 33 | 33 |
| 34 'use strict'; | 34 'use strict'; |
| 35 | 35 |
| 36 // NOTE: Moving this method (or any other code above) will change the location | 36 // NOTE: Moving this method (or any other code above) will change the location |
| 37 // of 'CONSOLE ERROR...' message in the expected text files. | 37 // of 'CONSOLE ERROR...' message in the expected text files. |
| 38 function _logError (message) { | 38 function _logError(message) { |
| 39 console.error('[audit.js] ' + message); | 39 console.error('[audit.js] ' + message); |
| 40 } | 40 } |
| 41 | 41 |
| 42 function _logPassed (message) { | 42 function _logPassed(message) { |
| 43 test(function (arg) { | 43 test(function(arg) { |
| 44 assert_true(true); | 44 assert_true(true); |
| 45 }, message); | 45 }, message); |
| 46 } | 46 } |
| 47 | 47 |
| 48 function _logFailed (message, detail) { | 48 function _logFailed(message, detail) { |
| 49 test(function () { | 49 test(function() { |
| 50 assert_true(false, detail); | 50 assert_true(false, detail); |
| 51 }, message); | 51 }, message); |
| 52 } | 52 } |
| 53 | 53 |
| 54 function _throwException (message) { | 54 function _throwException(message) { |
| 55 throw new Error(message); | 55 throw new Error(message); |
| 56 } | 56 } |
| 57 | 57 |
| 58 // TODO(hongchan): remove this hack after confirming all the tests are | 58 // TODO(hongchan): remove this hack after confirming all the tests are |
| 59 // finished correctly. (crbug.com/708817) | 59 // finished correctly. (crbug.com/708817) |
| 60 const _testharnessDone = window.done; | 60 const _testharnessDone = window.done; |
| 61 window.done = () => { | 61 window.done = () => { |
| 62 _throwException('Do NOT call done() method from the test code.'); | 62 _throwException('Do NOT call done() method from the test code.'); |
| 63 }; | 63 }; |
| 64 | 64 |
| 65 // Generate a descriptive string from a target value in various types. | 65 // Generate a descriptive string from a target value in various types. |
| 66 function _generateDescription (target, options) { | 66 function _generateDescription(target, options) { |
| 67 let targetString; | 67 let targetString; |
| 68 | 68 |
| 69 switch (typeof target) { | 69 switch (typeof target) { |
| 70 case 'object': | 70 case 'object': |
| 71 // Handle Arrays. | 71 // Handle Arrays. |
| 72 if (target instanceof Array || target instanceof Float32Array || | 72 if (target instanceof Array || target instanceof Float32Array || |
| 73 target instanceof Float64Array || target instanceof Uint8Array) { | 73 target instanceof Float64Array || target instanceof Uint8Array) { |
| 74 let arrayElements = target.length < options.numberOfArrayElements | 74 let arrayElements = target.length < options.numberOfArrayElements ? |
| 75 ? String(target) | 75 String(target) : |
| 76 : String(target.slice(0, options.numberOfArrayElements)) + '...'; | 76 String(target.slice(0, options.numberOfArrayElements)) + '...'; |
| 77 targetString = '[' + arrayElements + ']'; | 77 targetString = '[' + arrayElements + ']'; |
| 78 } else { | 78 } else { |
| 79 targetString = '' + String(targetString).split(/[\s\]]/)[1]; | 79 targetString = '' + String(targetString).split(/[\s\]]/)[1]; |
| 80 } | 80 } |
| 81 break; | 81 break; |
| 82 default: | 82 default: |
| 83 targetString = String(target); | 83 targetString = String(target); |
| 84 break; | 84 break; |
| 85 } | 85 } |
| 86 | 86 |
| 87 return targetString; | 87 return targetString; |
| 88 } | 88 } |
| 89 | 89 |
| 90 // Return a string suitable for printing one failed element in | 90 // Return a string suitable for printing one failed element in |
| 91 // |beCloseToArray|. | 91 // |beCloseToArray|. |
| 92 function _formatFailureEntry(index, actual, expected, abserr, threshold) { | 92 function _formatFailureEntry(index, actual, expected, abserr, threshold) { |
| 93 return '\t[' + index + ']\t' | 93 return '\t[' + index + ']\t' + actual.toExponential(16) + '\t' + |
| 94 + actual.toExponential(16) + '\t' | 94 expected.toExponential(16) + '\t' + abserr.toExponential(16) + '\t' + |
| 95 + expected.toExponential(16) + '\t' | 95 (abserr / Math.abs(expected)).toExponential(16) + '\t' + |
| 96 + abserr.toExponential(16) + '\t' | 96 threshold.toExponential(16); |
| 97 + (abserr / Math.abs(expected)).toExponential(16) + '\t' | |
| 98 + threshold.toExponential(16); | |
| 99 } | 97 } |
| 100 | 98 |
| 101 // Compute the error threshold criterion for |beCloseToArray| | 99 // Compute the error threshold criterion for |beCloseToArray| |
| 102 function _closeToThreshold(abserr, relerr, expected) { | 100 function _closeToThreshold(abserr, relerr, expected) { |
| 103 return Math.max(abserr, relerr * Math.abs(expected)); | 101 return Math.max(abserr, relerr * Math.abs(expected)); |
| 104 } | 102 } |
| 105 | 103 |
| 106 /** | 104 /** |
| 107 * @class Should | 105 * @class Should |
| 108 * @description Assertion subtask for the Audit task. | 106 * @description Assertion subtask for the Audit task. |
| 109 * @param {Task} parentTask Associated Task object. | 107 * @param {Task} parentTask Associated Task object. |
| 110 * @param {Any} actual Target value to be tested. | 108 * @param {Any} actual Target value to be tested. |
| 111 * @param {String} actualDescription String description of the test target. | 109 * @param {String} actualDescription String description of the test target. |
| 112 */ | 110 */ |
| 113 class Should { | 111 class Should { |
| 114 | 112 constructor(parentTask, actual, actualDescription) { |
| 115 constructor (parentTask, actual, actualDescription) { | |
| 116 this._task = parentTask; | 113 this._task = parentTask; |
| 117 | 114 |
| 118 this._actual = actual; | 115 this._actual = actual; |
| 119 this._actualDescription = (actualDescription || null); | 116 this._actualDescription = (actualDescription || null); |
| 120 this._expected = null; | 117 this._expected = null; |
| 121 this._expectedDescription = null; | 118 this._expectedDescription = null; |
| 122 | 119 |
| 123 this._detail = ''; | 120 this._detail = ''; |
| 124 this._printActualForFailure = true; | 121 this._printActualForFailure = true; |
| 125 | 122 |
| 126 this._result = null; | 123 this._result = null; |
| 127 | 124 |
| 128 /** | 125 /** |
| 129 * @param {Number} numberOfErrors Number of errors to be printed. | 126 * @param {Number} numberOfErrors Number of errors to be printed. |
| 130 * @param {Number} numberOfArrayElements Number of array elements to be | 127 * @param {Number} numberOfArrayElements Number of array elements to be |
| 131 * printed in the test log. | 128 * printed in the test log. |
| 132 * @param {Boolean} verbose Verbose output from the assertion. | 129 * @param {Boolean} verbose Verbose output from the assertion. |
| 133 */ | 130 */ |
| 134 this._options = { | 131 this._options = { |
| 135 numberOfErrors: 4, | 132 numberOfErrors: 4, |
| 136 numberOfArrayElements: 16, | 133 numberOfArrayElements: 16, |
| 137 verbose: false | 134 verbose: false |
| 138 }; | 135 }; |
| 139 } | 136 } |
| 140 | 137 |
| 141 _processArguments (args) { | 138 _processArguments(args) { |
| 142 if (args.length === 0) | 139 if (args.length === 0) |
| 143 return; | 140 return; |
| 144 | 141 |
| 145 if (args.length > 0) | 142 if (args.length > 0) |
| 146 this._expected = args[0]; | 143 this._expected = args[0]; |
| 147 | 144 |
| 148 if (typeof args[1] === 'string') { | 145 if (typeof args[1] === 'string') { |
| 149 // case 1: (expected, description, options) | 146 // case 1: (expected, description, options) |
| 150 this._expectedDescription = args[1]; | 147 this._expectedDescription = args[1]; |
| 151 Object.assign(this._options, args[2]); | 148 Object.assign(this._options, args[2]); |
| 152 } else if (typeof args[1] === 'object') { | 149 } else if (typeof args[1] === 'object') { |
| 153 // case 2: (expected, options) | 150 // case 2: (expected, options) |
| 154 Object.assign(this._options, args[1]); | 151 Object.assign(this._options, args[1]); |
| 155 } | 152 } |
| 156 } | 153 } |
| 157 | 154 |
| 158 _buildResultText () { | 155 _buildResultText() { |
| 159 if (this._result === null) | 156 if (this._result === null) |
| 160 _throwException('Illegal invocation: the assertion is not finished.'); | 157 _throwException('Illegal invocation: the assertion is not finished.'); |
| 161 | 158 |
| 162 let actualString = _generateDescription(this._actual, this._options); | 159 let actualString = _generateDescription(this._actual, this._options); |
| 163 | 160 |
| 164 // Use generated text when the description is not provided. | 161 // Use generated text when the description is not provided. |
| 165 if (!this._actualDescription) | 162 if (!this._actualDescription) |
| 166 this._actualDescription = actualString; | 163 this._actualDescription = actualString; |
| 167 | 164 |
| 168 if (!this._expectedDescription) { | 165 if (!this._expectedDescription) { |
| 169 this._expectedDescription = | 166 this._expectedDescription = |
| 170 _generateDescription(this._expected, this._options); | 167 _generateDescription(this._expected, this._options); |
| 171 } | 168 } |
| 172 | 169 |
| 173 // For the assertion with a single operand. | 170 // For the assertion with a single operand. |
| 174 this._detail = this._detail.replace( | 171 this._detail = |
| 175 /\$\{actual\}/g, this._actualDescription); | 172 this._detail.replace(/\$\{actual\}/g, this._actualDescription); |
| 176 | 173 |
| 177 // If there is a second operand (i.e. expected value), we have to build | 174 // If there is a second operand (i.e. expected value), we have to build |
| 178 // the string for it as well. | 175 // the string for it as well. |
| 179 if (this._expected !== null) { | 176 if (this._expected !== null) { |
| 180 this._detail = this._detail.replace( | 177 this._detail = |
| 181 /\$\{expected\}/g, this._expectedDescription); | 178 this._detail.replace(/\$\{expected\}/g, this._expectedDescription); |
| 182 } | 179 } |
| 183 | 180 |
| 184 // If there is any property in |_options|, replace the property name | 181 // If there is any property in |_options|, replace the property name |
| 185 // with the value. | 182 // with the value. |
| 186 for (let name in this._options) { | 183 for (let name in this._options) { |
| 187 if (name === 'numberOfErrors' | 184 if (name === 'numberOfErrors' || name === 'numberOfArrayElements' || |
| 188 || name === 'numberOfArrayElements' | 185 name === 'verbose') { |
| 189 || name === 'verbose') { | |
| 190 continue; | 186 continue; |
| 191 } | 187 } |
| 192 | 188 |
| 193 // The RegExp key string contains special character. Take care of it. | 189 // The RegExp key string contains special character. Take care of it. |
| 194 let re = '\$\{' + name + '\}'; | 190 let re = '\$\{' + name + '\}'; |
| 195 re = re.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); | 191 re = re.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); |
| 196 this._detail = this._detail.replace(new RegExp(re, 'g'), | 192 this._detail = this._detail.replace( |
| 197 _generateDescription(this._options[name])); | 193 new RegExp(re, 'g'), _generateDescription(this._options[name])); |
| 198 } | 194 } |
| 199 | 195 |
| 200 // If the test failed, add the actual value at the end. | 196 // If the test failed, add the actual value at the end. |
| 201 if (this._result === false && this._printActualForFailure === true) { | 197 if (this._result === false && this._printActualForFailure === true) { |
| 202 this._detail += ' Got ' + actualString + '.'; | 198 this._detail += ' Got ' + actualString + '.'; |
| 203 } | 199 } |
| 204 } | 200 } |
| 205 | 201 |
| 206 _finalize () { | 202 _finalize() { |
| 207 if (this._result) { | 203 if (this._result) { |
| 208 _logPassed(' ' + this._detail); | 204 _logPassed(' ' + this._detail); |
| 209 } else { | 205 } else { |
| 210 _logFailed('X ' + this._detail); | 206 _logFailed('X ' + this._detail); |
| 211 } | 207 } |
| 212 | 208 |
| 213 // This assertion is finished, so update the parent task accordingly. | 209 // This assertion is finished, so update the parent task accordingly. |
| 214 this._task.update(this); | 210 this._task.update(this); |
| 215 | 211 |
| 216 // TODO(hongchan): configurable 'detail' message. | 212 // TODO(hongchan): configurable 'detail' message. |
| 217 } | 213 } |
| 218 | 214 |
| 219 _assert (condition, passDetail, failDetail) { | 215 _assert(condition, passDetail, failDetail) { |
| 220 this._result = Boolean(condition); | 216 this._result = Boolean(condition); |
| 221 this._detail = this._result ? passDetail : failDetail; | 217 this._detail = this._result ? passDetail : failDetail; |
| 222 this._buildResultText(); | 218 this._buildResultText(); |
| 223 this._finalize(); | 219 this._finalize(); |
| 224 | 220 |
| 225 return this._result; | 221 return this._result; |
| 226 } | 222 } |
| 227 | 223 |
| 228 get result () { | 224 get result() { |
| 229 return this._result; | 225 return this._result; |
| 230 } | 226 } |
| 231 | 227 |
| 232 get detail () { | 228 get detail() { |
| 233 return this._detail; | 229 return this._detail; |
| 234 } | 230 } |
| 235 | 231 |
| 236 /** | 232 /** |
| 237 * should() assertions. | 233 * should() assertions. |
| 238 * | 234 * |
| 239 * @example All the assertions can have 1, 2 or 3 arguments: | 235 * @example All the assertions can have 1, 2 or 3 arguments: |
| 240 * should().doAssert(expected); | 236 * should().doAssert(expected); |
| 241 * should().doAssert(expected, options); | 237 * should().doAssert(expected, options); |
| 242 * should().doAssert(expected, expectedDescription, options); | 238 * should().doAssert(expected, expectedDescription, options); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 254 */ | 250 */ |
| 255 | 251 |
| 256 /** | 252 /** |
| 257 * Check if |actual| exists. | 253 * Check if |actual| exists. |
| 258 * | 254 * |
| 259 * @example | 255 * @example |
| 260 * should({}, 'An empty object').exist(); | 256 * should({}, 'An empty object').exist(); |
| 261 * @result | 257 * @result |
| 262 * "PASS An empty object does exist." | 258 * "PASS An empty object does exist." |
| 263 */ | 259 */ |
| 264 exist () { | 260 exist() { |
| 265 return this._assert( | 261 return this._assert( |
| 266 this._actual !== null && this._actual !== undefined, | 262 this._actual !== null && this._actual !== undefined, |
| 267 '${actual} does exist.', | 263 '${actual} does exist.', '${actual} does not exist.'); |
| 268 '${actual} does not exist.'); | |
| 269 } | 264 } |
| 270 | 265 |
| 271 /** | 266 /** |
| 272 * Check if |actual| operation wrapped in a function throws an exception | 267 * Check if |actual| operation wrapped in a function throws an exception |
| 273 * with a expected error type correctly. |expected| is optional. | 268 * with a expected error type correctly. |expected| is optional. |
| 274 * | 269 * |
| 275 * @example | 270 * @example |
| 276 * should(() => { let a = b; }, 'A bad code').throw(); | 271 * should(() => { let a = b; }, 'A bad code').throw(); |
| 277 * should(() => { let c = d; }, 'Assigning d to c.') | 272 * should(() => { let c = d; }, 'Assigning d to c.') |
| 278 * .throw('ReferenceError'); | 273 * .throw('ReferenceError'); |
| 279 * should(() => { let e = f; }, 'Assigning e to f.') | 274 * should(() => { let e = f; }, 'Assigning e to f.') |
| 280 * .throw('ReferenceError', { omitErrorMessage: true }); | 275 * .throw('ReferenceError', { omitErrorMessage: true }); |
| 281 * | 276 * |
| 282 * @result | 277 * @result |
| 283 * "PASS A bad code threw an exception of ReferenceError: b is not | 278 * "PASS A bad code threw an exception of ReferenceError: b is not |
| 284 * defined." | 279 * defined." |
| 285 * "PASS Assigning d to c threw ReferenceError: d is not defined." | 280 * "PASS Assigning d to c threw ReferenceError: d is not defined." |
| 286 * "PASS Assigning e to f threw ReferenceError: [error message | 281 * "PASS Assigning e to f threw ReferenceError: [error message |
| 287 * omitted]." | 282 * omitted]." |
| 288 */ | 283 */ |
| 289 throw () { | 284 throw() { |
| 290 this._processArguments(arguments); | 285 this._processArguments(arguments); |
| 291 this._printActualForFailure = false; | 286 this._printActualForFailure = false; |
| 292 | 287 |
| 293 let didThrowCorrectly = false; | 288 let didThrowCorrectly = false; |
| 294 let passDetail, failDetail; | 289 let passDetail, failDetail; |
| 295 | 290 |
| 296 try { | 291 try { |
| 297 // This should throw. | 292 // This should throw. |
| 298 this._actual(); | 293 this._actual(); |
| 299 // Catch did not happen, so the test is failed. | 294 // Catch did not happen, so the test is failed. |
| 300 failDetail = '${actual} did not throw an exception.'; | 295 failDetail = '${actual} did not throw an exception.'; |
| 301 } catch (error) { | 296 } catch (error) { |
| 302 let errorMessage = this._options.omitErrorMessage | 297 let errorMessage = this._options.omitErrorMessage ? |
| 303 ? ': [error message omitted]' | 298 ': [error message omitted]' : |
| 304 : ': "' + error.message + '"'; | 299 ': "' + error.message + '"'; |
| 305 if (this._expected === null || this._expected === undefined) { | 300 if (this._expected === null || this._expected === undefined) { |
| 306 // The expected error type was not given. | 301 // The expected error type was not given. |
| 307 didThrowCorrectly = true; | 302 didThrowCorrectly = true; |
| 308 passDetail = '${actual} threw ' + error.name + errorMessage + '.'; | 303 passDetail = '${actual} threw ' + error.name + errorMessage + '.'; |
| 309 } else if (error.name === this._expected) { | 304 } else if (error.name === this._expected) { |
| 310 // The expected error type match the actual one. | 305 // The expected error type match the actual one. |
| 311 didThrowCorrectly = true; | 306 didThrowCorrectly = true; |
| 312 passDetail = '${actual} threw ${expected}' + errorMessage + '.'; | 307 passDetail = '${actual} threw ${expected}' + errorMessage + '.'; |
| 313 } else { | 308 } else { |
| 314 didThrowCorrectly = false; | 309 didThrowCorrectly = false; |
| 315 failDetail = '${actual} threw "' + error.name | 310 failDetail = |
| 316 + '" instead of ${expected}.'; | 311 '${actual} threw "' + error.name + '" instead of ${expected}.'; |
| 317 } | 312 } |
| 318 } | 313 } |
| 319 | 314 |
| 320 return this._assert(didThrowCorrectly, passDetail, failDetail); | 315 return this._assert(didThrowCorrectly, passDetail, failDetail); |
| 321 } | 316 } |
| 322 | 317 |
| 323 /** | 318 /** |
| 324 * Check if |actual| operation wrapped in a function does not throws an | 319 * Check if |actual| operation wrapped in a function does not throws an |
| 325 * exception correctly. | 320 * exception correctly. |
| 326 * | 321 * |
| 327 * @example | 322 * @example |
| 328 * should(() => { let foo = 'bar'; }, 'let foo = "bar"').notThrow(); | 323 * should(() => { let foo = 'bar'; }, 'let foo = "bar"').notThrow(); |
| 329 * | 324 * |
| 330 * @result | 325 * @result |
| 331 * "PASS let foo = "bar" did not throw an exception." | 326 * "PASS let foo = "bar" did not throw an exception." |
| 332 */ | 327 */ |
| 333 notThrow () { | 328 notThrow() { |
| 334 this._printActualForFailure = false; | 329 this._printActualForFailure = false; |
| 335 | 330 |
| 336 let didThrowCorrectly = false; | 331 let didThrowCorrectly = false; |
| 337 let passDetail, failDetail; | 332 let passDetail, failDetail; |
| 338 | 333 |
| 339 try { | 334 try { |
| 340 this._actual(); | 335 this._actual(); |
| 341 passDetail = '${actual} did not throw an exception.'; | 336 passDetail = '${actual} did not throw an exception.'; |
| 342 } catch (error) { | 337 } catch (error) { |
| 343 didThrowCorrectly = true; | 338 didThrowCorrectly = true; |
| 344 failDetail = '${actual} incorrectly threw ' + error.name + ': "' | 339 failDetail = '${actual} incorrectly threw ' + error.name + ': "' + |
| 345 + error.message + '".'; | 340 error.message + '".'; |
| 346 } | 341 } |
| 347 | 342 |
| 348 return this._assert(!didThrowCorrectly, passDetail, failDetail); | 343 return this._assert(!didThrowCorrectly, passDetail, failDetail); |
| 349 } | 344 } |
| 350 | 345 |
| 351 /** | 346 /** |
| 352 * Check if |actual| promise is resolved correctly. Note that the returned | 347 * Check if |actual| promise is resolved correctly. Note that the returned |
| 353 * result from promise object will be passed to the following then() | 348 * result from promise object will be passed to the following then() |
| 354 * function. | 349 * function. |
| 355 * | 350 * |
| 356 * @example | 351 * @example |
| 357 * should('My promise', promise).beResolve().then((result) => { | 352 * should('My promise', promise).beResolve().then((result) => { |
| 358 * console.log(result); | 353 * console.log(result); |
| 359 * }); | 354 * }); |
| 360 * | 355 * |
| 361 * @result | 356 * @result |
| 362 * "PASS My promise resolved correctly." | 357 * "PASS My promise resolved correctly." |
| 363 * "FAIL X My promise rejected *INCORRECTLY* with _ERROR_." | 358 * "FAIL X My promise rejected *INCORRECTLY* with _ERROR_." |
| 364 */ | 359 */ |
| 365 beResolved () { | 360 beResolved() { |
| 366 return this._actual.then(function (result) { | 361 return this._actual.then( |
| 367 this._assert(true, '${actual} resolved correctly.', null); | 362 function(result) { |
| 368 return result; | 363 this._assert(true, '${actual} resolved correctly.', null); |
| 369 }.bind(this), function (error) { | 364 return result; |
| 370 this._assert(false, null, | 365 }.bind(this), |
| 371 '${actual} rejected incorrectly with ' + error + '.'); | 366 function(error) { |
| 372 }.bind(this)); | 367 this._assert( |
| 368 false, null, |
| 369 '${actual} rejected incorrectly with ' + error + '.'); |
| 370 }.bind(this)); |
| 373 } | 371 } |
| 374 | 372 |
| 375 /** | 373 /** |
| 376 * Check if |actual| promise is rejected correctly. | 374 * Check if |actual| promise is rejected correctly. |
| 377 * | 375 * |
| 378 * @example | 376 * @example |
| 379 * should('My promise', promise).beRejected().then(nextStuff); | 377 * should('My promise', promise).beRejected().then(nextStuff); |
| 380 * | 378 * |
| 381 * @result | 379 * @result |
| 382 * "PASS My promise rejected correctly (with _ERROR_)." | 380 * "PASS My promise rejected correctly (with _ERROR_)." |
| 383 * "FAIL X My promise resolved *INCORRECTLY*." | 381 * "FAIL X My promise resolved *INCORRECTLY*." |
| 384 */ | 382 */ |
| 385 beRejected () { | 383 beRejected() { |
| 386 return this._actual.then(function () { | 384 return this._actual.then( |
| 387 this._assert(false, null, '${actual} resolved incorrectly.'); | 385 function() { |
| 388 }.bind(this), function (error) { | 386 this._assert(false, null, '${actual} resolved incorrectly.'); |
| 389 this._assert(true, | 387 }.bind(this), |
| 390 '${actual} rejected correctly with ' + error + '.', null); | 388 function(error) { |
| 391 }.bind(this)); | 389 this._assert( |
| 390 true, '${actual} rejected correctly with ' + error + '.', null); |
| 391 }.bind(this)); |
| 392 } | 392 } |
| 393 | 393 |
| 394 /** | 394 /** |
| 395 * Check if |actual| promise is rejected correctly. | 395 * Check if |actual| promise is rejected correctly. |
| 396 * | 396 * |
| 397 * @example | 397 * @example |
| 398 * should(promise, 'My promise').beRejectedWith('_ERROR_').then(); | 398 * should(promise, 'My promise').beRejectedWith('_ERROR_').then(); |
| 399 * | 399 * |
| 400 * @result | 400 * @result |
| 401 * "PASS My promise rejected correctly with _ERROR_." | 401 * "PASS My promise rejected correctly with _ERROR_." |
| 402 * "FAIL X My promise rejected correctly but got _ACTUAL_ERROR instead of | 402 * "FAIL X My promise rejected correctly but got _ACTUAL_ERROR instead of |
| 403 * _EXPECTED_ERROR_." | 403 * _EXPECTED_ERROR_." |
| 404 * "FAIL X My promise resolved incorrectly." | 404 * "FAIL X My promise resolved incorrectly." |
| 405 */ | 405 */ |
| 406 beRejectedWith() { | 406 beRejectedWith() { |
| 407 this._processArguments(arguments); | 407 this._processArguments(arguments); |
| 408 | 408 |
| 409 return this._actual.then( | 409 return this._actual.then( |
| 410 function() { | 410 function() { |
| 411 this._assert(false, null, '${actual} resolved incorrectly.'); | 411 this._assert(false, null, '${actual} resolved incorrectly.'); |
| 412 }.bind(this), | 412 }.bind(this), |
| 413 function(error) { | 413 function(error) { |
| 414 if (this._expected !== error.name) { | 414 if (this._expected !== error.name) { |
| 415 this._assert( | 415 this._assert( |
| 416 false, null, '${actual} rejected correctly but got ' + | 416 false, null, |
| 417 error.name + ' instead of ' + this._expected + '.'); | 417 '${actual} rejected correctly but got ' + error.name + |
| 418 ' instead of ' + this._expected + '.'); |
| 418 } else { | 419 } else { |
| 419 this._assert( | 420 this._assert( |
| 420 true, | 421 true, |
| 421 '${actual} rejected correctly with ' + this._expected + '.', | 422 '${actual} rejected correctly with ' + this._expected + '.', |
| 422 null); | 423 null); |
| 423 } | 424 } |
| 424 }.bind(this)); | 425 }.bind(this)); |
| 425 } | 426 } |
| 426 | 427 |
| 427 /** | 428 /** |
| 428 * Check if |actual| is a boolean true. | 429 * Check if |actual| is a boolean true. |
| 429 * | 430 * |
| 430 * @example | 431 * @example |
| 431 * should(3 < 5, '3 < 5').beTrue(); | 432 * should(3 < 5, '3 < 5').beTrue(); |
| 432 * | 433 * |
| 433 * @result | 434 * @result |
| 434 * "PASS 3 < 5 is true." | 435 * "PASS 3 < 5 is true." |
| 435 */ | 436 */ |
| 436 beTrue () { | 437 beTrue() { |
| 437 return this._assert( | 438 return this._assert( |
| 438 this._actual === true, | 439 this._actual === true, '${actual} is true.', |
| 439 '${actual} is true.', | |
| 440 '${actual} is not true.'); | 440 '${actual} is not true.'); |
| 441 } | 441 } |
| 442 | 442 |
| 443 /** | 443 /** |
| 444 * Check if |actual| is a boolean false. | 444 * Check if |actual| is a boolean false. |
| 445 * | 445 * |
| 446 * @example | 446 * @example |
| 447 * should(3 > 5, '3 > 5').beFalse(); | 447 * should(3 > 5, '3 > 5').beFalse(); |
| 448 * | 448 * |
| 449 * @result | 449 * @result |
| 450 * "PASS 3 > 5 is false." | 450 * "PASS 3 > 5 is false." |
| 451 */ | 451 */ |
| 452 beFalse () { | 452 beFalse() { |
| 453 return this._assert( | 453 return this._assert( |
| 454 this._actual === false, | 454 this._actual === false, '${actual} is false.', |
| 455 '${actual} is false.', | |
| 456 '${actual} is not false.'); | 455 '${actual} is not false.'); |
| 457 } | 456 } |
| 458 | 457 |
| 459 /** | 458 /** |
| 460 * Check if |actual| is strictly equal to |expected|. (no type coercion) | 459 * Check if |actual| is strictly equal to |expected|. (no type coercion) |
| 461 * | 460 * |
| 462 * @example | 461 * @example |
| 463 * should(1).beEqualTo(1); | 462 * should(1).beEqualTo(1); |
| 464 * | 463 * |
| 465 * @result | 464 * @result |
| 466 * "PASS 1 is equal to 1." | 465 * "PASS 1 is equal to 1." |
| 467 */ | 466 */ |
| 468 beEqualTo () { | 467 beEqualTo() { |
| 469 this._processArguments(arguments); | 468 this._processArguments(arguments); |
| 470 return this._assert( | 469 return this._assert( |
| 471 this._actual === this._expected, | 470 this._actual === this._expected, '${actual} is equal to ${expected}.', |
| 472 '${actual} is equal to ${expected}.', | |
| 473 '${actual} is not equal to ${expected}.'); | 471 '${actual} is not equal to ${expected}.'); |
| 474 } | 472 } |
| 475 | 473 |
| 476 /** | 474 /** |
| 477 * Check if |actual| is not equal to |expected|. | 475 * Check if |actual| is not equal to |expected|. |
| 478 * | 476 * |
| 479 * @example | 477 * @example |
| 480 * should(1).notBeEqualTo(2); | 478 * should(1).notBeEqualTo(2); |
| 481 * | 479 * |
| 482 * @result | 480 * @result |
| 483 * "PASS 1 is not equal to 2." | 481 * "PASS 1 is not equal to 2." |
| 484 */ | 482 */ |
| 485 notBeEqualTo () { | 483 notBeEqualTo() { |
| 486 this._processArguments(arguments); | 484 this._processArguments(arguments); |
| 487 return this._assert( | 485 return this._assert( |
| 488 this._actual !== this._expected, | 486 this._actual !== this._expected, |
| 489 '${actual} is not equal to ${expected}.', | 487 '${actual} is not equal to ${expected}.', |
| 490 '${actual} should not be equal to ${expected}.'); | 488 '${actual} should not be equal to ${expected}.'); |
| 491 } | 489 } |
| 492 | 490 |
| 493 /** | 491 /** |
| 494 * Check if |actual| is greater than |expected|. | 492 * Check if |actual| is greater than |expected|. |
| 495 * | 493 * |
| 496 * @example | 494 * @example |
| 497 * should(2).beGreaterThanOrEqualTo(2); | 495 * should(2).beGreaterThanOrEqualTo(2); |
| 498 * | 496 * |
| 499 * @result | 497 * @result |
| 500 * "PASS 2 is greater than or equal to 2." | 498 * "PASS 2 is greater than or equal to 2." |
| 501 */ | 499 */ |
| 502 beGreaterThan () { | 500 beGreaterThan() { |
| 503 this._processArguments(arguments); | 501 this._processArguments(arguments); |
| 504 return this._assert( | 502 return this._assert( |
| 505 this._actual > this._expected, | 503 this._actual > this._expected, |
| 506 '${actual} is greater than ${expected}.', | 504 '${actual} is greater than ${expected}.', |
| 507 '${actual} is not greater than ${expected}.' | 505 '${actual} is not greater than ${expected}.'); |
| 508 ); | |
| 509 } | 506 } |
| 510 | 507 |
| 511 /** | 508 /** |
| 512 * Check if |actual| is greater than or equal to |expected|. | 509 * Check if |actual| is greater than or equal to |expected|. |
| 513 * | 510 * |
| 514 * @example | 511 * @example |
| 515 * should(2).beGreaterThan(1); | 512 * should(2).beGreaterThan(1); |
| 516 * | 513 * |
| 517 * @result | 514 * @result |
| 518 * "PASS 2 is greater than 1." | 515 * "PASS 2 is greater than 1." |
| 519 */ | 516 */ |
| 520 beGreaterThanOrEqualTo () { | 517 beGreaterThanOrEqualTo() { |
| 521 this._processArguments(arguments); | 518 this._processArguments(arguments); |
| 522 return this._assert( | 519 return this._assert( |
| 523 this._actual >= this._expected, | 520 this._actual >= this._expected, |
| 524 '${actual} is greater than or equal to ${expected}.', | 521 '${actual} is greater than or equal to ${expected}.', |
| 525 '${actual} is not greater than or equal to ${expected}.' | 522 '${actual} is not greater than or equal to ${expected}.'); |
| 526 ); | |
| 527 } | 523 } |
| 528 | 524 |
| 529 /** | 525 /** |
| 530 * Check if |actual| is less than |expected|. | 526 * Check if |actual| is less than |expected|. |
| 531 * | 527 * |
| 532 * @example | 528 * @example |
| 533 * should(1).beLessThan(2); | 529 * should(1).beLessThan(2); |
| 534 * | 530 * |
| 535 * @result | 531 * @result |
| 536 * "PASS 1 is less than 2." | 532 * "PASS 1 is less than 2." |
| 537 */ | 533 */ |
| 538 beLessThan () { | 534 beLessThan() { |
| 539 this._processArguments(arguments); | 535 this._processArguments(arguments); |
| 540 return this._assert( | 536 return this._assert( |
| 541 this._actual < this._expected, | 537 this._actual < this._expected, '${actual} is less than ${expected}.', |
| 542 '${actual} is less than ${expected}.', | 538 '${actual} is not less than ${expected}.'); |
| 543 '${actual} is not less than ${expected}.' | |
| 544 ); | |
| 545 } | 539 } |
| 546 | 540 |
| 547 /** | 541 /** |
| 548 * Check if |actual| is less than or equal to |expected|. | 542 * Check if |actual| is less than or equal to |expected|. |
| 549 * | 543 * |
| 550 * @example | 544 * @example |
| 551 * should(1).beLessThanOrEqualTo(1); | 545 * should(1).beLessThanOrEqualTo(1); |
| 552 * | 546 * |
| 553 * @result | 547 * @result |
| 554 * "PASS 1 is less than or equal to 1." | 548 * "PASS 1 is less than or equal to 1." |
| 555 */ | 549 */ |
| 556 beLessThanOrEqualTo () { | 550 beLessThanOrEqualTo() { |
| 557 this._processArguments(arguments); | 551 this._processArguments(arguments); |
| 558 return this._assert( | 552 return this._assert( |
| 559 this._actual <= this._expected, | 553 this._actual <= this._expected, |
| 560 '${actual} is less than or equal to ${expected}.', | 554 '${actual} is less than or equal to ${expected}.', |
| 561 '${actual} is not less than or equal to ${expected}.' | 555 '${actual} is not less than or equal to ${expected}.'); |
| 562 ); | |
| 563 } | 556 } |
| 564 | 557 |
| 565 /** | 558 /** |
| 566 * Check if |actual| array is filled with a constant |expected| value. | 559 * Check if |actual| array is filled with a constant |expected| value. |
| 567 * | 560 * |
| 568 * @example | 561 * @example |
| 569 * should([1, 1, 1]).beConstantValueOf(1); | 562 * should([1, 1, 1]).beConstantValueOf(1); |
| 570 * | 563 * |
| 571 * @result | 564 * @result |
| 572 * "PASS [1,1,1] contains only the constant 1." | 565 * "PASS [1,1,1] contains only the constant 1." |
| 573 */ | 566 */ |
| 574 beConstantValueOf () { | 567 beConstantValueOf() { |
| 575 this._processArguments(arguments); | 568 this._processArguments(arguments); |
| 576 this._printActualForFailure = false; | 569 this._printActualForFailure = false; |
| 577 | 570 |
| 578 let passed = true; | 571 let passed = true; |
| 579 let passDetail, failDetail; | 572 let passDetail, failDetail; |
| 580 let errors = {}; | 573 let errors = {}; |
| 581 | 574 |
| 582 let actual = this._actual; | 575 let actual = this._actual; |
| 583 let expected = this._expected; | 576 let expected = this._expected; |
| 584 for (let index = 0; index < actual.length; ++index) { | 577 for (let index = 0; index < actual.length; ++index) { |
| 585 if (actual[index] !== expected) | 578 if (actual[index] !== expected) |
| 586 errors[index] = actual[index]; | 579 errors[index] = actual[index]; |
| 587 } | 580 } |
| 588 | 581 |
| 589 let numberOfErrors = Object.keys(errors).length; | 582 let numberOfErrors = Object.keys(errors).length; |
| 590 passed = numberOfErrors === 0; | 583 passed = numberOfErrors === 0; |
| 591 | 584 |
| 592 if (passed) { | 585 if (passed) { |
| 593 passDetail = '${actual} contains only the constant ${expected}.'; | 586 passDetail = '${actual} contains only the constant ${expected}.'; |
| 594 } else { | 587 } else { |
| 595 let counter = 0; | 588 let counter = 0; |
| 596 failDetail = 'Expected ${expected} for all values but found ' | 589 failDetail = 'Expected ${expected} for all values but found ' + |
| 597 + numberOfErrors + ' unexpected values: '; | 590 numberOfErrors + ' unexpected values: '; |
| 598 failDetail += '\n\tIndex\tActual'; | 591 failDetail += '\n\tIndex\tActual'; |
| 599 for (let errorIndex in errors) { | 592 for (let errorIndex in errors) { |
| 600 failDetail += '\n\t[' + errorIndex + ']' | 593 failDetail += '\n\t[' + errorIndex + ']' + |
| 601 + '\t' + errors[errorIndex]; | 594 '\t' + errors[errorIndex]; |
| 602 if (++counter >= this._options.numberOfErrors) { | 595 if (++counter >= this._options.numberOfErrors) { |
| 603 failDetail += '\n\t...and ' + (numberOfErrors - counter) | 596 failDetail += |
| 604 + ' more errors.'; | 597 '\n\t...and ' + (numberOfErrors - counter) + ' more errors.'; |
| 605 break; | 598 break; |
| 606 } | 599 } |
| 607 } | 600 } |
| 608 } | 601 } |
| 609 | 602 |
| 610 return this._assert(passed, passDetail, failDetail); | 603 return this._assert(passed, passDetail, failDetail); |
| 611 } | 604 } |
| 612 | 605 |
| 613 /** | 606 /** |
| 614 * Check if |actual| array is not filled with a constant |expected| value. | 607 * Check if |actual| array is not filled with a constant |expected| value. |
| 615 * | 608 * |
| 616 * @example | 609 * @example |
| 617 * should([1, 0, 1]).notBeConstantValueOf(1); | 610 * should([1, 0, 1]).notBeConstantValueOf(1); |
| 618 * should([0, 0, 0]).notBeConstantValueOf(0); | 611 * should([0, 0, 0]).notBeConstantValueOf(0); |
| 619 * | 612 * |
| 620 * @result | 613 * @result |
| 621 * "PASS [1,0,1] is not constantly 1 (contains 1 different value)." | 614 * "PASS [1,0,1] is not constantly 1 (contains 1 different value)." |
| 622 * "FAIL X [0,0,0] should have contain at least one value different | 615 * "FAIL X [0,0,0] should have contain at least one value different |
| 623 * from 0." | 616 * from 0." |
| 624 */ | 617 */ |
| 625 notBeConstantValueOf () { | 618 notBeConstantValueOf() { |
| 626 this._processArguments(arguments); | 619 this._processArguments(arguments); |
| 627 this._printActualForFailure = false; | 620 this._printActualForFailure = false; |
| 628 | 621 |
| 629 let passed = true; | 622 let passed = true; |
| 630 let passDetail; | 623 let passDetail; |
| 631 let failDetail; | 624 let failDetail; |
| 632 let differences = {}; | 625 let differences = {}; |
| 633 | 626 |
| 634 let actual = this._actual; | 627 let actual = this._actual; |
| 635 let expected = this._expected; | 628 let expected = this._expected; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 655 | 648 |
| 656 /** | 649 /** |
| 657 * Check if |actual| array is identical to |expected| array element-wise. | 650 * Check if |actual| array is identical to |expected| array element-wise. |
| 658 * | 651 * |
| 659 * @example | 652 * @example |
| 660 * should([1, 2, 3]).beEqualToArray([1, 2, 3]); | 653 * should([1, 2, 3]).beEqualToArray([1, 2, 3]); |
| 661 * | 654 * |
| 662 * @result | 655 * @result |
| 663 * "[1,2,3] is identical to the array [1,2,3]." | 656 * "[1,2,3] is identical to the array [1,2,3]." |
| 664 */ | 657 */ |
| 665 beEqualToArray () { | 658 beEqualToArray() { |
| 666 this._processArguments(arguments); | 659 this._processArguments(arguments); |
| 667 this._printActualForFailure = false; | 660 this._printActualForFailure = false; |
| 668 | 661 |
| 669 let passed = true; | 662 let passed = true; |
| 670 let passDetail, failDetail; | 663 let passDetail, failDetail; |
| 671 let errorIndices = []; | 664 let errorIndices = []; |
| 672 | 665 |
| 673 if (this._actual.length !== this._expected.length) { | 666 if (this._actual.length !== this._expected.length) { |
| 674 passed = false; | 667 passed = false; |
| 675 failDetail = 'The array length does not match.'; | 668 failDetail = 'The array length does not match.'; |
| 676 return this._assert(passed, passDetail, failDetail); | 669 return this._assert(passed, passDetail, failDetail); |
| 677 } | 670 } |
| 678 | 671 |
| 679 let actual = this._actual; | 672 let actual = this._actual; |
| 680 let expected = this._expected; | 673 let expected = this._expected; |
| 681 for (let index = 0; index < actual.length; ++index) { | 674 for (let index = 0; index < actual.length; ++index) { |
| 682 if (actual[index] !== expected[index]) | 675 if (actual[index] !== expected[index]) |
| 683 errorIndices.push(index); | 676 errorIndices.push(index); |
| 684 } | 677 } |
| 685 | 678 |
| 686 passed = errorIndices.length === 0; | 679 passed = errorIndices.length === 0; |
| 687 | 680 |
| 688 if (passed) { | 681 if (passed) { |
| 689 passDetail = '${actual} is identical to the array ${expected}.'; | 682 passDetail = '${actual} is identical to the array ${expected}.'; |
| 690 } else { | 683 } else { |
| 691 let counter = 0; | 684 let counter = 0; |
| 692 failDetail = '${actual} expected to be equal to the array ${expected} ' | 685 failDetail = |
| 693 + 'but differs in ' + errorIndices.length + ' places:' | 686 '${actual} expected to be equal to the array ${expected} ' + |
| 694 + '\n\tIndex\tActual\t\t\tExpected'; | 687 'but differs in ' + errorIndices.length + ' places:' + |
| 688 '\n\tIndex\tActual\t\t\tExpected'; |
| 695 for (let index of errorIndices) { | 689 for (let index of errorIndices) { |
| 696 failDetail += '\n\t[' + index + ']' | 690 failDetail += '\n\t[' + index + ']' + |
| 697 + '\t' + this._actual[index].toExponential(16) | 691 '\t' + this._actual[index].toExponential(16) + '\t' + |
| 698 + '\t' + this._expected[index].toExponential(16); | 692 this._expected[index].toExponential(16); |
| 699 if (++counter >= this._options.numberOfErrors) { | 693 if (++counter >= this._options.numberOfErrors) { |
| 700 failDetail += '\n\t...and ' + (errorIndices.length - counter) | 694 failDetail += '\n\t...and ' + (errorIndices.length - counter) + |
| 701 + ' more errors.'; | 695 ' more errors.'; |
| 702 break; | 696 break; |
| 703 } | 697 } |
| 704 } | 698 } |
| 705 } | 699 } |
| 706 | 700 |
| 707 return this._assert(passed, passDetail, failDetail); | 701 return this._assert(passed, passDetail, failDetail); |
| 708 } | 702 } |
| 709 | 703 |
| 710 /** | 704 /** |
| 711 * Check if |actual| array contains only the values in |expected| in the | 705 * Check if |actual| array contains only the values in |expected| in the |
| 712 * order of values in |expected|. | 706 * order of values in |expected|. |
| 713 * | 707 * |
| 714 * @example | 708 * @example |
| 715 * Should([1, 1, 3, 3, 2], 'My random array').containValues([1, 3, 2]); | 709 * Should([1, 1, 3, 3, 2], 'My random array').containValues([1, 3, 2]); |
| 716 * | 710 * |
| 717 * @result | 711 * @result |
| 718 * "PASS [1,1,3,3,2] contains all the expected values in the correct | 712 * "PASS [1,1,3,3,2] contains all the expected values in the correct |
| 719 * order: [1,3,2]. | 713 * order: [1,3,2]. |
| 720 */ | 714 */ |
| 721 containValues () { | 715 containValues() { |
| 722 this._processArguments(arguments); | 716 this._processArguments(arguments); |
| 723 this._printActualForFailure = false; | 717 this._printActualForFailure = false; |
| 724 | 718 |
| 725 let passed = true; | 719 let passed = true; |
| 726 let indexedActual = []; | 720 let indexedActual = []; |
| 727 let firstErrorIndex = null; | 721 let firstErrorIndex = null; |
| 728 | 722 |
| 729 // Collect the unique value sequence from the actual. | 723 // Collect the unique value sequence from the actual. |
| 730 for (let i = 0, prev = null; i < this._actual.length; i++) { | 724 for (let i = 0, prev = null; i < this._actual.length; i++) { |
| 731 if (this._actual[i] !== prev) { | 725 if (this._actual[i] !== prev) { |
| 732 indexedActual.push({ | 726 indexedActual.push({index: i, value: this._actual[i]}); |
| 733 index: i, | |
| 734 value: this._actual[i] | |
| 735 }); | |
| 736 prev = this._actual[i]; | 727 prev = this._actual[i]; |
| 737 } | 728 } |
| 738 } | 729 } |
| 739 | 730 |
| 740 // Compare against the expected sequence. | 731 // Compare against the expected sequence. |
| 741 for (let j = 0; j < this._expected.length; j++) { | 732 for (let j = 0; j < this._expected.length; j++) { |
| 742 if (this._expected[j] !== indexedActual[j].value) { | 733 if (this._expected[j] !== indexedActual[j].value) { |
| 743 firstErrorIndex = indexedActual[j].index; | 734 firstErrorIndex = indexedActual[j].index; |
| 744 passed = false; | 735 passed = false; |
| 745 break; | 736 break; |
| 746 } | 737 } |
| 747 } | 738 } |
| 748 | 739 |
| 749 return this._assert( | 740 return this._assert( |
| 750 passed, | 741 passed, |
| 751 '${actual} contains all the expected values in the correct order: ' | 742 '${actual} contains all the expected values in the correct order: ' + |
| 752 + '${expected}.', | 743 '${expected}.', |
| 753 '${actual} expected to have the value sequence of ${expected} but ' | 744 '${actual} expected to have the value sequence of ${expected} but ' + |
| 754 + 'got ' + this._actual[firstErrorIndex] + ' at index ' | 745 'got ' + this._actual[firstErrorIndex] + ' at index ' + |
| 755 + firstErrorIndex + '.'); | 746 firstErrorIndex + '.'); |
| 756 } | 747 } |
| 757 | 748 |
| 758 /** | 749 /** |
| 759 * Check if |actual| array does not have any glitches. Note that |threshold| | 750 * Check if |actual| array does not have any glitches. Note that |threshold| |
| 760 * is not optional and is to define the desired threshold value. | 751 * is not optional and is to define the desired threshold value. |
| 761 * | 752 * |
| 762 * @example | 753 * @example |
| 763 * should([0.5, 0.5, 0.55, 0.5, 0.45, 0.5]).notGlitch(0.06); | 754 * should([0.5, 0.5, 0.55, 0.5, 0.45, 0.5]).notGlitch(0.06); |
| 764 * | 755 * |
| 765 * @result | 756 * @result |
| 766 * "PASS [0.5,0.5,0.55,0.5,0.45,0.5] has no glitch above the threshold | 757 * "PASS [0.5,0.5,0.55,0.5,0.45,0.5] has no glitch above the threshold |
| 767 * of 0.06." | 758 * of 0.06." |
| 768 * | 759 * |
| 769 */ | 760 */ |
| 770 notGlitch () { | 761 notGlitch() { |
| 771 this._processArguments(arguments); | 762 this._processArguments(arguments); |
| 772 this._printActualForFailure = false; | 763 this._printActualForFailure = false; |
| 773 | 764 |
| 774 let passed = true; | 765 let passed = true; |
| 775 let passDetail, failDetail; | 766 let passDetail, failDetail; |
| 776 | 767 |
| 777 let actual = this._actual; | 768 let actual = this._actual; |
| 778 let expected = this._expected; | 769 let expected = this._expected; |
| 779 for (let index = 0; index < actual.length; ++index) { | 770 for (let index = 0; index < actual.length; ++index) { |
| 780 let diff = Math.abs(actual[index - 1] - actual[index]); | 771 let diff = Math.abs(actual[index - 1] - actual[index]); |
| 781 if (diff >= expected) { | 772 if (diff >= expected) { |
| 782 passed = false; | 773 passed = false; |
| 783 failDetail = '${actual} has a glitch at index ' + index + ' of size ' | 774 failDetail = '${actual} has a glitch at index ' + index + |
| 784 + diff + '.'; | 775 ' of size ' + diff + '.'; |
| 785 } | 776 } |
| 786 } | 777 } |
| 787 | 778 |
| 788 passDetail = | 779 passDetail = |
| 789 '${actual} has no glitch above the threshold of ${expected}.'; | 780 '${actual} has no glitch above the threshold of ${expected}.'; |
| 790 | 781 |
| 791 return this._assert(passed, passDetail, failDetail); | 782 return this._assert(passed, passDetail, failDetail); |
| 792 } | 783 } |
| 793 | 784 |
| 794 /** | 785 /** |
| 795 * Check if |actual| is close to |expected| using the given relative error | 786 * Check if |actual| is close to |expected| using the given relative error |
| 796 * |threshold|. | 787 * |threshold|. |
| 797 * | 788 * |
| 798 * @example | 789 * @example |
| 799 * should(2.3).beCloseTo(2, { threshold: 0.3 }); | 790 * should(2.3).beCloseTo(2, { threshold: 0.3 }); |
| 800 * | 791 * |
| 801 * @result | 792 * @result |
| 802 * "PASS 2.3 is 2 within an error of 0.3." | 793 * "PASS 2.3 is 2 within an error of 0.3." |
| 803 * @param {Object} options Options for assertion. | 794 * @param {Object} options Options for assertion. |
| 804 * @param {Number} options.threshold Threshold value for the comparison. | 795 * @param {Number} options.threshold Threshold value for the comparison. |
| 805 */ | 796 */ |
| 806 beCloseTo () { | 797 beCloseTo() { |
| 807 this._processArguments(arguments); | 798 this._processArguments(arguments); |
| 808 | 799 |
| 809 // The threshold is relative except when |expected| is zero, in which case | 800 // The threshold is relative except when |expected| is zero, in which case |
| 810 // it is absolute. | 801 // it is absolute. |
| 811 let absExpected = this._expected ? Math.abs(this._expected) : 1; | 802 let absExpected = this._expected ? Math.abs(this._expected) : 1; |
| 812 let error = Math.abs(this._actual - this._expected) / absExpected; | 803 let error = Math.abs(this._actual - this._expected) / absExpected; |
| 813 | 804 |
| 814 // debugger; | 805 // debugger; |
| 815 | 806 |
| 816 return this._assert( | 807 return this._assert( |
| 817 error <= this._options.threshold, | 808 error <= this._options.threshold, |
| 818 '${actual} is ${expected} within an error of ${threshold}.', | 809 '${actual} is ${expected} within an error of ${threshold}.', |
| 819 '${actual} is not close to ${expected} within a relative error of ' + | 810 '${actual} is not close to ${expected} within a relative error of ' + |
| 820 '${threshold} (RelErr=' + error + ').'); | 811 '${threshold} (RelErr=' + error + ').'); |
| 821 } | 812 } |
| 822 | 813 |
| 823 /** | 814 /** |
| 824 * Check if |target| array is close to |expected| array element-wise within | 815 * Check if |target| array is close to |expected| array element-wise within |
| 825 * a certain error bound given by the |options|. | 816 * a certain error bound given by the |options|. |
| 826 * | 817 * |
| 827 * The error criterion is: | 818 * The error criterion is: |
| 828 * abs(actual[k] - expected[k]) < max(absErr, relErr * abs(expected)) | 819 * abs(actual[k] - expected[k]) < max(absErr, relErr * abs(expected)) |
| 829 * | 820 * |
| 830 * If nothing is given for |options|, then absErr = relErr = 0. If | 821 * If nothing is given for |options|, then absErr = relErr = 0. If |
| 831 * absErr = 0, then the error criterion is a relative error. A non-zero | 822 * absErr = 0, then the error criterion is a relative error. A non-zero |
| 832 * absErr value produces a mix intended to handle the case where the | 823 * absErr value produces a mix intended to handle the case where the |
| 833 * expected value is 0, allowing the target value to differ by absErr from | 824 * expected value is 0, allowing the target value to differ by absErr from |
| 834 * the expected. | 825 * the expected. |
| 835 * | 826 * |
| 836 * @param {Number} options.absoluteThreshold Absolute threshold. | 827 * @param {Number} options.absoluteThreshold Absolute threshold. |
| 837 * @param {Number} options.relativeThreshold Relative threshold. | 828 * @param {Number} options.relativeThreshold Relative threshold. |
| 838 */ | 829 */ |
| 839 beCloseToArray () { | 830 beCloseToArray() { |
| 840 this._processArguments(arguments); | 831 this._processArguments(arguments); |
| 841 this._printActualForFailure = false; | 832 this._printActualForFailure = false; |
| 842 | 833 |
| 843 let passed = true; | 834 let passed = true; |
| 844 let passDetail, failDetail; | 835 let passDetail, failDetail; |
| 845 | 836 |
| 846 // Parsing options. | 837 // Parsing options. |
| 847 let absErrorThreshold = (this._options.absoluteThreshold || 0); | 838 let absErrorThreshold = (this._options.absoluteThreshold || 0); |
| 848 let relErrorThreshold = (this._options.relativeThreshold || 0); | 839 let relErrorThreshold = (this._options.relativeThreshold || 0); |
| 849 | 840 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 860 let maxRelError = -Infinity, maxRelErrorIndex = -1; | 851 let maxRelError = -Infinity, maxRelErrorIndex = -1; |
| 861 | 852 |
| 862 let actual = this._actual; | 853 let actual = this._actual; |
| 863 let expected = this._expected; | 854 let expected = this._expected; |
| 864 | 855 |
| 865 for (let index = 0; index < expected.length; ++index) { | 856 for (let index = 0; index < expected.length; ++index) { |
| 866 let diff = Math.abs(actual[index] - expected[index]); | 857 let diff = Math.abs(actual[index] - expected[index]); |
| 867 let absExpected = Math.abs(expected[index]); | 858 let absExpected = Math.abs(expected[index]); |
| 868 let relError = diff / absExpected; | 859 let relError = diff / absExpected; |
| 869 | 860 |
| 870 if (diff > Math.max(absErrorThreshold, | 861 if (diff > |
| 871 relErrorThreshold * absExpected)) { | 862 Math.max(absErrorThreshold, relErrorThreshold * absExpected)) { |
| 872 | |
| 873 if (diff > maxAbsError) { | 863 if (diff > maxAbsError) { |
| 874 maxAbsErrorIndex = index; | 864 maxAbsErrorIndex = index; |
| 875 maxAbsError = diff; | 865 maxAbsError = diff; |
| 876 } | 866 } |
| 877 | 867 |
| 878 if (!isNaN(relError) && relError > maxRelError) { | 868 if (!isNaN(relError) && relError > maxRelError) { |
| 879 maxRelErrorIndex = index; | 869 maxRelErrorIndex = index; |
| 880 maxRelError = relError; | 870 maxRelError = relError; |
| 881 } | 871 } |
| 882 | 872 |
| 883 errors[index] = diff; | 873 errors[index] = diff; |
| 884 } | 874 } |
| 885 } | 875 } |
| 886 | 876 |
| 887 let numberOfErrors = Object.keys(errors).length; | 877 let numberOfErrors = Object.keys(errors).length; |
| 888 let maxAllowedErrorDetail = JSON.stringify({ | 878 let maxAllowedErrorDetail = JSON.stringify({ |
| 889 absoluteThreshold: absErrorThreshold, | 879 absoluteThreshold: absErrorThreshold, |
| 890 relativeThreshold: relErrorThreshold | 880 relativeThreshold: relErrorThreshold |
| 891 }); | 881 }); |
| 892 | 882 |
| 893 if (numberOfErrors === 0) { | 883 if (numberOfErrors === 0) { |
| 894 // The assertion was successful. | 884 // The assertion was successful. |
| 895 passDetail = '${actual} equals ${expected} with an element-wise ' | 885 passDetail = '${actual} equals ${expected} with an element-wise ' + |
| 896 + 'tolerance of ' + maxAllowedErrorDetail + '.'; | 886 'tolerance of ' + maxAllowedErrorDetail + '.'; |
| 897 } else { | 887 } else { |
| 898 // Failed. Prepare the detailed failure log. | 888 // Failed. Prepare the detailed failure log. |
| 899 passed = false; | 889 passed = false; |
| 900 failDetail = '${actual} does not equal ${expected} with an ' | 890 failDetail = '${actual} does not equal ${expected} with an ' + |
| 901 + 'element-wise tolerance of ' + maxAllowedErrorDetail + '.\n'; | 891 'element-wise tolerance of ' + maxAllowedErrorDetail + '.\n'; |
| 902 | 892 |
| 903 // Print out actual, expected, absolute error, and relative error. | 893 // Print out actual, expected, absolute error, and relative error. |
| 904 let counter = 0; | 894 let counter = 0; |
| 905 failDetail += '\tIndex\tActual\t\t\tExpected\t\tAbsError' | 895 failDetail += '\tIndex\tActual\t\t\tExpected\t\tAbsError' + |
| 906 + '\t\tRelError\t\tTest threshold'; | 896 '\t\tRelError\t\tTest threshold'; |
| 907 let printedIndices = []; | 897 let printedIndices = []; |
| 908 for (let index in errors) { | 898 for (let index in errors) { |
| 909 failDetail += '\n' + _formatFailureEntry( | 899 failDetail += |
| 910 index, actual[index], | 900 '\n' + |
| 911 expected[index], errors[index], | 901 _formatFailureEntry( |
| 912 _closeToThreshold( | 902 index, actual[index], expected[index], errors[index], |
| 913 absErrorThreshold, relErrorThreshold, | 903 _closeToThreshold( |
| 914 expected[index])); | 904 absErrorThreshold, relErrorThreshold, expected[index])); |
| 915 | 905 |
| 916 printedIndices.push(index); | 906 printedIndices.push(index); |
| 917 if (++counter > this._options.numberOfErrors) { | 907 if (++counter > this._options.numberOfErrors) { |
| 918 failDetail += | 908 failDetail += |
| 919 '\n\t...and ' + (numberOfErrors - counter) + ' more errors.'; | 909 '\n\t...and ' + (numberOfErrors - counter) + ' more errors.'; |
| 920 break; | 910 break; |
| 921 } | 911 } |
| 922 } | 912 } |
| 923 | 913 |
| 924 // Finalize the error log: print out the location of both the maxAbs | 914 // Finalize the error log: print out the location of both the maxAbs |
| 925 // error and the maxRel error so we can adjust thresholds appropriately | 915 // error and the maxRel error so we can adjust thresholds appropriately |
| 926 // in the test. | 916 // in the test. |
| 927 failDetail += '\n' | 917 failDetail += '\n' + |
| 928 + '\tMax AbsError of ' + maxAbsError.toExponential(16) | 918 '\tMax AbsError of ' + maxAbsError.toExponential(16) + |
| 929 + ' at index of ' + maxAbsErrorIndex + '.\n'; | 919 ' at index of ' + maxAbsErrorIndex + '.\n'; |
| 930 if (printedIndices.find(element => { | 920 if (printedIndices.find(element => { |
| 931 return element == maxAbsErrorIndex; | 921 return element == maxAbsErrorIndex; |
| 932 }) === undefined) { | 922 }) === undefined) { |
| 933 // Print an entry for this index if we haven't already. | 923 // Print an entry for this index if we haven't already. |
| 934 failDetail += | 924 failDetail += |
| 935 _formatFailureEntry( | 925 _formatFailureEntry( |
| 936 maxAbsErrorIndex, actual[maxAbsErrorIndex], | 926 maxAbsErrorIndex, actual[maxAbsErrorIndex], |
| 937 expected[maxAbsErrorIndex], errors[maxAbsErrorIndex], | 927 expected[maxAbsErrorIndex], errors[maxAbsErrorIndex], |
| 938 _closeToThreshold( | 928 _closeToThreshold( |
| 939 absErrorThreshold, relErrorThreshold, | 929 absErrorThreshold, relErrorThreshold, |
| (...skipping 26 matching lines...) Expand all Loading... |
| 966 * | 956 * |
| 967 * TODO(hongchan): remove this method when the transition from the old Audit | 957 * TODO(hongchan): remove this method when the transition from the old Audit |
| 968 * to the new Audit is completed. | 958 * to the new Audit is completed. |
| 969 * @example | 959 * @example |
| 970 * should(true, 'The message is').message('truthful!', 'false!'); | 960 * should(true, 'The message is').message('truthful!', 'false!'); |
| 971 * | 961 * |
| 972 * @result | 962 * @result |
| 973 * "PASS The message is truthful!" | 963 * "PASS The message is truthful!" |
| 974 */ | 964 */ |
| 975 message(passDetail, failDetail) { | 965 message(passDetail, failDetail) { |
| 976 return this._assert(this._actual, | 966 return this._assert( |
| 977 '${actual} ' + passDetail, | 967 this._actual, '${actual} ' + passDetail, '${actual} ' + failDetail); |
| 978 '${actual} ' + failDetail); | |
| 979 } | 968 } |
| 980 | 969 |
| 981 /** | 970 /** |
| 982 * Check if |expected| property is truly owned by |actual| object. | 971 * Check if |expected| property is truly owned by |actual| object. |
| 983 * | 972 * |
| 984 * @example | 973 * @example |
| 985 * should(BaseAudioContext.prototype, | 974 * should(BaseAudioContext.prototype, |
| 986 * 'BaseAudioContext.prototype').haveOwnProperty('createGain'); | 975 * 'BaseAudioContext.prototype').haveOwnProperty('createGain'); |
| 987 * | 976 * |
| 988 * @result | 977 * @result |
| 989 * "PASS BaseAudioContext.prototype has an own property of | 978 * "PASS BaseAudioContext.prototype has an own property of |
| 990 * 'createGain'." | 979 * 'createGain'." |
| 991 */ | 980 */ |
| 992 haveOwnProperty () { | 981 haveOwnProperty() { |
| 993 this._processArguments(arguments); | 982 this._processArguments(arguments); |
| 994 | 983 |
| 995 return this._assert( | 984 return this._assert( |
| 996 this._actual.hasOwnProperty(this._expected), | 985 this._actual.hasOwnProperty(this._expected), |
| 997 '${actual} has an own property of "${expected}".', | 986 '${actual} has an own property of "${expected}".', |
| 998 '${actual} does not own the property of "${expected}".'); | 987 '${actual} does not own the property of "${expected}".'); |
| 999 } | 988 } |
| 1000 | 989 |
| 1001 | 990 |
| 1002 /** | 991 /** |
| 1003 * Check if |expected| property is not owned by |actual| object. | 992 * Check if |expected| property is not owned by |actual| object. |
| 1004 * | 993 * |
| 1005 * @example | 994 * @example |
| 1006 * should(BaseAudioContext.prototype, | 995 * should(BaseAudioContext.prototype, |
| 1007 * 'BaseAudioContext.prototype') | 996 * 'BaseAudioContext.prototype') |
| 1008 * .notHaveOwnProperty('startRendering'); | 997 * .notHaveOwnProperty('startRendering'); |
| 1009 * | 998 * |
| 1010 * @result | 999 * @result |
| 1011 * "PASS BaseAudioContext.prototype does not have an own property of | 1000 * "PASS BaseAudioContext.prototype does not have an own property of |
| 1012 * 'startRendering'." | 1001 * 'startRendering'." |
| 1013 */ | 1002 */ |
| 1014 notHaveOwnProperty () { | 1003 notHaveOwnProperty() { |
| 1015 this._processArguments(arguments); | 1004 this._processArguments(arguments); |
| 1016 | 1005 |
| 1017 return this._assert( | 1006 return this._assert( |
| 1018 !this._actual.hasOwnProperty(this._expected), | 1007 !this._actual.hasOwnProperty(this._expected), |
| 1019 '${actual} does not have an own property of "${expected}".', | 1008 '${actual} does not have an own property of "${expected}".', |
| 1020 '${actual} has an own the property of "${expected}".') | 1009 '${actual} has an own the property of "${expected}".') |
| 1021 } | 1010 } |
| 1022 | 1011 |
| 1023 | 1012 |
| 1024 /** | 1013 /** |
| 1025 * Check if an object is inherited from a class. This looks up the entire | 1014 * Check if an object is inherited from a class. This looks up the entire |
| 1026 * prototype chain of a given object and tries to find a match. | 1015 * prototype chain of a given object and tries to find a match. |
| 1027 * | 1016 * |
| 1028 * @example | 1017 * @example |
| 1029 * should(sourceNode, 'A buffer source node') | 1018 * should(sourceNode, 'A buffer source node') |
| 1030 * .inheritFrom('AudioScheduledSourceNode'); | 1019 * .inheritFrom('AudioScheduledSourceNode'); |
| 1031 * | 1020 * |
| 1032 * @result | 1021 * @result |
| 1033 * "PASS A buffer source node inherits from 'AudioScheduledSourceNode'." | 1022 * "PASS A buffer source node inherits from 'AudioScheduledSourceNode'." |
| 1034 */ | 1023 */ |
| 1035 inheritFrom () { | 1024 inheritFrom() { |
| 1036 this._processArguments(arguments); | 1025 this._processArguments(arguments); |
| 1037 | 1026 |
| 1038 let prototypes = []; | 1027 let prototypes = []; |
| 1039 let currentPrototype = Object.getPrototypeOf(this._actual); | 1028 let currentPrototype = Object.getPrototypeOf(this._actual); |
| 1040 while (currentPrototype) { | 1029 while (currentPrototype) { |
| 1041 prototypes.push(currentPrototype.constructor.name); | 1030 prototypes.push(currentPrototype.constructor.name); |
| 1042 currentPrototype = Object.getPrototypeOf(currentPrototype); | 1031 currentPrototype = Object.getPrototypeOf(currentPrototype); |
| 1043 } | 1032 } |
| 1044 | 1033 |
| 1045 return this._assert( | 1034 return this._assert( |
| 1046 prototypes.includes(this._expected), | 1035 prototypes.includes(this._expected), |
| 1047 '${actual} inherits from "${expected}".', | 1036 '${actual} inherits from "${expected}".', |
| 1048 '${actual} does not inherit from "${expected}".'); | 1037 '${actual} does not inherit from "${expected}".'); |
| 1049 } | 1038 } |
| 1050 } | 1039 } |
| 1051 | 1040 |
| 1052 | 1041 |
| 1053 // Task Class state enum. | 1042 // Task Class state enum. |
| 1054 const TaskState = { | 1043 const TaskState = {PENDING: 0, STARTED: 1, FINISHED: 2}; |
| 1055 PENDING: 0, | |
| 1056 STARTED: 1, | |
| 1057 FINISHED: 2 | |
| 1058 }; | |
| 1059 | 1044 |
| 1060 | 1045 |
| 1061 /** | 1046 /** |
| 1062 * @class Task | 1047 * @class Task |
| 1063 * @description WebAudio testing task. Managed by TaskRunner. | 1048 * @description WebAudio testing task. Managed by TaskRunner. |
| 1064 */ | 1049 */ |
| 1065 class Task { | 1050 class Task { |
| 1066 | |
| 1067 /** | 1051 /** |
| 1068 * Task constructor. | 1052 * Task constructor. |
| 1069 * @param {Object} taskRunner Reference of associated task runner. | 1053 * @param {Object} taskRunner Reference of associated task runner. |
| 1070 * @param {String||Object} taskLabel Task label if a string is given. This | 1054 * @param {String||Object} taskLabel Task label if a string is given. This |
| 1071 * parameter can be a dictionary with the | 1055 * parameter can be a dictionary with the |
| 1072 * following fields. | 1056 * following fields. |
| 1073 * @param {String} taskLabel.label Task label. | 1057 * @param {String} taskLabel.label Task label. |
| 1074 * @param {String} taskLabel.description Description of task. | 1058 * @param {String} taskLabel.description Description of task. |
| 1075 * @param {Function} taskFunction Task function to be performed. | 1059 * @param {Function} taskFunction Task function to be performed. |
| 1076 * @return {Object} Task object. | 1060 * @return {Object} Task object. |
| 1077 */ | 1061 */ |
| 1078 constructor (taskRunner, taskLabel, taskFunction) { | 1062 constructor(taskRunner, taskLabel, taskFunction) { |
| 1079 this._taskRunner = taskRunner; | 1063 this._taskRunner = taskRunner; |
| 1080 this._taskFunction = taskFunction; | 1064 this._taskFunction = taskFunction; |
| 1081 | 1065 |
| 1082 if (typeof taskLabel === 'string') { | 1066 if (typeof taskLabel === 'string') { |
| 1083 this._label = taskLabel; | 1067 this._label = taskLabel; |
| 1084 this._description = null; | 1068 this._description = null; |
| 1085 } else if (typeof taskLabel === 'object') { | 1069 } else if (typeof taskLabel === 'object') { |
| 1086 if (typeof taskLabel.label !== 'string') { | 1070 if (typeof taskLabel.label !== 'string') { |
| 1087 _throwException('Task.constructor:: task label must be string.'); | 1071 _throwException('Task.constructor:: task label must be string.'); |
| 1088 } | 1072 } |
| 1089 this._label = taskLabel.label; | 1073 this._label = taskLabel.label; |
| 1090 this._description = (typeof taskLabel.description === 'string') | 1074 this._description = (typeof taskLabel.description === 'string') ? |
| 1091 ? taskLabel.description : null; | 1075 taskLabel.description : |
| 1076 null; |
| 1092 } else { | 1077 } else { |
| 1093 _throwException('Task.constructor:: task label must be a string or ' + | 1078 _throwException( |
| 1094 'a dictionary.'); | 1079 'Task.constructor:: task label must be a string or ' + |
| 1080 'a dictionary.'); |
| 1095 } | 1081 } |
| 1096 | 1082 |
| 1097 this._state = TaskState.PENDING; | 1083 this._state = TaskState.PENDING; |
| 1098 this._result = true; | 1084 this._result = true; |
| 1099 | 1085 |
| 1100 this._totalAssertions = 0; | 1086 this._totalAssertions = 0; |
| 1101 this._failedAssertions = 0; | 1087 this._failedAssertions = 0; |
| 1102 } | 1088 } |
| 1103 | 1089 |
| 1104 get label () { | 1090 get label() { |
| 1105 return this._label; | 1091 return this._label; |
| 1106 } | 1092 } |
| 1107 | 1093 |
| 1108 get state () { | 1094 get state() { |
| 1109 return this._state; | 1095 return this._state; |
| 1110 } | 1096 } |
| 1111 | 1097 |
| 1112 get result () { | 1098 get result() { |
| 1113 return this._result; | 1099 return this._result; |
| 1114 } | 1100 } |
| 1115 | 1101 |
| 1116 // Start the assertion chain. | 1102 // Start the assertion chain. |
| 1117 should (actual, actualDescription) { | 1103 should(actual, actualDescription) { |
| 1118 // If no argument is given, we cannot proceed. Halt. | 1104 // If no argument is given, we cannot proceed. Halt. |
| 1119 if (arguments.length === 0) | 1105 if (arguments.length === 0) |
| 1120 _throwException('Task.should:: requires at least 1 argument.'); | 1106 _throwException('Task.should:: requires at least 1 argument.'); |
| 1121 | 1107 |
| 1122 return new Should(this, actual, actualDescription); | 1108 return new Should(this, actual, actualDescription); |
| 1123 } | 1109 } |
| 1124 | 1110 |
| 1125 // Run this task. |this| task will be passed into the user-supplied test | 1111 // Run this task. |this| task will be passed into the user-supplied test |
| 1126 // task function. | 1112 // task function. |
| 1127 run () { | 1113 run() { |
| 1128 this._state = TaskState.STARTED; | 1114 this._state = TaskState.STARTED; |
| 1129 | 1115 |
| 1130 // Print out the task entry with label and description. | 1116 // Print out the task entry with label and description. |
| 1131 _logPassed('> [' + this._label + '] ' | 1117 _logPassed( |
| 1132 + (this._description ? this._description : '')); | 1118 '> [' + this._label + '] ' + |
| 1119 (this._description ? this._description : '')); |
| 1133 | 1120 |
| 1134 this._taskFunction( | 1121 this._taskFunction(this, this.should.bind(this)); |
| 1135 this, | |
| 1136 this.should.bind(this)); | |
| 1137 } | 1122 } |
| 1138 | 1123 |
| 1139 // Update the task success based on the individual assertion/test inside. | 1124 // Update the task success based on the individual assertion/test inside. |
| 1140 update (subTask) { | 1125 update(subTask) { |
| 1141 // After one of tests fails within a task, the result is irreversible. | 1126 // After one of tests fails within a task, the result is irreversible. |
| 1142 if (subTask.result === false) { | 1127 if (subTask.result === false) { |
| 1143 this._result = false; | 1128 this._result = false; |
| 1144 this._failedAssertions++; | 1129 this._failedAssertions++; |
| 1145 } | 1130 } |
| 1146 | 1131 |
| 1147 this._totalAssertions++; | 1132 this._totalAssertions++; |
| 1148 } | 1133 } |
| 1149 | 1134 |
| 1150 // Finish the current task and start the next one if available. | 1135 // Finish the current task and start the next one if available. |
| 1151 done () { | 1136 done() { |
| 1152 this._state = TaskState.FINISHED; | 1137 this._state = TaskState.FINISHED; |
| 1153 | 1138 |
| 1154 let message = '< [' + this._label + '] '; | 1139 let message = '< [' + this._label + '] '; |
| 1155 | 1140 |
| 1156 if (this._result) { | 1141 if (this._result) { |
| 1157 message += 'All assertions passed. (total ' + this._totalAssertions | 1142 message += 'All assertions passed. (total ' + this._totalAssertions + |
| 1158 + ' assertions)'; | 1143 ' assertions)'; |
| 1159 _logPassed(message); | 1144 _logPassed(message); |
| 1160 } else { | 1145 } else { |
| 1161 message += this._failedAssertions + ' out of ' + this._totalAssertions | 1146 message += this._failedAssertions + ' out of ' + this._totalAssertions + |
| 1162 + ' assertions were failed.' | 1147 ' assertions were failed.' |
| 1163 _logFailed(message); | 1148 _logFailed(message); |
| 1164 } | 1149 } |
| 1165 | 1150 |
| 1166 this._taskRunner._runNextTask(); | 1151 this._taskRunner._runNextTask(); |
| 1167 } | 1152 } |
| 1168 | 1153 |
| 1169 isPassed () { | 1154 isPassed() { |
| 1170 return this._state === TaskState.FINISHED && this._result; | 1155 return this._state === TaskState.FINISHED && this._result; |
| 1171 } | 1156 } |
| 1172 | 1157 |
| 1173 toString () { | 1158 toString() { |
| 1174 return '"' + this._label + '": ' + this._description; | 1159 return '"' + this._label + '": ' + this._description; |
| 1175 } | 1160 } |
| 1176 | |
| 1177 } | 1161 } |
| 1178 | 1162 |
| 1179 | 1163 |
| 1180 /** | 1164 /** |
| 1181 * @class TaskRunner | 1165 * @class TaskRunner |
| 1182 * @description WebAudio testing task runner. Manages tasks. | 1166 * @description WebAudio testing task runner. Manages tasks. |
| 1183 */ | 1167 */ |
| 1184 class TaskRunner { | 1168 class TaskRunner { |
| 1185 | 1169 constructor() { |
| 1186 constructor () { | |
| 1187 this._tasks = {}; | 1170 this._tasks = {}; |
| 1188 this._taskSequence = []; | 1171 this._taskSequence = []; |
| 1189 this._currentTaskIndex = -1; | 1172 this._currentTaskIndex = -1; |
| 1190 | 1173 |
| 1191 // Configure testharness.js for the async operation. | 1174 // Configure testharness.js for the async operation. |
| 1192 setup (new Function (), { | 1175 setup(new Function(), {explicit_done: true}); |
| 1193 explicit_done: true | |
| 1194 }); | |
| 1195 } | 1176 } |
| 1196 | 1177 |
| 1197 _runNextTask () { | 1178 _runNextTask() { |
| 1198 if (this._currentTaskIndex < this._taskSequence.length) { | 1179 if (this._currentTaskIndex < this._taskSequence.length) { |
| 1199 this._tasks[this._taskSequence[this._currentTaskIndex++]].run(); | 1180 this._tasks[this._taskSequence[this._currentTaskIndex++]].run(); |
| 1200 } else { | 1181 } else { |
| 1201 this._finish(); | 1182 this._finish(); |
| 1202 } | 1183 } |
| 1203 } | 1184 } |
| 1204 | 1185 |
| 1205 _finish () { | 1186 _finish() { |
| 1206 let numberOfFailures = 0; | 1187 let numberOfFailures = 0; |
| 1207 for (let taskIndex in this._taskSequence) { | 1188 for (let taskIndex in this._taskSequence) { |
| 1208 let task = this._tasks[this._taskSequence[taskIndex]]; | 1189 let task = this._tasks[this._taskSequence[taskIndex]]; |
| 1209 numberOfFailures += task.result ? 0 : 1; | 1190 numberOfFailures += task.result ? 0 : 1; |
| 1210 } | 1191 } |
| 1211 | 1192 |
| 1212 let prefix = '# AUDIT TASK RUNNER FINISHED: '; | 1193 let prefix = '# AUDIT TASK RUNNER FINISHED: '; |
| 1213 if (numberOfFailures > 0) { | 1194 if (numberOfFailures > 0) { |
| 1214 _logFailed(prefix + numberOfFailures + ' out of ' | 1195 _logFailed( |
| 1215 + this._taskSequence.length + ' tasks were failed.'); | 1196 prefix + numberOfFailures + ' out of ' + this._taskSequence.length + |
| 1197 ' tasks were failed.'); |
| 1216 } else { | 1198 } else { |
| 1217 _logPassed(prefix + this._taskSequence.length | 1199 _logPassed( |
| 1218 + ' tasks ran successfully.'); | 1200 prefix + this._taskSequence.length + ' tasks ran successfully.'); |
| 1219 } | 1201 } |
| 1220 | 1202 |
| 1221 // From testharness.js, report back to the test infrastructure that | 1203 // From testharness.js, report back to the test infrastructure that |
| 1222 // the task runner completed all the tasks. | 1204 // the task runner completed all the tasks. |
| 1223 _testharnessDone(); | 1205 _testharnessDone(); |
| 1224 } | 1206 } |
| 1225 | 1207 |
| 1226 // |taskLabel| can be either a string or a dictionary. See Task constructor | 1208 // |taskLabel| can be either a string or a dictionary. See Task constructor |
| 1227 // for the detail. | 1209 // for the detail. |
| 1228 define (taskLabel, taskFunction) { | 1210 define(taskLabel, taskFunction) { |
| 1229 let task = new Task(this, taskLabel, taskFunction); | 1211 let task = new Task(this, taskLabel, taskFunction); |
| 1230 if (this._tasks.hasOwnProperty(task.label)) { | 1212 if (this._tasks.hasOwnProperty(task.label)) { |
| 1231 _throwException('Audit.define:: Duplicate task definition.'); | 1213 _throwException('Audit.define:: Duplicate task definition.'); |
| 1232 return; | 1214 return; |
| 1233 } | 1215 } |
| 1234 this._tasks[task.label] = task; | 1216 this._tasks[task.label] = task; |
| 1235 this._taskSequence.push(task.label); | 1217 this._taskSequence.push(task.label); |
| 1236 } | 1218 } |
| 1237 | 1219 |
| 1238 // Start running all the tasks scheduled. Multiple task names can be passed | 1220 // Start running all the tasks scheduled. Multiple task names can be passed |
| 1239 // to execute them sequentially. Zero argument will perform all defined | 1221 // to execute them sequentially. Zero argument will perform all defined |
| 1240 // tasks in the order of definition. | 1222 // tasks in the order of definition. |
| 1241 run () { | 1223 run() { |
| 1242 // Display the beginning of the test suite. | 1224 // Display the beginning of the test suite. |
| 1243 _logPassed('# AUDIT TASK RUNNER STARTED.'); | 1225 _logPassed('# AUDIT TASK RUNNER STARTED.'); |
| 1244 | 1226 |
| 1245 // If the argument is specified, override the default task sequence with | 1227 // If the argument is specified, override the default task sequence with |
| 1246 // the specified one. | 1228 // the specified one. |
| 1247 if (arguments.length > 0) { | 1229 if (arguments.length > 0) { |
| 1248 this._taskSequence = []; | 1230 this._taskSequence = []; |
| 1249 for (let i = 0; i < arguments.length; i++) { | 1231 for (let i = 0; i < arguments.length; i++) { |
| 1250 let taskLabel = arguments[i]; | 1232 let taskLabel = arguments[i]; |
| 1251 if (!this._tasks.hasOwnProperty(taskLabel)) { | 1233 if (!this._tasks.hasOwnProperty(taskLabel)) { |
| 1252 _throwException('Audit.run:: undefined task.'); | 1234 _throwException('Audit.run:: undefined task.'); |
| 1253 } else if (this._taskSequence.includes(taskLabel)) { | 1235 } else if (this._taskSequence.includes(taskLabel)) { |
| 1254 _throwException('Audit.run:: duplicate task request.'); | 1236 _throwException('Audit.run:: duplicate task request.'); |
| 1255 } else { | 1237 } else { |
| 1256 this._taskSequence.push(taskLabel); | 1238 this._taskSequence.push(taskLabel); |
| 1257 } | 1239 } |
| 1258 } | 1240 } |
| 1259 } | 1241 } |
| 1260 | 1242 |
| 1261 if (this._taskSequence.length === 0) { | 1243 if (this._taskSequence.length === 0) { |
| 1262 _throwException('Audit.run:: no task to run.'); | 1244 _throwException('Audit.run:: no task to run.'); |
| 1263 return; | 1245 return; |
| 1264 } | 1246 } |
| 1265 | 1247 |
| 1266 // Start the first task. | 1248 // Start the first task. |
| 1267 this._currentTaskIndex = 0; | 1249 this._currentTaskIndex = 0; |
| 1268 this._runNextTask(); | 1250 this._runNextTask(); |
| 1269 } | 1251 } |
| 1270 | |
| 1271 } | 1252 } |
| 1272 | 1253 |
| 1273 /** | 1254 /** |
| 1274 * Load file from a given URL and pass ArrayBuffer to the following promise. | 1255 * Load file from a given URL and pass ArrayBuffer to the following promise. |
| 1275 * @param {String} fileUrl file URL. | 1256 * @param {String} fileUrl file URL. |
| 1276 * @return {Promise} | 1257 * @return {Promise} |
| 1277 * | 1258 * |
| 1278 * @example | 1259 * @example |
| 1279 * Audit.loadFileFromUrl('resources/my-sound.ogg').then((response) => { | 1260 * Audit.loadFileFromUrl('resources/my-sound.ogg').then((response) => { |
| 1280 * audioContext.decodeAudioData(response).then((audioBuffer) => { | 1261 * audioContext.decodeAudioData(response).then((audioBuffer) => { |
| 1281 * // Do something with AudioBuffer. | 1262 * // Do something with AudioBuffer. |
| 1282 * }); | 1263 * }); |
| 1283 * }); | 1264 * }); |
| 1284 */ | 1265 */ |
| 1285 function loadFileFromUrl (fileUrl) { | 1266 function loadFileFromUrl(fileUrl) { |
| 1286 return new Promise((resolve, reject) => { | 1267 return new Promise((resolve, reject) => { |
| 1287 let xhr = new XMLHttpRequest(); | 1268 let xhr = new XMLHttpRequest(); |
| 1288 xhr.open('GET', fileUrl, true); | 1269 xhr.open('GET', fileUrl, true); |
| 1289 xhr.responseType = 'arraybuffer'; | 1270 xhr.responseType = 'arraybuffer'; |
| 1290 | 1271 |
| 1291 xhr.onload = () => { | 1272 xhr.onload = () => { |
| 1292 // |status = 0| is a workaround for the run-webkit-test server. We are | 1273 // |status = 0| is a workaround for the run-webkit-test server. We are |
| 1293 // speculating the server quits the transaction prematurely without | 1274 // speculating the server quits the transaction prematurely without |
| 1294 // completing the request. | 1275 // completing the request. |
| 1295 if (xhr.status === 200 || xhr.status === 0) { | 1276 if (xhr.status === 200 || xhr.status === 0) { |
| 1296 resolve(xhr.response); | 1277 resolve(xhr.response); |
| 1297 } else { | 1278 } else { |
| 1298 let errorMessage = 'loadFile: Request failed when loading ' + | 1279 let errorMessage = 'loadFile: Request failed when loading ' + |
| 1299 fileUrl + '. ' + xhr.statusText + '. (status = ' + | 1280 fileUrl + '. ' + xhr.statusText + '. (status = ' + xhr.status + |
| 1300 xhr.status + ')'; | 1281 ')'; |
| 1301 if (reject) { | 1282 if (reject) { |
| 1302 reject(errorMessage); | 1283 reject(errorMessage); |
| 1303 } else { | 1284 } else { |
| 1304 new Error(errorMessage); | 1285 new Error(errorMessage); |
| 1305 } | 1286 } |
| 1306 } | 1287 } |
| 1307 }; | 1288 }; |
| 1308 | 1289 |
| 1309 xhr.onerror = (event) => { | 1290 xhr.onerror = (event) => { |
| 1310 let errorMessage = | 1291 let errorMessage = |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1334 return { | 1315 return { |
| 1335 | 1316 |
| 1336 /** | 1317 /** |
| 1337 * Creates an instance of Audit task runner. | 1318 * Creates an instance of Audit task runner. |
| 1338 * @param {Object} options Options for task runner. | 1319 * @param {Object} options Options for task runner. |
| 1339 * @param {Boolean} options.requireResultFile True if the test suite | 1320 * @param {Boolean} options.requireResultFile True if the test suite |
| 1340 * requires explicit text | 1321 * requires explicit text |
| 1341 * comparison with the expected | 1322 * comparison with the expected |
| 1342 * result file. | 1323 * result file. |
| 1343 */ | 1324 */ |
| 1344 createTaskRunner: function (options) { | 1325 createTaskRunner: function(options) { |
| 1345 if (options && options.requireResultFile == true) { | 1326 if (options && options.requireResultFile == true) { |
| 1346 _logError('this test requires the explicit comparison with the ' | 1327 _logError( |
| 1347 + 'expected result when it runs with run-webkit-tests.'); | 1328 'this test requires the explicit comparison with the ' + |
| 1329 'expected result when it runs with run-webkit-tests.'); |
| 1348 } | 1330 } |
| 1349 | 1331 |
| 1350 return new TaskRunner(); | 1332 return new TaskRunner(); |
| 1351 }, | 1333 }, |
| 1352 | 1334 |
| 1353 /** | 1335 /** |
| 1354 * Load file from a given URL and pass ArrayBuffer to the following promise. | 1336 * Load file from a given URL and pass ArrayBuffer to the following promise. |
| 1355 * See |loadFileFromUrl| method for the detail. | 1337 * See |loadFileFromUrl| method for the detail. |
| 1356 */ | 1338 */ |
| 1357 loadFileFromUrl: loadFileFromUrl | 1339 loadFileFromUrl: loadFileFromUrl |
| 1358 | 1340 |
| 1359 }; | 1341 }; |
| 1360 | 1342 |
| 1361 })(); | 1343 })(); |
| OLD | NEW |