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