blob: 794ba25efcca9456caf4f08cb9f2b1c94a7fd0c3 [file] [log] [blame]
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// All Rights Reserved.
/**
* @fileoverview A class representing a set of test functions that use
* asynchronous functions that cannot be meaningfully mocked.
*
* To create a Google-compatable JsUnit test using this test case, put the
* following snippet in your test:
*
* var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
*
* To make the test runner wait for your asynchronous behaviour, use:
*
* asyncTestCase.waitForAsync('Waiting for xhr to respond');
*
* The next test will not start until the following call is made, or a
* timeout occurs:
*
* asyncTestCase.continueTesting();
*
* There does NOT need to be a 1:1 mapping of waitForAsync calls and
* continueTesting calls. The next test will be run after a single call to
* continueTesting is made, as long as there is no subsequent call to
* waitForAsync in the same thread.
*
* Example:
* // Returning here would cause the next test to be run.
* asyncTestCase.waitForAsync('description 1');
* // Returning here would *not* cause the next test to be run.
* // Only effect of additional waitForAsync() calls is an updated
* // description in the case of a timeout.
* asyncTestCase.waitForAsync('updated description');
* asyncTestCase.continueTesting();
* // Returning here would cause the next test to be run.
* asyncTestCase.waitForAsync('just kidding, still running.');
* // Returning here would *not* cause the next test to be run.
*
* The test runner can also be made to wait for more than one asynchronous
* event with:
*
* asyncTestCase.waitForSignals(n);
*
* The next test will not start until asyncTestCase.signal() is called n times,
* or the test step timeout is exceeded.
*
* This class supports asynchronous behaviour in all test functions except for
* tearDownPage. If such support is needed, it can be added.
*
* Example Usage:
*
* var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
* // Optionally, set a longer-than-normal step timeout.
* asyncTestCase.stepTimeout = 30 * 1000;
*
* function testSetTimeout() {
* var step = 0;
* function stepCallback() {
* step++;
* switch (step) {
* case 1:
* var startTime = goog.now();
* asyncTestCase.waitForAsync('step 1');
* window.setTimeout(stepCallback, 100);
* break;
* case 2:
* assertTrue('Timeout fired too soon',
* goog.now() - startTime >= 100);
* asyncTestCase.waitForAsync('step 2');
* window.setTimeout(stepCallback, 100);
* break;
* case 3:
* assertTrue('Timeout fired too soon',
* goog.now() - startTime >= 200);
* asyncTestCase.continueTesting();
* break;
* default:
* fail('Unexpected call to stepCallback');
* }
* }
* stepCallback();
* }
*
* Known Issues:
* IE7 Exceptions:
* As the failingtest.html will show, it appears as though ie7 does not
* propagate an exception past a function called using the func.call()
* syntax. This causes case 3 of the failing tests (exceptions) to show up
* as timeouts in IE.
* window.onerror:
* This seems to catch errors only in ff2/ff3. It does not work in Safari or
* IE7. The consequence of this is that exceptions that would have been
* caught by window.onerror show up as timeouts.
*
* @author agrieve@google.com (Andrew Grieve)
*/
goog.provide('goog.testing.AsyncTestCase');
goog.provide('goog.testing.AsyncTestCase.ControlBreakingException');
goog.require('goog.testing.TestCase');
goog.require('goog.testing.asserts');
/**
* A test case that is capable of running tests the contain asynchronous logic.
* @param {string=} opt_name A descriptive name for the test case.
* @extends {goog.testing.TestCase}
* @constructor
*/
goog.testing.AsyncTestCase = function(opt_name) {
goog.testing.TestCase.call(this, opt_name);
};
goog.inherits(goog.testing.AsyncTestCase, goog.testing.TestCase);
/**
* Represents result of top stack function call.
* @typedef {{controlBreakingExceptionThrown: boolean, message: string}}
* @private
*/
goog.testing.AsyncTestCase.TopStackFuncResult_;
/**
* An exception class used solely for control flow.
* @param {string=} opt_message Error message.
* @constructor
* @extends {Error}
* @final
*/
goog.testing.AsyncTestCase.ControlBreakingException = function(opt_message) {
goog.testing.AsyncTestCase.ControlBreakingException.base(
this, 'constructor', opt_message);
/**
* The exception message.
* @type {string}
*/
this.message = opt_message || '';
};
goog.inherits(goog.testing.AsyncTestCase.ControlBreakingException, Error);
/**
* Return value for .toString().
* @type {string}
*/
goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING =
'[AsyncTestCase.ControlBreakingException]';
/**
* Marks this object as a ControlBreakingException
* @type {boolean}
*/
goog.testing.AsyncTestCase.ControlBreakingException.prototype.
isControlBreakingException = true;
/** @override */
goog.testing.AsyncTestCase.ControlBreakingException.prototype.toString =
function() {
// This shows up in the console when the exception is not caught.
return goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;
};
/**
* How long to wait for a single step of a test to complete in milliseconds.
* A step starts when a call to waitForAsync() is made.
* @type {number}
*/
goog.testing.AsyncTestCase.prototype.stepTimeout = 1000;
/**
* How long to wait after a failed test before moving onto the next one.
* The purpose of this is to allow any pending async callbacks from the failing
* test to finish up and not cause the next test to fail.
* @type {number}
*/
goog.testing.AsyncTestCase.prototype.timeToSleepAfterFailure = 500;
/**
* Turn on extra logging to help debug failing async. tests.
* @type {boolean}
* @private
*/
goog.testing.AsyncTestCase.prototype.enableDebugLogs_ = false;
/**
* A reference to the original asserts.js assert_() function.
* @private
*/
goog.testing.AsyncTestCase.prototype.origAssert_;
/**
* A reference to the original asserts.js fail() function.
* @private
*/
goog.testing.AsyncTestCase.prototype.origFail_;
/**
* A reference to the original window.onerror function.
* @type {Function|undefined}
* @private
*/
goog.testing.AsyncTestCase.prototype.origOnError_;
/**
* The stage of the test we are currently on.
* @type {Function|undefined}}
* @private
*/
goog.testing.AsyncTestCase.prototype.curStepFunc_;
/**
* The name of the stage of the test we are currently on.
* @type {string}
* @private
*/
goog.testing.AsyncTestCase.prototype.curStepName_ = '';
/**
* The stage of the test we should run next.
* @type {Function|undefined}
* @private
*/
goog.testing.AsyncTestCase.prototype.nextStepFunc;
/**
* The name of the stage of the test we should run next.
* @type {string}
* @private
*/
goog.testing.AsyncTestCase.prototype.nextStepName_ = '';
/**
* The handle to the current setTimeout timer.
* @type {number}
* @private
*/
goog.testing.AsyncTestCase.prototype.timeoutHandle_ = 0;
/**
* Marks if the cleanUp() function has been called for the currently running
* test.
* @type {boolean}
* @private
*/
goog.testing.AsyncTestCase.prototype.cleanedUp_ = false;
/**
* The currently active test.
* @type {goog.testing.TestCase.Test|undefined}
* @protected
*/
goog.testing.AsyncTestCase.prototype.activeTest;
/**
* A flag to prevent recursive exception handling.
* @type {boolean}
* @private
*/
goog.testing.AsyncTestCase.prototype.inException_ = false;
/**
* Flag used to determine if we can move to the next step in the testing loop.
* @type {boolean}
* @private
*/
goog.testing.AsyncTestCase.prototype.isReady_ = true;
/**
* Number of signals to wait for before continuing testing when waitForSignals
* is used.
* @type {number}
* @private
*/
goog.testing.AsyncTestCase.prototype.expectedSignalCount_ = 0;
/**
* Number of signals received.
* @type {number}
* @private
*/
goog.testing.AsyncTestCase.prototype.receivedSignalCount_ = 0;
/**
* Flag that tells us if there is a function in the call stack that will make
* a call to pump_().
* @type {boolean}
* @private
*/
goog.testing.AsyncTestCase.prototype.returnWillPump_ = false;
/**
* The number of times we have thrown a ControlBreakingException so that we
* know not to complain in our window.onerror handler. In Webkit, window.onerror
* is not supported, and so this counter will keep going up but we won't care
* about it.
* @type {number}
* @private
*/
goog.testing.AsyncTestCase.prototype.numControlExceptionsExpected_ = 0;
/**
* The current step name.
* @return {!string} Step name.
* @protected
*/
goog.testing.AsyncTestCase.prototype.getCurrentStepName = function() {
return this.curStepName_;
};
/**
* Preferred way of creating an AsyncTestCase. Creates one and initializes it
* with the G_testRunner.
* @param {string=} opt_name A descriptive name for the test case.
* @return {!goog.testing.AsyncTestCase} The created AsyncTestCase.
*/
goog.testing.AsyncTestCase.createAndInstall = function(opt_name) {
var asyncTestCase = new goog.testing.AsyncTestCase(opt_name);
goog.testing.TestCase.initializeTestRunner(asyncTestCase);
return asyncTestCase;
};
/**
* Informs the testcase not to continue to the next step in the test cycle
* until continueTesting is called.
* @param {string=} opt_name A description of what we are waiting for.
*/
goog.testing.AsyncTestCase.prototype.waitForAsync = function(opt_name) {
this.isReady_ = false;
this.curStepName_ = opt_name || this.curStepName_;
// Reset the timer that tracks if the async test takes too long.
this.stopTimeoutTimer_();
this.startTimeoutTimer_();
};
/**
* Continue with the next step in the test cycle.
*/
goog.testing.AsyncTestCase.prototype.continueTesting = function() {
if (this.receivedSignalCount_ < this.expectedSignalCount_) {
var remaining = this.expectedSignalCount_ - this.receivedSignalCount_;
throw Error('Still waiting for ' + remaining + ' signals.');
}
this.endCurrentStep_();
};
/**
* Ends the current test step and queues the next test step to run.
* @private
*/
goog.testing.AsyncTestCase.prototype.endCurrentStep_ = function() {
if (!this.isReady_) {
// We are a potential entry point, so we pump.
this.isReady_ = true;
this.stopTimeoutTimer_();
// Run this in a setTimeout so that the caller has a chance to call
// waitForAsync() again before we continue.
this.timeout(goog.bind(this.pump_, this, null), 0);
}
};
/**
* Informs the testcase not to continue to the next step in the test cycle
* until signal is called the specified number of times. Within a test, this
* function behaves additively if called multiple times; the number of signals
* to wait for will be the sum of all expected number of signals this function
* was called with.
* @param {number} times The number of signals to receive before
* continuing testing.
* @param {string=} opt_name A description of what we are waiting for.
*/
goog.testing.AsyncTestCase.prototype.waitForSignals =
function(times, opt_name) {
this.expectedSignalCount_ += times;
if (this.receivedSignalCount_ < this.expectedSignalCount_) {
this.waitForAsync(opt_name);
}
};
/**
* Signals once to continue with the test. If this is the last signal that the
* test was waiting on, call continueTesting.
*/
goog.testing.AsyncTestCase.prototype.signal = function() {
if (++this.receivedSignalCount_ === this.expectedSignalCount_ &&
this.expectedSignalCount_ > 0) {
this.endCurrentStep_();
}
};
/**
* Handles an exception thrown by a test.
* @param {*=} opt_e The exception object associated with the failure
* or a string.
* @throws Always throws a ControlBreakingException.
*/
goog.testing.AsyncTestCase.prototype.doAsyncError = function(opt_e) {
// If we've caught an exception that we threw, then just pass it along. This
// can happen if doAsyncError() was called from a call to assert and then
// again by pump_().
if (opt_e && opt_e.isControlBreakingException) {
throw opt_e;
}
// Prevent another timeout error from triggering for this test step.
this.stopTimeoutTimer_();
// doError() uses test.name. Here, we create a dummy test and give it a more
// helpful name based on the step we're currently on.
var fakeTestObj = new goog.testing.TestCase.Test(this.curStepName_,
goog.nullFunction);
if (this.activeTest) {
fakeTestObj.name = this.activeTest.name + ' [' + fakeTestObj.name + ']';
}
if (this.activeTest) {
// Note: if the test has an error, and then tearDown has an error, they will
// both be reported.
this.doError(fakeTestObj, opt_e);
} else {
this.exceptionBeforeTest = opt_e;
}
// This is a potential entry point, so we pump. We also add in a bit of a
// delay to try and prevent any async behavior from the failed test from
// causing the next test to fail.
this.timeout(goog.bind(this.pump_, this, this.doAsyncErrorTearDown_),
this.timeToSleepAfterFailure);
// We just caught an exception, so we do not want the code above us on the
// stack to continue executing. If pump_ is in our call-stack, then it will
// batch together multiple errors, so we only increment the count if pump_ is
// not in the stack and let pump_ increment the count when it batches them.
if (!this.returnWillPump_) {
this.numControlExceptionsExpected_ += 1;
this.dbgLog_('doAsynError: numControlExceptionsExpected_ = ' +
this.numControlExceptionsExpected_ + ' and throwing exception.');
}
// Copy the error message to ControlBreakingException.
var message = '';
if (typeof opt_e == 'string') {
message = opt_e;
} else if (opt_e && opt_e.message) {
message = opt_e.message;
}
throw new goog.testing.AsyncTestCase.ControlBreakingException(message);
};
/**
* Sets up the test page and then waits until the test case has been marked
* as ready before executing the tests.
* @override
*/
goog.testing.AsyncTestCase.prototype.runTests = function() {
this.hookAssert_();
this.hookOnError_();
this.setNextStep_(this.doSetUpPage_, 'setUpPage');
// We are an entry point, so we pump.
this.pump_();
};
/**
* Starts the tests.
* @override
*/
goog.testing.AsyncTestCase.prototype.cycleTests = function() {
// We are an entry point, so we pump.
this.saveMessage('Start');
this.setNextStep_(this.doIteration_, 'doIteration');
this.pump_();
};
/**
* Finalizes the test case, called when the tests have finished executing.
* @override
*/
goog.testing.AsyncTestCase.prototype.finalize = function() {
this.unhookAll_();
this.setNextStep_(null, 'finalized');
goog.testing.AsyncTestCase.superClass_.finalize.call(this);
};
/**
* Enables verbose logging of what is happening inside of the AsyncTestCase.
*/
goog.testing.AsyncTestCase.prototype.enableDebugLogging = function() {
this.enableDebugLogs_ = true;
};
/**
* Logs the given debug message to the console (when enabled).
* @param {string} message The message to log.
* @private
*/
goog.testing.AsyncTestCase.prototype.dbgLog_ = function(message) {
if (this.enableDebugLogs_) {
this.log('AsyncTestCase - ' + message);
}
};
/**
* Wraps doAsyncError() for when we are sure that the test runner has no user
* code above it in the stack.
* @param {string|Error=} opt_e The exception object associated with the
* failure or a string.
* @private
*/
goog.testing.AsyncTestCase.prototype.doTopOfStackAsyncError_ =
function(opt_e) {
/** @preserveTry */
try {
this.doAsyncError(opt_e);
} catch (e) {
// We know that we are on the top of the stack, so there is no need to
// throw this exception in this case.
if (e.isControlBreakingException) {
this.numControlExceptionsExpected_ -= 1;
this.dbgLog_('doTopOfStackAsyncError_: numControlExceptionsExpected_ = ' +
this.numControlExceptionsExpected_ + ' and catching exception.');
} else {
throw e;
}
}
};
/**
* Calls the tearDown function, catching any errors, and then moves on to
* the next step in the testing cycle.
* @private
*/
goog.testing.AsyncTestCase.prototype.doAsyncErrorTearDown_ = function() {
if (this.inException_) {
// We get here if tearDown is throwing the error.
// Upon calling continueTesting, the inline function 'doAsyncError' (set
// below) is run.
this.endCurrentStep_();
} else {
this.inException_ = true;
this.isReady_ = true;
// The continue point is different depending on if the error happened in
// setUpPage() or in setUp()/test*()/tearDown().
var stepFuncAfterError = this.nextStepFunc_;
var stepNameAfterError = 'TestCase.execute (after error)';
if (this.activeTest) {
stepFuncAfterError = this.doIteration_;
stepNameAfterError = 'doIteration (after error)';
}
// We must set the next step before calling tearDown.
this.setNextStep_(function() {
this.inException_ = false;
// This is null when an error happens in setUpPage.
this.setNextStep_(stepFuncAfterError, stepNameAfterError);
}, 'doAsyncError');
// Call the test's tearDown().
if (!this.cleanedUp_) {
this.cleanedUp_ = true;
this.tearDown();
}
}
};
/**
* Replaces the asserts.js assert_() and fail() functions with a wrappers to
* catch the exceptions.
* @private
*/
goog.testing.AsyncTestCase.prototype.hookAssert_ = function() {
if (!this.origAssert_) {
this.origAssert_ = _assert;
this.origFail_ = fail;
var self = this;
_assert = function() {
/** @preserveTry */
try {
self.origAssert_.apply(this, arguments);
} catch (e) {
self.dbgLog_('Wrapping failed assert()');
self.doAsyncError(e);
}
};
fail = function() {
/** @preserveTry */
try {
self.origFail_.apply(this, arguments);
} catch (e) {
self.dbgLog_('Wrapping fail()');
self.doAsyncError(e);
}
};
}
};
/**
* Sets a window.onerror handler for catching exceptions that happen in async
* callbacks. Note that as of Safari 3.1, Safari does not support this.
* @private
*/
goog.testing.AsyncTestCase.prototype.hookOnError_ = function() {
if (!this.origOnError_) {
this.origOnError_ = window.onerror;
var self = this;
window.onerror = function(error, url, line) {
// Ignore exceptions that we threw on purpose.
var cbe =
goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;
if (String(error).indexOf(cbe) != -1 &&
self.numControlExceptionsExpected_) {
self.numControlExceptionsExpected_ -= 1;
self.dbgLog_('window.onerror: numControlExceptionsExpected_ = ' +
self.numControlExceptionsExpected_ + ' and ignoring exception. ' +
error);
// Tell the browser not to compain about the error.
return true;
} else {
self.dbgLog_('window.onerror caught exception.');
var message = error + '\nURL: ' + url + '\nLine: ' + line;
self.doTopOfStackAsyncError_(message);
// Tell the browser to complain about the error.
return false;
}
};
}
};
/**
* Unhooks window.onerror and _assert.
* @private
*/
goog.testing.AsyncTestCase.prototype.unhookAll_ = function() {
if (this.origOnError_) {
window.onerror = this.origOnError_;
this.origOnError_ = null;
_assert = this.origAssert_;
this.origAssert_ = null;
fail = this.origFail_;
this.origFail_ = null;
}
};
/**
* Enables the timeout timer. This timer fires unless continueTesting is
* called.
* @private
*/
goog.testing.AsyncTestCase.prototype.startTimeoutTimer_ = function() {
if (!this.timeoutHandle_ && this.stepTimeout > 0) {
this.timeoutHandle_ = this.timeout(goog.bind(function() {
this.dbgLog_('Timeout timer fired with id ' + this.timeoutHandle_);
this.timeoutHandle_ = 0;
this.doTopOfStackAsyncError_('Timed out while waiting for ' +
'continueTesting() to be called.');
}, this, null), this.stepTimeout);
this.dbgLog_('Started timeout timer with id ' + this.timeoutHandle_);
}
};
/**
* Disables the timeout timer.
* @private
*/
goog.testing.AsyncTestCase.prototype.stopTimeoutTimer_ = function() {
if (this.timeoutHandle_) {
this.dbgLog_('Clearing timeout timer with id ' + this.timeoutHandle_);
this.clearTimeout(this.timeoutHandle_);
this.timeoutHandle_ = 0;
}
};
/**
* Sets the next function to call in our sequence of async callbacks.
* @param {Function} func The function that executes the next step.
* @param {string} name A description of the next step.
* @private
*/
goog.testing.AsyncTestCase.prototype.setNextStep_ = function(func, name) {
this.nextStepFunc_ = func && goog.bind(func, this);
this.nextStepName_ = name;
};
/**
* Calls the given function, redirecting any exceptions to doAsyncError.
* @param {Function} func The function to call.
* @return {!goog.testing.AsyncTestCase.TopStackFuncResult_} Returns a
* TopStackFuncResult_.
* @private
*/
goog.testing.AsyncTestCase.prototype.callTopOfStackFunc_ = function(func) {
/** @preserveTry */
try {
func.call(this);
return {controlBreakingExceptionThrown: false, message: ''};
} catch (e) {
this.dbgLog_('Caught exception in callTopOfStackFunc_');
/** @preserveTry */
try {
this.doAsyncError(e);
return {controlBreakingExceptionThrown: false, message: ''};
} catch (e2) {
if (!e2.isControlBreakingException) {
throw e2;
}
return {controlBreakingExceptionThrown: true, message: e2.message};
}
}
};
/**
* Calls the next callback when the isReady_ flag is true.
* @param {Function=} opt_doFirst A function to call before pumping.
* @private
* @throws Throws a ControlBreakingException if there were any failing steps.
*/
goog.testing.AsyncTestCase.prototype.pump_ = function(opt_doFirst) {
// If this function is already above us in the call-stack, then we should
// return rather than pumping in order to minimize call-stack depth.
if (!this.returnWillPump_) {
this.setBatchTime(this.now());
this.returnWillPump_ = true;
var topFuncResult = {};
if (opt_doFirst) {
topFuncResult = this.callTopOfStackFunc_(opt_doFirst);
}
// Note: we don't check for this.running here because it is not set to true
// while executing setUpPage and tearDownPage.
// Also, if isReady_ is false, then one of two things will happen:
// 1. Our timeout callback will be called.
// 2. The tests will call continueTesting(), which will call pump_() again.
while (this.isReady_ && this.nextStepFunc_ &&
!topFuncResult.controlBreakingExceptionThrown) {
this.curStepFunc_ = this.nextStepFunc_;
this.curStepName_ = this.nextStepName_;
this.nextStepFunc_ = null;
this.nextStepName_ = '';
this.dbgLog_('Performing step: ' + this.curStepName_);
topFuncResult =
this.callTopOfStackFunc_(/** @type {Function} */(this.curStepFunc_));
// If the max run time is exceeded call this function again async so as
// not to block the browser.
var delta = this.now() - this.getBatchTime();
if (delta > goog.testing.TestCase.maxRunTime &&
!topFuncResult.controlBreakingExceptionThrown) {
this.saveMessage('Breaking async');
var self = this;
this.timeout(function() { self.pump_(); }, 100);
break;
}
}
this.returnWillPump_ = false;
} else if (opt_doFirst) {
opt_doFirst.call(this);
}
};
/**
* Sets up the test page and then waits until the test case has been marked
* as ready before executing the tests.
* @private
*/
goog.testing.AsyncTestCase.prototype.doSetUpPage_ = function() {
this.setNextStep_(this.execute, 'TestCase.execute');
this.setUpPage();
};
/**
* Step 1: Move to the next test.
* @private
*/
goog.testing.AsyncTestCase.prototype.doIteration_ = function() {
this.expectedSignalCount_ = 0;
this.receivedSignalCount_ = 0;
this.activeTest = this.next();
if (this.activeTest && this.running) {
this.result_.runCount++;
// If this test should be marked as having failed, doIteration will go
// straight to the next test.
if (this.maybeFailTestEarly(this.activeTest)) {
this.setNextStep_(this.doIteration_, 'doIteration');
} else {
this.setNextStep_(this.doSetUp_, 'setUp');
}
} else {
// All tests done.
this.finalize();
}
};
/**
* Step 2: Call setUp().
* @private
*/
goog.testing.AsyncTestCase.prototype.doSetUp_ = function() {
this.log('Running test: ' + this.activeTest.name);
this.cleanedUp_ = false;
this.setNextStep_(this.doExecute_, this.activeTest.name);
this.setUp();
};
/**
* Step 3: Call test.execute().
* @private
*/
goog.testing.AsyncTestCase.prototype.doExecute_ = function() {
this.setNextStep_(this.doTearDown_, 'tearDown');
this.activeTest.execute();
};
/**
* Step 4: Call tearDown().
* @private
*/
goog.testing.AsyncTestCase.prototype.doTearDown_ = function() {
this.cleanedUp_ = true;
this.setNextStep_(this.doNext_, 'doNext');
this.tearDown();
};
/**
* Step 5: Call doSuccess()
* @private
*/
goog.testing.AsyncTestCase.prototype.doNext_ = function() {
this.setNextStep_(this.doIteration_, 'doIteration');
this.doSuccess(/** @type {goog.testing.TestCase.Test} */(this.activeTest));
};