Index: chrome/test/data/webui/test_api.js |
diff --git a/chrome/test/data/webui/test_api.js b/chrome/test/data/webui/test_api.js |
index 4e554bd8722b8d8d7051966d576dba1cdbb20ed3..ba99df67131b0d57313ea0a693b0c683f37aa921 100644 |
--- a/chrome/test/data/webui/test_api.js |
+++ b/chrome/test/data/webui/test_api.js |
@@ -99,12 +99,25 @@ var currentTestArguments = null; |
/** |
* This should be initialized by the test fixture and can be referenced |
- * during the test run. |
- * @type {Mock4JS.Mock} |
+ * during the test run. It holds any mocked handler methods. |
+ * @type {?Mock4JS.Mock} |
**/ |
mockHandler: null, |
/** |
+ * This should be initialized by the test fixture and can be referenced |
+ * during the test run. It holds any mocked global functions. |
+ * @type {?Mock4JS.Mock} |
+ **/ |
+ mockGlobals: null, |
+ |
+ /** |
+ * Value is passed through call to C++ RunJavascriptF to invoke this test. |
+ * @type {boolean} |
+ **/ |
+ isAsync: false, |
+ |
+ /** |
* Override this method to perform initialization during preload (such as |
* creating mocks and registering handlers). |
* @type {Function} |
@@ -125,7 +138,42 @@ var currentTestArguments = null; |
**/ |
TearDown: function() { |
Mock4JS.verifyAllMocks(); |
- } |
+ }, |
+ |
+ /** |
+ * Called to run the body from the perspective of this fixture. |
+ * @type {Function} |
+ */ |
+ RunTest: function(testBody) { |
+ testBody.call(this); |
+ }, |
+ |
+ /** |
+ * @param {boolean} alwaysDone When true, always call testDone(). |
+ * @param {Function} completion The function to call to complete the test. |
+ * @return {function(): void} Return a function, bound to this test fixture, |
+ * which continues the test. |
+ **/ |
+ continueTest: function(alwaysDone, completion) { |
+ var completionAction = new CallFunctionAction( |
+ this, null, completion, null); |
+ var runAll = new RunAllAction( |
+ true, alwaysDone, [completionAction]); |
+ return function() { |
+ runAll.invoke(); |
+ }; |
+ }, |
+ |
+ /** |
+ * Call this during SetUp to defer the call to RunTest() until later. The |
+ * caller must call the returned function at some point to run the test. |
+ * @type {Function} |
+ * @return {function(): void} A function which will run the current body of |
+ * the currentTestCase. |
+ */ |
+ deferRunTest: function() { |
+ return currentTestCase.deferRunTest(); |
+ }, |
}; |
/** |
@@ -143,11 +191,32 @@ var currentTestArguments = null; |
} |
TestCase.prototype = { |
+ /** |
+ * The name of this test. |
+ * @type {string} |
+ */ |
name: null, |
+ |
+ /** |
+ * The test fixture to set |this| to when running the test |body|. |
+ * @type {testing.Test} |
+ */ |
fixture: null, |
+ |
+ /** |
+ * The test body to execute in RunTest(). |
+ * @type {Function} |
+ **/ |
body: null, |
/** |
+ * True when the test fixture will run the test later. |
+ * @type {boolean} |
+ * @private |
+ */ |
+ deferred_: false, |
+ |
+ /** |
* Called at preload time, proxies to the fixture. |
* @type {Function} |
**/ |
@@ -157,6 +226,30 @@ var currentTestArguments = null; |
}, |
/** |
+ * Called before a test runs. |
+ **/ |
+ SetUp: function() { |
+ if (this.fixture) |
+ this.fixture.SetUp(); |
+ }, |
+ |
+ /** |
+ * Called before a test is torn down (by testDone()). |
+ **/ |
+ TearDown: function() { |
+ if (this.fixture) |
+ this.fixture.TearDown(); |
+ }, |
+ |
+ /** |
+ * Called to run this test's body. |
+ **/ |
+ RunTest: function() { |
+ if (this.body && this.fixture) |
+ this.fixture.RunTest(this.body); |
+ }, |
+ |
+ /** |
* Runs this test case with |this| set to the |fixture|. |
* |
* Note: Tests created with TEST_F may depend upon |this| being set to an |
@@ -166,13 +259,36 @@ var currentTestArguments = null; |
* @type {Function} |
**/ |
Run: function() { |
- if (this.fixture) |
- this.fixture.SetUp(); |
- if (this.body) |
- this.body.call(this.fixture); |
- if (this.fixture) |
- this.fixture.TearDown(); |
+ try { |
+ this.SetUp(); |
+ } catch(e) { |
+ console.error(e.stack); |
+ } |
+ |
+ if (!this.deferred_) |
+ this.RunTest(); |
+ |
+ // TearDown called by testDone(). |
+ }, |
+ |
+ /** |
+ * Cause this TestCase to be deferred (don't call RunTest()) until the |
+ * returned function is called. |
+ * @type {Function} |
+ * @return {function(): void} A function thatwill run this TestCase when |
+ * called. |
+ */ |
+ deferRunTest: function() { |
+ this.deferred_ = true; |
+ var completionAction = new CallFunctionAction( |
+ this, null, this.RunTest, null); |
+ var runAll = new RunAllAction( |
+ true, true, [completionAction]); |
+ return function() { |
+ runAll.invoke(); |
+ }; |
}, |
+ |
}; |
/** |
@@ -184,7 +300,7 @@ var currentTestArguments = null; |
/** |
* Registers the message, object and callback for {@code chrome.send} |
* @param {string} name The name of the message to route to this |callback|. |
- * @param {Object} messageHAndler Pass as |this| when calling the |callback|. |
+ * @param {Object} messageHandler Pass as |this| when calling the |callback|. |
* @param {function(...)} callback Called by {@code chrome.send}. |
* @see sendCallbacks |
**/ |
@@ -202,11 +318,46 @@ var currentTestArguments = null; |
**/ |
function registerMockMessageCallbacks(mockObject, mockClass) { |
var mockProxy = mockObject.proxy(); |
- for (func in mockClass.prototype) { |
+ for (var func in mockClass.prototype) { |
if (typeof(mockClass.prototype[func]) == 'function') { |
- registerMessageCallback(func, |
- mockProxy, |
- mockProxy[func]); |
+ registerMessageCallback(func, mockProxy, mockProxy[func]); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Holds the mapping of name -> global override information. |
+ * @type {Object} |
+ **/ |
+ var globalOverrides = {}; |
+ |
+ /** |
+ * Registers the global function name, object and callback. |
+ * @param {string} name The name of the message to route to this |callback|. |
+ * @param {Object} object Pass as |this| when calling the |callback|. |
+ * @param {function(...)} callback Called by {@code chrome.send}. |
+ **/ |
+ function registerMockGlobal(name, object, callback) { |
+ assertEquals(undefined, globalOverrides[name]); |
+ globalOverrides[name] = { |
+ object: object, |
+ callback: callback, |
+ }; |
+ } |
+ |
+ /** |
+ * Register all methods of {@code mockClass.prototype} as overrides to global |
+ * functions of the same name as the method, using the proxy of the |
+ * |mockObject| to handle the functions. |
+ * @param {Mock4JS.Mock} mockObject The mock to register callbacks against. |
+ * @param {function(new:Object)} mockClAss Constructor for the mocked class. |
+ * @see registerMessageCallback |
+ **/ |
+ function registerMockGlobals(mockObject, mockClass) { |
+ var mockProxy = mockObject.proxy(); |
+ for (var func in mockClass.prototype) { |
+ if (typeof(mockClass.prototype[func]) == 'function') { |
arv (Not doing code reviews)
2011/08/22 20:57:18
typeof should not have parentheses
Sheridan Rawlins
2011/08/23 01:06:15
Done.
|
+ registerMockGlobal(func, mockProxy, mockProxy[func]); |
} |
} |
} |
@@ -348,6 +499,10 @@ var currentTestArguments = null; |
function testDone(result) { |
if (!testIsDone) { |
testIsDone = true; |
+ if (currentTestCase) { |
+ currentTestCase.TearDown(); |
+ currentTestCase = null; |
+ } |
chrome.send('testResult', result ? result : testResult()); |
errors.splice(0, errors.length); |
} else { |
@@ -616,6 +771,16 @@ var currentTestArguments = null; |
__proto__: oldChrome, |
send: send, |
}; |
+ |
+ // Override globals at load time so they will be defined. |
+ for (var func in globalOverrides) { |
+ assertNotEquals(undefined, this[func]); |
+ var globalOverride = globalOverrides[func]; |
+ assertNotEquals(undefined, globalOverride); |
+ assertEquals(undefined, globalOverride.original); |
+ globalOverride.original = this[func]; |
+ this[func] = globalOverride.callback.bind(globalOverride.object); |
+ } |
}); |
currentTestCase = createTestCase(testFixture, testName); |
currentTestCase.PreLoad(); |
@@ -676,92 +841,315 @@ var currentTestArguments = null; |
* @see runTest |
**/ |
function RUN_TEST_F(testFixture, testName) { |
- try { |
- if (!currentTestCase) |
- currentTestCase = createTestCase(testFixture, testName); |
- assertEquals(currentTestCase.name, testName); |
- assertEquals(currentTestCase.fixture.name, testFixture); |
- console.log('Running TestCase ' + testFixture + '.' + testName); |
- currentTestCase.Run(); |
- } finally { |
- currentTestCase = null; |
- } |
+ if (!currentTestCase) |
+ currentTestCase = createTestCase(testFixture, testName); |
+ assertEquals(currentTestCase.name, testName); |
+ assertEquals(currentTestCase.fixture.name, testFixture); |
+ console.log('Running TestCase ' + testFixture + '.' + testName); |
+ currentTestCase.Run(); |
} |
/** |
+ * Matcher which pushes the |actualArgument| of argumentMatches() onto |
+ * |arguments|, then proxies the return result to the |realMatcher|. Because |
+ * |realMatcher| can be any type, this constructor overrides argumentMatches, |
+ * then forces |this| to be an instance of |realMatcher|. |
mmenke
2011/08/22 16:26:44
You're defining a class in terms of one of its met
Sheridan Rawlins
2011/08/23 01:06:15
I rewrote this with composition instead of hacky i
|
+ * @param {Array} arguments The arguments to push |actualArgument| onto. |
+ * @param {Object} realMatcher The real matcher check arguments with. |
+ * @constructor |
+ * @extends {realMatcher} |
arv (Not doing code reviews)
2011/08/22 20:57:18
This annotation is a bit strange
Sheridan Rawlins
2011/08/23 01:06:15
See new version.
On 2011/08/22 20:57:18, arv wrot
|
+ **/ |
+ function SaveArgumentMatcher(arguments, realMatcher) { |
+ this.arguments_ = arguments; |
+ this.argumentMatches = this.argumentMatches; |
mmenke
2011/08/22 16:26:44
?
Sheridan Rawlins
2011/08/23 01:06:15
Yeah, this was weird - it could have been this.arg
|
+ this.__proto__ = realMatcher; |
arv (Not doing code reviews)
2011/08/22 20:57:18
This seems a bit hacky. How about?
var obj = Obje
Sheridan Rawlins
2011/08/23 01:06:15
See new version with composition instead of the ha
|
+ } |
+ |
+ SaveArgumentMatcher.prototype = { |
+ /** |
+ * Holds the arguments to push each |actualArgument| onto. |
+ * @type {Array} |
+ * @private |
+ **/ |
+ arguments_: null, |
+ |
+ /** |
+ * Pushes |actualArgument| onto |arguments_| for later use. Clears |
+ * |arguments_| on non-match. |
+ * @param {*} actualArgument The argument to match and save. |
+ * @return {boolean} value of calling the |realMatcher|. |
+ * @override |
+ **/ |
+ argumentMatches: function(actualArgument) { |
+ this.arguments_.push(actualArgument); |
+ var match = this.__proto__.argumentMatches(actualArgument); |
+ if (!match) |
+ this.arguments_.splice(0, this.arguments_.length); |
+ |
+ return match; |
+ }, |
+ }; |
+ |
+ /** |
+ * Allow mock stubs() and expects() to know what arguments were passed to the |
+ * |realMatcher|. |
mmenke
2011/08/22 16:26:44
This isn't terribly illuminating for someone tryin
Sheridan Rawlins
2011/08/23 01:06:15
OK. Added more context to comments.
On 2011/08/2
|
+ * @param {!Object} realMatcher The real matcher to perform matching with. |
+ * @constructor |
+ **/ |
+ function SaveMockArguments() { |
+ this.arguments = []; |
+ } |
+ |
+ SaveMockArguments.prototype = { |
+ /** |
+ * Wraps the |realMatcher| with an object which will push its argument onto |
+ * |arguments| and call realMatcher. |
+ * @param {Object} realMatcher A Mock4JS matcher object for this argument. |
+ * @return {SaveArgumentMatcher} A new matcher which will push its argument |
+ * onto |arguments|. |
+ **/ |
+ match: function(realMatcher) { |
+ return new SaveArgumentMatcher(this.arguments, realMatcher); |
+ }, |
+ |
+ /** |
+ * Remember the argument passed to this stub invocation. |
+ * @type {Array} |
+ **/ |
+ arguments: null, |
+ }; |
+ |
+ /** |
* CallFunctionAction is provided to allow mocks to have side effects. |
+ * @param {Object} obj The object to set |this| to when calling |func_|. |
+ * @param {?SaveMockArguments} savedArgs when non-null, saved arguments are |
+ * passed to |func|. |
* @param {Function} func The function to call. |
* @param {Array} args Any arguments to pass to func. |
* @constructor |
**/ |
- function CallFunctionAction(func, args) { |
- this._func = func; |
- this._args = args; |
+ function CallFunctionAction(obj, savedArgs, func, args) { |
+ this.obj_ = obj; |
+ this.savedArgs_ = savedArgs; |
+ this.func_ = func; |
+ this.args_ = args; |
} |
CallFunctionAction.prototype = { |
+ /** |
+ * Set |this| to |obj_| when calling |func_|. |
+ * @type {?Object} |
+ **/ |
+ obj_: null, |
+ |
+ /** |
+ * The SaveMockArguments to hold arguments when invoking |func_|. |
+ * @type {?SaveMockArguments} |
+ * @private |
+ **/ |
+ savedArgs_: null, |
+ |
+ /** |
+ * The function to call when invoked. |
+ * @type {!Function} |
+ * @private |
+ **/ |
+ func_: null, |
+ |
+ /** |
+ * Arguments to pass to |func_| when invoked. |
+ * @type {!Array} |
+ **/ |
+ args_: null, |
+ |
+ /** |
+ * Accessor for |func_|. |
+ * @return {Function} The function to invoke. |
+ **/ |
+ get func() { |
+ return this.func_; |
+ }, |
+ |
+ /** |
+ * Called by Mock4JS when using .will() to specify actions for stubs() or |
+ * expects(). Clears |savedArgs_| so it can be reused. |
+ * @return The results of calling |func_| with the concatenation of |
+ * |savedArgs_| and |args_|. |
+ **/ |
invoke: function() { |
- return this._func.apply(null, this._args); |
+ var prependArgs = []; |
+ if (this.savedArgs_) { |
+ prependArgs = this.savedArgs_.arguments.splice( |
+ 0, this.savedArgs_.arguments.length); |
+ } |
+ return this.func.apply(this.obj_, prependArgs.concat(this.args_)); |
}, |
+ |
+ /** |
+ * Describe this action to Mock4JS. |
+ * @return {string} A description of this action. |
+ **/ |
describe: function() { |
- return 'calls the given function with arguments ' + this._args; |
+ return 'calls the given function with saved arguments and ' + this.args_; |
} |
}; |
/** |
* Syntactic sugar for will() on a Mock4JS.Mock. |
mmenke
2011/08/22 16:26:44
nit: "for use with" make it clearer that this sho
Sheridan Rawlins
2011/08/23 01:06:15
Done.
|
- * @param {Function} func the function to call when the method is invoked. |
- * @param {...*} var_args arguments to pass when calling func. |
+ * @param {Function} func The function to call when the method is invoked. |
+ * @param {...*} var_args Arguments to pass when calling func. |
+ * @return {CallFunctionAction} Action for use in will. |
**/ |
function callFunction(func) { |
- return new CallFunctionAction(func, |
- Array.prototype.slice.call(arguments, 1)); |
+ return new CallFunctionAction( |
+ null, null, func, Array.prototype.slice.call(arguments, 1)); |
} |
/** |
- * Allow mock stubs() and expects() to know what arguments were passed to the |
- * |realMatcher|. |
- * @param {!Object} realMatcher The real matcher to perform matching with. |
+ * Syntactic sugar for will() on a Mock4JS.Mock. |
mmenke
2011/08/22 16:26:44
nit: "for use with"
Sheridan Rawlins
2011/08/23 01:06:15
Done.
|
+ * @param {SaveMockArguments} savedArgs Arguments saved with this object |
+ * are passed to |func|. |
+ * @param {Function} func The function to call when the method is invoked. |
+ * @param {...*} var_args Arguments to pass when calling func. |
+ * @return {CallFunctionAction} Action for use in will. |
+ **/ |
+ function callFunctionWithSavedArgs(savedArgs, func) { |
+ return new CallFunctionAction( |
+ null, savedArgs, func, Array.prototype.slice.call(arguments, 2)); |
+ } |
+ |
+ /** |
+ * CallGlobalAction is provided to help override global function with mocking |
+ * expectations and still call the global function. |
mmenke
2011/08/22 16:26:44
nit: Say what it does, not just why it exists.
Sheridan Rawlins
2011/08/23 01:06:15
Done.
|
+ * @param {?SaveMockArguments} savedArgs when non-null, saved arguments are |
+ * passed to the global function |funcName|. |
+ * @param {Function} funcName The name of the global function to call. |
+ * @param {Array} args Any arguments to pass to func. |
* @constructor |
+ * @extends {CallFunctionAction} |
+ * @see globalOverrides |
**/ |
- function SaveArgumentsMatcher(realMatcher) { |
- this.realMatcher_ = realMatcher; |
+ function CallGlobalAction(savedArgs, funcName, args) { |
+ CallFunctionAction.call(this, null, savedArgs, funcName, args); |
} |
- SaveArgumentsMatcher.prototype = { |
+ CallGlobalAction.prototype = { |
+ __proto__: CallFunctionAction.prototype, |
+ |
/** |
- * Remember the argument passed to this stub invocation. |
- * @type {*} |
+ * Fetch and return the original global function to call. |
+ * @return {Function} The global function to invoke. |
+ * @override |
**/ |
- argument: undefined, |
+ get func() { |
+ var func = globalOverrides[this.func_].original; |
+ assertNotEquals(undefined, func); |
+ return func; |
+ }, |
+ }; |
+ /** |
+ * Syntactic sugar for will() on a Mock4JS.Mock. |
mmenke
2011/08/22 16:26:44
nit: For use with.
Sheridan Rawlins
2011/08/23 01:06:15
Done.
|
+ * @param {SaveMockArguments} savedArgs Arguments saved with this object |
+ * are passed to the global function |funcName|. |
+ * @param {Function} funcName The name of the global function to call when the |
+ * method is invoked. |
mmenke
2011/08/22 16:26:44
nit: Name of a registered mock global function.
Sheridan Rawlins
2011/08/23 01:06:15
Done.
|
+ * @param {...*} var_args Arguments to pass when calling func. |
+ * @return {CallGlobalAction} Action for use in will. |
+ **/ |
+ function callGlobalWithSavedArgs(savedArgs, funcName) { |
+ return new CallGlobalAction( |
+ savedArgs, funcName, Array.prototype.slice.call(arguments, 2)); |
+ } |
+ |
+ /** |
+ * Runs all |actions|. |
+ * @param {boolean} isAsync When true, call testDone() on Errors. |
+ * @param {boolean} alwaysDone When true, always call testDone(). |
+ * @param {Array.<Object>} actions Actions to run. |
+ * @constructor |
+ **/ |
+ function RunAllAction(isAsync, alwaysDone, actions) { |
+ this.isAsync_ = isAsync; |
+ this.alwaysDone_ = alwaysDone; |
+ this.actions_ = actions; |
+ } |
+ |
+ RunAllAction.prototype = { |
/** |
- * @type {Object} the object performing the real match. |
+ * When true, call testDone() on Errors. |
+ * @type {boolean} |
* @private |
- */ |
- realMatcher_: null, |
+ **/ |
+ isAsync_: false, |
/** |
- * Saves |actualArgument| for later use by the mock stub or expect. |
- * @param {*} actualArgument The argument to match and save. |
- * @return {boolean} value of calling the |realMatcher_|. |
+ * When true, always call testDone(). |
+ * @type {boolean} |
+ * @private |
**/ |
- argumentMatches: function(actualArgument) { |
- this.argument = actualArgument; |
- return this.realMatcher_.argumentMatches.call(this.realMatcher_, |
- actualArgument); |
+ alwaysDone_: false, |
+ |
+ /** |
+ * Holds the actions to execute when invoked. |
+ * @type {Array} |
+ * @private |
+ **/ |
+ actions_: null, |
+ |
+ /** |
+ * Runs all |actions_|, returning the last one. |
+ **/ |
+ invoke: function() { |
+ try { |
+ var result; |
+ for(var i = 0; i < this.actions_.length; ++i) { |
+ result = this.actions_[i].invoke.apply( |
+ this.actions_[i], Array.prototype.slice.call(arguments)); |
+ } |
+ if (this.alwaysDone_) |
+ testDone(); |
+ |
+ return result; |
+ } catch (e) { |
+ if (!this.isAsync_) |
+ throw e; |
+ |
+ errors.push(e); |
+ testDone(); |
mmenke
2011/08/22 16:26:44
You should also call testDone on expect failures,
Sheridan Rawlins
2011/08/23 01:06:15
I don't think so. There should be a distinction be
mmenke
2011/08/23 01:36:09
Then should probably add a way to tell if there's
Sheridan Rawlins
2011/08/23 05:52:18
Ok. I added a whenTestDone enum to describe when
mmenke
2011/08/23 15:26:29
Seems reasonable to me.
|
+ } |
}, |
/** |
- * Generic description for this Matcher object. |
- * @return {string} description of the matcher for this argument. |
+ * Describe this action to Mock4JS. |
+ * @return {string} A description of this action. |
**/ |
describe: function() { |
- return 'SaveArguments(' + |
- this.realMatcher_.describe.call(this.realMatcher_) + ')'; |
+ return 'Calls all actions: ' + this.actions_; |
}, |
}; |
+ /** |
+ * Syntactic sugar for will() on a Mock4JS.Mock. |
+ * @param {...Object} var_actions Actions to run. |
+ * @return {RunAllAction} Action for use in will. |
+ **/ |
+ function runAllActions() { |
+ return new RunAllAction(false, false, |
+ Array.prototype.slice.call(arguments)); |
+ } |
+ |
+ /** |
+ * Syntactic sugar for will() on a Mock4JS.Mock. |
+ * @param {boolean} alwaysDone When true, always call testDone() otherwise, |
+ * call it only when Errors are thrown (from asserts). |
+ * @return {RunAllAction} Action for use in will. |
+ **/ |
+ function runAllActionsAsync(alwaysDone) { |
+ return new RunAllAction(true, alwaysDone, |
+ Array.prototype.slice.call(arguments)); |
+ } |
+ |
// Exports. |
testing.Test = Test; |
window.testDone = testDone; |
@@ -775,6 +1163,8 @@ var currentTestArguments = null; |
window.assertNotEquals = assertNotEquals; |
window.assertNotReached = assertNotReached; |
window.callFunction = callFunction; |
+ window.callFunctionWithSavedArgs = callFunctionWithSavedArgs; |
+ window.callGlobalWithSavedArgs = callGlobalWithSavedArgs; |
window.expectTrue = createExpect(assertTrue); |
window.expectFalse = createExpect(assertFalse); |
window.expectGE = createExpect(assertGE); |
@@ -786,11 +1176,14 @@ var currentTestArguments = null; |
window.expectNotReached = createExpect(assertNotReached); |
window.preloadJavascriptLibraries = preloadJavascriptLibraries; |
window.registerMessageCallback = registerMessageCallback; |
+ window.registerMockGlobals = registerMockGlobals; |
window.registerMockMessageCallbacks = registerMockMessageCallbacks; |
window.resetTestState = resetTestState; |
+ window.runAllActions = runAllActions; |
+ window.runAllActionsAsync = runAllActionsAsync; |
window.runTest = runTest; |
window.runTestFunction = runTestFunction; |
- window.SaveArgumentsMatcher = SaveArgumentsMatcher; |
+ window.SaveMockArguments = SaveMockArguments; |
window.TEST = TEST; |
window.TEST_F = TEST_F; |
window.GEN = GEN; |