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 |