OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // Library providing basic test framework functionality. | 5 /** |
| 6 * @fileoverview Library providing basic test framework functionality. |
| 7 **/ |
| 8 |
| 9 /** |
| 10 * Namespace for |Test|. |
| 11 * @type {Object} |
| 12 **/ |
| 13 var testing = {}; |
| 14 |
| 15 /** |
| 16 * Hold the currentTestCase across between PreLoad and Run. |
| 17 * @type {TestCase} |
| 18 **/ |
| 19 var currentTestCase = null; |
6 | 20 |
7 (function() { | 21 (function() { |
| 22 // Provide global objects for generation case. |
| 23 if (this['window'] === undefined) |
| 24 this['window'] = this; |
| 25 if (this['chrome'] === undefined) { |
| 26 this['chrome'] = { |
| 27 send: function() {}, |
| 28 }; |
| 29 } |
| 30 if (this['console'] === undefined) { |
| 31 this['console'] = { |
| 32 log: print, |
| 33 }; |
| 34 } |
| 35 |
| 36 /** |
| 37 * This class will be exported as testing.Test, and is provided to hold the |
| 38 * fixture's configuration and callback methods for the various phases of |
| 39 * invoking a test. It is called "Test" rather than TestFixture to roughly |
| 40 * mimic the gtest's class names. |
| 41 * @constructor |
| 42 **/ |
| 43 function Test() {} |
| 44 |
| 45 Test.prototype = { |
| 46 /** |
| 47 * The name of the test. |
| 48 **/ |
| 49 name: null, |
| 50 |
| 51 /** |
| 52 * When set to a string value representing a url, generate BrowsePreload |
| 53 * call, which will browse to the url and call fixture.PreLoad of the |
| 54 * currentTestCase. |
| 55 * @type {String} |
| 56 **/ |
| 57 browsePreload: null, |
| 58 |
| 59 /** |
| 60 * When set to a string value representing an html page in the test |
| 61 * directory, generate BrowsePrintPreload call, which will browse to a url |
| 62 * representing the file, cause print, and call fixture.PreLoad of the |
| 63 * currentTestCase. |
| 64 * @type {String} |
| 65 **/ |
| 66 browsePrintPreload: null, |
| 67 |
| 68 /** |
| 69 * When set to a function, will be called in the context of the test |
| 70 * generation inside the function, and before any generated C++. |
| 71 * @type {function(string,string)} |
| 72 **/ |
| 73 testGenPreamble: null, |
| 74 |
| 75 /** |
| 76 * When set to a function, will be called in the context of the test |
| 77 * generation inside the function, and before any generated C++. |
| 78 * @type {function(string,string)} |
| 79 **/ |
| 80 testGenPostamble: null, |
| 81 |
| 82 /** |
| 83 * When set to a non-null String, auto-generate typedef before generating |
| 84 * TEST*: {@code typedef typedefCppFixture testFixture}. |
| 85 * @type {String} |
| 86 **/ |
| 87 typedefCppFixture: 'WebUIBrowserTest', |
| 88 |
| 89 /** |
| 90 * This should be initialized by the test fixture and can be referenced |
| 91 * during the test run. |
| 92 * @type {Mock4JS.Mock} |
| 93 **/ |
| 94 mockHandler: null, |
| 95 |
| 96 /** |
| 97 * Override this method to perform initialization during preload (such as |
| 98 * creating mocks and registering handlers). |
| 99 * @type {Function} |
| 100 **/ |
| 101 PreLoad: function() {}, |
| 102 |
| 103 /** |
| 104 * Override this method to perform tasks before running your test. |
| 105 * @type {Function} |
| 106 **/ |
| 107 SetUp: function() {}, |
| 108 |
| 109 /** |
| 110 * Override this method to perform tasks after running your test. If you |
| 111 * create a mock class, you must call Mock4JS.verifyAllMocks() in this |
| 112 * phase. |
| 113 * @type {Function} |
| 114 **/ |
| 115 TearDown: function() { |
| 116 Mock4JS.verifyAllMocks(); |
| 117 } |
| 118 }; |
| 119 |
| 120 /** |
| 121 * This class is not exported and is available to hold the state of the |
| 122 * |currentTestCase| throughout preload and test run. |
| 123 * @param {String} name The name of the test case. |
| 124 * @param {Test} fixture The fixture object for this test case. |
| 125 * @param {Function} body The code to run for the test. |
| 126 * @constructor |
| 127 **/ |
| 128 function TestCase(name, fixture, body) { |
| 129 this.name = name; |
| 130 this.fixture = fixture; |
| 131 this.body = body; |
| 132 } |
| 133 |
| 134 TestCase.prototype = { |
| 135 name: null, |
| 136 fixture: null, |
| 137 body: null, |
| 138 |
| 139 /** |
| 140 * Called at preload time, proxies to the fixture. |
| 141 * @type {Function} |
| 142 **/ |
| 143 PreLoad: function(name) { |
| 144 if (this.fixture) |
| 145 this.fixture.PreLoad(); |
| 146 }, |
| 147 |
| 148 /** |
| 149 * Runs this test case. |
| 150 * @type {Function} |
| 151 **/ |
| 152 Run: function() { |
| 153 if (this.fixture) |
| 154 this.fixture.SetUp(); |
| 155 if (this.body) |
| 156 this.body.call(this.fixture); |
| 157 if (this.fixture) |
| 158 this.fixture.TearDown(); |
| 159 }, |
| 160 }; |
| 161 |
| 162 /** |
| 163 * Registry of javascript-defined callbacks for {@code chrome.send}. |
| 164 * @type {Object} |
| 165 **/ |
| 166 var sendCallbacks = {}; |
| 167 |
| 168 /** |
| 169 * Registers the message, object and callback for {@code chrome.send} |
| 170 * @param {String} name The name of the message to route to this |callback|. |
| 171 * @param {Object} messageHAndler Pass as |this| when calling the |callback|. |
| 172 * @param {function(...)} callback Called by {@code chrome.send}. |
| 173 * @see sendCallbacks |
| 174 **/ |
| 175 function registerMessageCallback(name, messageHandler, callback) { |
| 176 sendCallbacks[name] = [messageHandler, callback]; |
| 177 } |
| 178 |
| 179 /** |
| 180 * Register all methods of {@code mockClass.prototype} with messages of the |
| 181 * same name as the method, using the proxy of the |mockObject| as the |
| 182 * |messageHandler| when registering. |
| 183 * @param {Mock4JS.Mock} mockObject The mock to register callbacks against. |
| 184 * @param {function(new:Object)} mockClAss Constructor for the mocked class. |
| 185 * @see registerMessageCallback |
| 186 **/ |
| 187 function registerMockMessageCallbacks(mockObject, mockClass) { |
| 188 var mockProxy = mockObject.proxy(); |
| 189 for (func in mockClass.prototype) { |
| 190 if (typeof(mockClass.prototype[func]) == 'function') { |
| 191 registerMessageCallback(func, |
| 192 mockProxy, |
| 193 mockProxy[func]); |
| 194 } |
| 195 } |
| 196 } |
| 197 |
| 198 /** |
| 199 * Holds the old chrome object when overriding for preload and registry of |
| 200 * handlers. |
| 201 * @type {Object} |
| 202 **/ |
| 203 var oldChrome = chrome; |
| 204 |
| 205 /** |
| 206 * Overrides {@code chrome.send} for routing messages to javascript |
| 207 * functions. Also fallsback to sending with the |oldChrome| object. |
| 208 * @param {String} messageName The message to route. |
| 209 * @see oldChrome |
| 210 **/ |
| 211 function send(messageName) { |
| 212 var callback = sendCallbacks[messageName]; |
| 213 var args = Array.prototype.slice.call(arguments, 1); |
| 214 if (callback != undefined) |
| 215 callback[1].apply(callback[0], args); |
| 216 else |
| 217 oldChrome.send.apply(oldChrome, args); |
| 218 } |
| 219 |
8 // Asserts. | 220 // Asserts. |
9 // Use the following assertions to verify a condition within a test. | 221 // Use the following assertions to verify a condition within a test. |
10 // If assertion fails, the C++ backend will be immediately notified. | 222 // If assertion fails, the C++ backend will be immediately notified. |
11 // If assertion passes, no notification will be sent to the C++ backend. | 223 // If assertion passes, no notification will be sent to the C++ backend. |
| 224 |
| 225 /** |
| 226 * When |test| !== |expected|, aborts the current test. |
| 227 * @param {Boolean} test The predicate to check against |expected|. |
| 228 * @param {Boolean} expected The expected value of |test|. |
| 229 * @param {String=} message The message to include in the Error thrown. |
| 230 * @throws {Error} upon failure. |
| 231 **/ |
12 function assertBool(test, expected, message) { | 232 function assertBool(test, expected, message) { |
13 if (test !== expected) { | 233 if (test !== expected) { |
14 if (message) | 234 if (message) |
15 message = test + '\n' + message; | 235 message = test + '\n' + message; |
16 else | 236 else |
17 message = test; | 237 message = test; |
18 throw new Error(message); | 238 throw new Error(message); |
19 } | 239 } |
20 } | 240 } |
21 | 241 |
22 var old_chrome = chrome; | 242 /** |
23 var send_callbacks = {}; | 243 * When |test| !== true, aborts the current test. |
24 | 244 * @param {Boolean} test The predicate to check against |expected|. |
25 function registerMessageCallback(name, object, callback) { | 245 * @param {String=} message The message to include in the Error thrown. |
26 send_callbacks[name] = [object, callback]; | 246 * @throws {Error} upon failure. |
27 } | 247 **/ |
28 | |
29 function send(messageName) { | |
30 var callback = send_callbacks[messageName]; | |
31 var args = Array.prototype.slice.call(arguments, 1); | |
32 if (callback != undefined) | |
33 callback[1].apply(callback[0], args); | |
34 else | |
35 old_chrome.send.apply(old_chrome, args); | |
36 } | |
37 | |
38 function assertTrue(test, message) { | 248 function assertTrue(test, message) { |
39 assertBool(test, true, message); | 249 assertBool(test, true, message); |
40 } | 250 } |
41 | 251 |
| 252 /** |
| 253 * When |test| !== false, aborts the current test. |
| 254 * @param {Boolean} test The predicate to check against |expected|. |
| 255 * @param {String=} message The message to include in the Error thrown. |
| 256 * @throws {Error} upon failure. |
| 257 **/ |
42 function assertFalse(test, message) { | 258 function assertFalse(test, message) { |
43 assertBool(test, false, message); | 259 assertBool(test, false, message); |
44 } | 260 } |
45 | 261 |
| 262 /** |
| 263 * When |expected| !== |actual|, aborts the current test. |
| 264 * @param {*} expected The predicate to check against |expected|. |
| 265 * @param {*} actual The expected value of |test|. |
| 266 * @param {String=} message The message to include in the Error thrown. |
| 267 * @throws {Error} upon failure. |
| 268 **/ |
46 function assertEquals(expected, actual, message) { | 269 function assertEquals(expected, actual, message) { |
47 if (expected != actual) { | 270 if (expected != actual) { |
48 throw new Error('Test Error. Actual: ' + actual + '\nExpected: ' + | 271 throw new Error('Test Error. Actual: ' + actual + '\nExpected: ' + |
49 expected + '\n' + message); | 272 expected + '\n' + message); |
50 } | 273 } |
51 if (typeof expected != typeof actual) { | 274 if (typeof expected != typeof actual) { |
52 throw new Error('Test Error' + | 275 throw new Error('Test Error' + |
53 ' (type mismatch)\nActual Type: ' + typeof actual + | 276 ' (type mismatch)\nActual Type: ' + typeof actual + |
54 '\nExpected Type:' + typeof expected + '\n' + message); | 277 '\nExpected Type:' + typeof expected + '\n' + message); |
55 } | 278 } |
56 } | 279 } |
57 | 280 |
| 281 /** |
| 282 * Always aborts the current test. |
| 283 * @param {String=} message The message to include in the Error thrown. |
| 284 * @throws {Error} always. |
| 285 **/ |
58 function assertNotReached(message) { | 286 function assertNotReached(message) { |
59 throw new Error(message); | 287 throw new Error(message); |
60 } | 288 } |
61 | 289 |
| 290 /** |
| 291 * Holds the errors, if any, caught by expects so that the test case can fail. |
| 292 * @type {Array.<Error>} |
| 293 **/ |
62 var errors = []; | 294 var errors = []; |
63 | 295 |
| 296 /** |
| 297 * Creates a function based upon a function that thows an exception on |
| 298 * failure. The new function stuffs any errors into the |errors| array for |
| 299 * checking by runTest. This allows tests to continue running other checks, |
| 300 * while failing the overal test if any errors occurrred. |
| 301 * @param {Function} assertFunc The function which may throw an Error. |
| 302 * @return {Function} A function that applies its arguments to |assertFunc|. |
| 303 * @see errors |
| 304 * @see runTest |
| 305 **/ |
64 function createExpect(assertFunc) { | 306 function createExpect(assertFunc) { |
65 return function() { | 307 return function() { |
66 try { | 308 try { |
67 assertFunc.apply(null, arguments); | 309 assertFunc.apply(null, arguments); |
68 } catch (e) { | 310 } catch (e) { |
69 console.log('Failed: ' + currentTest.name + '\n' + e.stack); | |
70 errors.push(e); | 311 errors.push(e); |
71 } | 312 } |
72 }; | 313 }; |
73 } | 314 } |
74 | 315 |
| 316 /** |
| 317 * This is the starting point for tests run by WebUIBrowserTest. It clears |
| 318 * |errors|, runs the test surrounded by an expect to catch Errors. If |
| 319 * |errors| is non-empty, it reports a failure and a message by joining |
| 320 * |errors|. |
| 321 * @param {String} testFunction The function name to call. |
| 322 * @param {Array} testArguments The arguments to call |testFunction| with. |
| 323 * @return {Array.<Boolean, String>} [test-succeeded, message-if-failed] |
| 324 * @see errors |
| 325 * @see createExpect |
| 326 **/ |
75 function runTest(testFunction, testArguments) { | 327 function runTest(testFunction, testArguments) { |
76 errors = []; | 328 errors = []; |
77 // Avoid eval() if at all possible, since it will not work on pages | 329 // Avoid eval() if at all possible, since it will not work on pages |
78 // that have enabled content-security-policy. | 330 // that have enabled content-security-policy. |
79 currentTest = this[testFunction]; // global object -- not a method. | 331 var testBody = this[testFunction]; // global object -- not a method. |
80 if (typeof currentTest === "undefined") { | 332 if (typeof testBody === "undefined") |
81 currentTest = eval(testFunction); | 333 testBody = eval(testFunction); |
82 } | 334 if (testBody != RUN_TEST_F) |
83 console.log('Running test ' + currentTest.name); | 335 console.log('Running test ' + testBody.name); |
84 createExpect(currentTest).apply(null, testArguments); | 336 createExpect(testBody).apply(null, testArguments); |
85 | 337 |
86 if (errors.length) { | 338 if (errors.length) { |
| 339 for (var i = 0; i < errors.length; ++i) { |
| 340 console.log('Failed: ' + testFunction + '(' + |
| 341 testArguments.toString() + ')\n' + errors[i].stack); |
| 342 } |
87 return [false, errors.join('\n')]; | 343 return [false, errors.join('\n')]; |
88 } | 344 } else { |
89 | 345 return [true]; |
90 return [true]; | 346 } |
91 } | 347 } |
92 | 348 |
93 function preloadJavascriptLibraries(overload_chrome_send) { | 349 /** |
94 if (overload_chrome_send) | 350 * Creates a new test case for the given |testFixture| and |testName|. Assumes |
95 chrome = { 'send': send }; | 351 * |testFixture| describes a globally available subclass of type Test. |
| 352 * @param {String} testFixture The fixture for this test case. |
| 353 * @param {String} testName The name for this test case. |
| 354 * @return {TestCase} A newly created TestCase. |
| 355 **/ |
| 356 function createTestCase(testFixture, testName) { |
| 357 var fixtureConstructor = this[testFixture]; |
| 358 var testBody = fixtureConstructor.testCaseBodies[testName]; |
| 359 var fixture = new fixtureConstructor(); |
| 360 fixture.name = testFixture; |
| 361 return new TestCase(testName, fixture, testBody); |
| 362 } |
| 363 |
| 364 /** |
| 365 * Used by WebUIBrowserTest to preload the javascript libraries at the |
| 366 * appropriate time for javascript injection into the current page. This |
| 367 * creates a test case and calls its PreLoad for any early initialization such |
| 368 * as registering handlers before the page's javascript runs it's OnLoad |
| 369 * method. |
| 370 * @param {String} testFixture The test fixture name. |
| 371 * @param {String} testName The test name. |
| 372 **/ |
| 373 function preloadJavascriptLibraries(testFixture, testName) { |
| 374 chrome = { |
| 375 __proto__: oldChrome, |
| 376 send: send, |
| 377 }; |
| 378 currentTestCase = createTestCase(testFixture, testName); |
| 379 currentTestCase.PreLoad(); |
| 380 } |
| 381 |
| 382 /** |
| 383 * During generation phase, this outputs; do nothing at runtime. |
| 384 **/ |
| 385 function GEN() {} |
| 386 |
| 387 /** |
| 388 * At runtime, register the testName with a test fixture. Since this method |
| 389 * doesn't have a test fixture, we create a dummy fixture to hold its |name| |
| 390 * and |testCaseBodies|. |
| 391 * @param {String} testCaseName The name of the test case. |
| 392 * @param {String} testName The name of the test function. |
| 393 * @param {Function} testBody The body to execute when running this test. |
| 394 **/ |
| 395 function TEST(testCaseName, testName, testBody) { |
| 396 if (this[testCaseName] === undefined) { |
| 397 var fixtureConstructor = new function() {}; |
| 398 this[testCaseName] = fixtureConstructor; |
| 399 fixtureConstructor.prototype.name = testCaseName; |
| 400 fixtureConstructor.testCaseBodies = {}; |
| 401 } |
| 402 fixtureConstructor.testCaseBodies[testName] = testBody; |
| 403 } |
| 404 |
| 405 /** |
| 406 * At runtime, register the testName with its fixture. Stuff the |name| into |
| 407 * the |testFixture|'s prototype, if needed, and the |testCaseBodies| into its |
| 408 * constructor. |
| 409 * @param {String} testFixture The name of the test fixture class. |
| 410 * @param {String} testName The name of the test function. |
| 411 * @param {Function} testBody The body to execute when running this test. |
| 412 **/ |
| 413 function TEST_F(testFixture, testName, testBody) { |
| 414 var fixtureConstructor = this[testFixture]; |
| 415 if (!fixtureConstructor.prototype.name) |
| 416 fixtureConstructor.prototype.name = testFixture; |
| 417 if (fixtureConstructor['testCaseBodies'] === undefined) |
| 418 fixtureConstructor.testCaseBodies = {}; |
| 419 fixtureConstructor.testCaseBodies[testName] = testBody; |
| 420 } |
| 421 |
| 422 /** |
| 423 * RunJavascriptTestF uses this as the |testFunction| when invoking |
| 424 * runTest. If |currentTestCase| is non-null at this point, verify that |
| 425 * |testFixture| and |testName| agree with the preloaded values. Create |
| 426 * |currentTestCase|, if needed, run it, and clear the |currentTestCase|. |
| 427 * @param {String} testFixture The name of the test fixture class. |
| 428 * @param {String} testName The name of the test function. |
| 429 * @see preloadJavascriptLibraries |
| 430 * @see runTest |
| 431 **/ |
| 432 function RUN_TEST_F(testFixture, testName) { |
| 433 if (!currentTestCase) |
| 434 currentTestCase = createTestCase(testFixture, testName); |
| 435 assertEquals(currentTestCase.name, testName); |
| 436 assertEquals(currentTestCase.fixture.name, testFixture); |
| 437 console.log('Running TestCase ' + testFixture + '.' + testName); |
| 438 currentTestCase.Run(); |
| 439 currentTestCase = null; |
| 440 } |
| 441 |
| 442 /** |
| 443 * CallFunctionAction is provided to allow mocks to have side effects. |
| 444 * @param {Function} func The function to call. |
| 445 * @param {Array} args Any arguments to pass to func. |
| 446 * @constructor |
| 447 **/ |
| 448 function CallFunctionAction(func, args) { |
| 449 this._func = func; |
| 450 this._args = args; |
| 451 } |
| 452 |
| 453 CallFunctionAction.prototype = { |
| 454 invoke: function() { |
| 455 return this._func.apply(null, this._args); |
| 456 }, |
| 457 describe: function() { |
| 458 return 'calls the given function with arguments ' + this._args; |
| 459 } |
| 460 }; |
| 461 |
| 462 /** |
| 463 * Syntactic sugar for will() on a Mock4JS.Mock. |
| 464 * @param {Function} func the function to call when the method is invoked. |
| 465 * @param {...*} var_args arguments to pass when calling func. |
| 466 **/ |
| 467 function callFunction(func) { |
| 468 return new CallFunctionAction(func, |
| 469 Array.prototype.slice.call(arguments, 1)); |
96 } | 470 } |
97 | 471 |
98 // Exports. | 472 // Exports. |
| 473 testing.Test = Test; |
99 window.assertTrue = assertTrue; | 474 window.assertTrue = assertTrue; |
100 window.assertFalse = assertFalse; | 475 window.assertFalse = assertFalse; |
101 window.assertEquals = assertEquals; | 476 window.assertEquals = assertEquals; |
102 window.assertNotReached = assertNotReached; | 477 window.assertNotReached = assertNotReached; |
| 478 window.callFunction = callFunction; |
103 window.expectTrue = createExpect(assertTrue); | 479 window.expectTrue = createExpect(assertTrue); |
104 window.expectFalse = createExpect(assertFalse); | 480 window.expectFalse = createExpect(assertFalse); |
105 window.expectEquals = createExpect(assertEquals); | 481 window.expectEquals = createExpect(assertEquals); |
106 window.expectNotReached = createExpect(assertNotReached); | 482 window.expectNotReached = createExpect(assertNotReached); |
107 window.registerMessageCallback = registerMessageCallback; | 483 window.registerMessageCallback = registerMessageCallback; |
| 484 window.registerMockMessageCallbacks = registerMockMessageCallbacks; |
108 window.runTest = runTest; | 485 window.runTest = runTest; |
109 window.preloadJavascriptLibraries = preloadJavascriptLibraries; | 486 window.preloadJavascriptLibraries = preloadJavascriptLibraries; |
| 487 window.TEST = TEST; |
| 488 window.TEST_F = TEST_F; |
| 489 window.GEN = GEN; |
| 490 |
| 491 // Import the Mock4JS helpers. |
| 492 Mock4JS.addMockSupport(window); |
110 })(); | 493 })(); |
OLD | NEW |