# lib/goog/testing/asynctestcase.js

    1// Copyright 2010 The Closure Library Authors. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS-IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14// All Rights Reserved.1516/**17 * @fileoverview A class representing a set of test functions that use18 * asynchronous functions that cannot be meaningfully mocked.19 *20 * To create a Google-compatable JsUnit test using this test case, put the21 * following snippet in your test:22 *23 *   var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();24 *25 * To make the test runner wait for your asynchronous behaviour, use:26 *27 *   asyncTestCase.waitForAsync('Waiting for xhr to respond');28 *29 * The next test will not start until the following call is made, or a30 * timeout occurs:31 *32 *   asyncTestCase.continueTesting();33 *34 * There does NOT need to be a 1:1 mapping of waitForAsync calls and35 * continueTesting calls. The next test will be run after a single call to36 * continueTesting is made, as long as there is no subsequent call to37 * waitForAsync in the same thread.38 *39 * Example:40 *   // Returning here would cause the next test to be run.41 *   asyncTestCase.waitForAsync('description 1');42 *   // Returning here would *not* cause the next test to be run.43 *   // Only effect of additional waitForAsync() calls is an updated44 *   // description in the case of a timeout.45 *   asyncTestCase.waitForAsync('updated description');46 *   asyncTestCase.continueTesting();47 *   // Returning here would cause the next test to be run.48 *   asyncTestCase.waitForAsync('just kidding, still running.');49 *   // Returning here would *not* cause the next test to be run.50 *51 * The test runner can also be made to wait for more than one asynchronous52 * event with:53 *54 *   asyncTestCase.waitForSignals(n);55 *56 * The next test will not start until asyncTestCase.signal() is called n times,57 * or the test step timeout is exceeded.58 *59 * This class supports asynchronous behaviour in all test functions except for60 * tearDownPage. If such support is needed, it can be added.61 *62 * Example Usage:63 *64 *   var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();65 *   // Optionally, set a longer-than-normal step timeout.66 *   asyncTestCase.stepTimeout = 30 * 1000;67 *68 *   function testSetTimeout() {69 *     var step = 0;70 *     function stepCallback() {71 *       step++;72 *       switch (step) {73 *         case 1:74 *           var startTime = goog.now();75 *           asyncTestCase.waitForAsync('step 1');76 *           window.setTimeout(stepCallback, 100);77 *           break;78 *         case 2:79 *           assertTrue('Timeout fired too soon',80 *               goog.now() - startTime >= 100);81 *           asyncTestCase.waitForAsync('step 2');82 *           window.setTimeout(stepCallback, 100);83 *           break;84 *         case 3:85 *           assertTrue('Timeout fired too soon',86 *               goog.now() - startTime >= 200);87 *           asyncTestCase.continueTesting();88 *           break;89 *         default:90 *           fail('Unexpected call to stepCallback');91 *       }92 *     }93 *     stepCallback();94 *   }95 *96 * Known Issues:97 *   IE7 Exceptions:98 *     As the failingtest.html will show, it appears as though ie7 does not99 *     propagate an exception past a function called using the func.call()100 *     syntax. This causes case 3 of the failing tests (exceptions) to show up101 *     as timeouts in IE.102 *   window.onerror:103 *     This seems to catch errors only in ff2/ff3. It does not work in Safari or104 *     IE7. The consequence of this is that exceptions that would have been105 *     caught by window.onerror show up as timeouts.106 *107 * @author agrieve@google.com (Andrew Grieve)108 */109110goog.provide('goog.testing.AsyncTestCase');111goog.provide('goog.testing.AsyncTestCase.ControlBreakingException');112113goog.require('goog.testing.TestCase');114goog.require('goog.testing.TestCase.Test');115goog.require('goog.testing.asserts');116117118119/**120 * A test case that is capable of running tests the contain asynchronous logic.121 * @param {string=} opt_name A descriptive name for the test case.122 * @extends {goog.testing.TestCase}123 * @constructor124 */125goog.testing.AsyncTestCase = function(opt_name) {126  goog.testing.TestCase.call(this, opt_name);127};128goog.inherits(goog.testing.AsyncTestCase, goog.testing.TestCase);129130131/**132 * Represents result of top stack function call.133 * @typedef {{controlBreakingExceptionThrown: boolean, message: string}}134 * @private135 */136goog.testing.AsyncTestCase.TopStackFuncResult_;137138139140/**141 * An exception class used solely for control flow.142 * @param {string=} opt_message Error message.143 * @constructor144 * @final145 */146goog.testing.AsyncTestCase.ControlBreakingException = function(opt_message) {147  /**148   * The exception message.149   * @type {string}150   */151  this.message = opt_message || '';152};153154155/**156 * Return value for .toString().157 * @type {string}158 */159goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING =160    '[AsyncTestCase.ControlBreakingException]';161162163/**164 * Marks this object as a ControlBreakingException165 * @type {boolean}166 */167goog.testing.AsyncTestCase.ControlBreakingException.prototype.168    isControlBreakingException = true;169170171/** @override */172goog.testing.AsyncTestCase.ControlBreakingException.prototype.toString =173    function() {174  // This shows up in the console when the exception is not caught.175  return goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;176};177178179/**180 * How long to wait for a single step of a test to complete in milliseconds.181 * A step starts when a call to waitForAsync() is made.182 * @type {number}183 */184goog.testing.AsyncTestCase.prototype.stepTimeout = 1000;185186187/**188 * How long to wait after a failed test before moving onto the next one.189 * The purpose of this is to allow any pending async callbacks from the failing190 * test to finish up and not cause the next test to fail.191 * @type {number}192 */193goog.testing.AsyncTestCase.prototype.timeToSleepAfterFailure = 500;194195196/**197 * Turn on extra logging to help debug failing async. tests.198 * @type {boolean}199 * @private200 */201goog.testing.AsyncTestCase.prototype.enableDebugLogs_ = false;202203204/**205 * A reference to the original asserts.js assert_() function.206 * @private207 */208goog.testing.AsyncTestCase.prototype.origAssert_;209210211/**212 * A reference to the original asserts.js fail() function.213 * @private214 */215goog.testing.AsyncTestCase.prototype.origFail_;216217218/**219 * A reference to the original window.onerror function.220 * @type {Function|undefined}221 * @private222 */223goog.testing.AsyncTestCase.prototype.origOnError_;224225226/**227 * The stage of the test we are currently on.228 * @type {Function|undefined}}229 * @private230 */231goog.testing.AsyncTestCase.prototype.curStepFunc_;232233234/**235 * The name of the stage of the test we are currently on.236 * @type {string}237 * @private238 */239goog.testing.AsyncTestCase.prototype.curStepName_ = '';240241242/**243 * The stage of the test we should run next.244 * @type {Function|undefined}245 * @private246 */247goog.testing.AsyncTestCase.prototype.nextStepFunc;248249250/**251 * The name of the stage of the test we should run next.252 * @type {string}253 * @private254 */255goog.testing.AsyncTestCase.prototype.nextStepName_ = '';256257258/**259 * The handle to the current setTimeout timer.260 * @type {number}261 * @private262 */263goog.testing.AsyncTestCase.prototype.timeoutHandle_ = 0;264265266/**267 * Marks if the cleanUp() function has been called for the currently running268 * test.269 * @type {boolean}270 * @private271 */272goog.testing.AsyncTestCase.prototype.cleanedUp_ = false;273274275/**276 * The currently active test.277 * @type {goog.testing.TestCase.Test|undefined}278 * @protected279 */280goog.testing.AsyncTestCase.prototype.activeTest;281282283/**284 * A flag to prevent recursive exception handling.285 * @type {boolean}286 * @private287 */288goog.testing.AsyncTestCase.prototype.inException_ = false;289290291/**292 * Flag used to determine if we can move to the next step in the testing loop.293 * @type {boolean}294 * @private295 */296goog.testing.AsyncTestCase.prototype.isReady_ = true;297298299/**300 * Number of signals to wait for before continuing testing when waitForSignals301 * is used.302 * @type {number}303 * @private304 */305goog.testing.AsyncTestCase.prototype.expectedSignalCount_ = 0;306307308/**309 * Number of signals received.310 * @type {number}311 * @private312 */313goog.testing.AsyncTestCase.prototype.receivedSignalCount_ = 0;314315316/**317 * Flag that tells us if there is a function in the call stack that will make318 * a call to pump_().319 * @type {boolean}320 * @private321 */322goog.testing.AsyncTestCase.prototype.returnWillPump_ = false;323324325/**326 * The number of times we have thrown a ControlBreakingException so that we327 * know not to complain in our window.onerror handler. In Webkit, window.onerror328 * is not supported, and so this counter will keep going up but we won't care329 * about it.330 * @type {number}331 * @private332 */333goog.testing.AsyncTestCase.prototype.numControlExceptionsExpected_ = 0;334335336/**337 * The current step name.338 * @return {!string} Step name.339 * @protected340 */341goog.testing.AsyncTestCase.prototype.getCurrentStepName = function() {342  return this.curStepName_;343};344345346/**347 * Preferred way of creating an AsyncTestCase. Creates one and initializes it348 * with the G_testRunner.349 * @param {string=} opt_name A descriptive name for the test case.350 * @return {!goog.testing.AsyncTestCase} The created AsyncTestCase.351 */352goog.testing.AsyncTestCase.createAndInstall = function(opt_name) {353  var asyncTestCase = new goog.testing.AsyncTestCase(opt_name);354  goog.testing.TestCase.initializeTestRunner(asyncTestCase);355  return asyncTestCase;356};357358359/**360 * Informs the testcase not to continue to the next step in the test cycle361 * until continueTesting is called.362 * @param {string=} opt_name A description of what we are waiting for.363 */364goog.testing.AsyncTestCase.prototype.waitForAsync = function(opt_name) {365  this.isReady_ = false;366  this.curStepName_ = opt_name || this.curStepName_;367368  // Reset the timer that tracks if the async test takes too long.369  this.stopTimeoutTimer_();370  this.startTimeoutTimer_();371};372373374/**375 * Continue with the next step in the test cycle.376 */377goog.testing.AsyncTestCase.prototype.continueTesting = function() {378  if (this.receivedSignalCount_ < this.expectedSignalCount_) {379    var remaining = this.expectedSignalCount_ - this.receivedSignalCount_;380    throw Error('Still waiting for ' + remaining + ' signals.');381  }382  this.endCurrentStep_();383};384385386/**387 * Ends the current test step and queues the next test step to run.388 * @private389 */390goog.testing.AsyncTestCase.prototype.endCurrentStep_ = function() {391  if (!this.isReady_) {392    // We are a potential entry point, so we pump.393    this.isReady_ = true;394    this.stopTimeoutTimer_();395    // Run this in a setTimeout so that the caller has a chance to call396    // waitForAsync() again before we continue.397    this.timeout(goog.bind(this.pump_, this, null), 0);398  }399};400401402/**403 * Informs the testcase not to continue to the next step in the test cycle404 * until signal is called the specified number of times. Within a test, this405 * function behaves additively if called multiple times; the number of signals406 * to wait for will be the sum of all expected number of signals this function407 * was called with.408 * @param {number} times The number of signals to receive before409 *    continuing testing.410 * @param {string=} opt_name A description of what we are waiting for.411 */412goog.testing.AsyncTestCase.prototype.waitForSignals =413    function(times, opt_name) {414  this.expectedSignalCount_ += times;415  if (this.receivedSignalCount_ < this.expectedSignalCount_) {416    this.waitForAsync(opt_name);417  }418};419420421/**422 * Signals once to continue with the test. If this is the last signal that the423 * test was waiting on, call continueTesting.424 */425goog.testing.AsyncTestCase.prototype.signal = function() {426  if (++this.receivedSignalCount_ === this.expectedSignalCount_ &&427      this.expectedSignalCount_ > 0) {428    this.endCurrentStep_();429  }430};431432433/**434 * Handles an exception thrown by a test.435 * @param {*=} opt_e The exception object associated with the failure436 *     or a string.437 * @throws Always throws a ControlBreakingException.438 */439goog.testing.AsyncTestCase.prototype.doAsyncError = function(opt_e) {440  // If we've caught an exception that we threw, then just pass it along. This441  // can happen if doAsyncError() was called from a call to assert and then442  // again by pump_().443  if (opt_e && opt_e.isControlBreakingException) {444    throw opt_e;445  }446447  // Prevent another timeout error from triggering for this test step.448  this.stopTimeoutTimer_();449450  // doError() uses test.name. Here, we create a dummy test and give it a more451  // helpful name based on the step we're currently on.452  var fakeTestObj = new goog.testing.TestCase.Test(this.curStepName_,453                                                   goog.nullFunction);454  if (this.activeTest) {455    fakeTestObj.name = this.activeTest.name + ' [' + fakeTestObj.name + ']';456  }457458  if (this.activeTest) {459    // Note: if the test has an error, and then tearDown has an error, they will460    // both be reported.461    this.doError(fakeTestObj, opt_e);462  } else {463    this.exceptionBeforeTest = opt_e;464  }465466  // This is a potential entry point, so we pump. We also add in a bit of a467  // delay to try and prevent any async behavior from the failed test from468  // causing the next test to fail.469  this.timeout(goog.bind(this.pump_, this, this.doAsyncErrorTearDown_),470      this.timeToSleepAfterFailure);471472  // We just caught an exception, so we do not want the code above us on the473  // stack to continue executing. If pump_ is in our call-stack, then it will474  // batch together multiple errors, so we only increment the count if pump_ is475  // not in the stack and let pump_ increment the count when it batches them.476  if (!this.returnWillPump_) {477    this.numControlExceptionsExpected_ += 1;478    this.dbgLog_('doAsynError: numControlExceptionsExpected_ = ' +479        this.numControlExceptionsExpected_ + ' and throwing exception.');480  }481482  // Copy the error message to ControlBreakingException.483  var message = '';484  if (typeof opt_e == 'string') {485    message = opt_e;486  } else if (opt_e && opt_e.message) {487    message = opt_e.message;488  }489  throw new goog.testing.AsyncTestCase.ControlBreakingException(message);490};491492493/**494 * Sets up the test page and then waits until the test case has been marked495 * as ready before executing the tests.496 * @override497 */498goog.testing.AsyncTestCase.prototype.runTests = function() {499  this.hookAssert_();500  this.hookOnError_();501502  this.setNextStep_(this.doSetUpPage_, 'setUpPage');503  // We are an entry point, so we pump.504  this.pump_();505};506507508/**509 * Starts the tests.510 * @override511 */512goog.testing.AsyncTestCase.prototype.cycleTests = function() {513  // We are an entry point, so we pump.514  this.saveMessage('Start');515  this.setNextStep_(this.doIteration_, 'doIteration');516  this.pump_();517};518519520/**521 * Finalizes the test case, called when the tests have finished executing.522 * @override523 */524goog.testing.AsyncTestCase.prototype.finalize = function() {525  this.unhookAll_();526  this.setNextStep_(null, 'finalized');527  goog.testing.AsyncTestCase.superClass_.finalize.call(this);528};529530531/**532 * Enables verbose logging of what is happening inside of the AsyncTestCase.533 */534goog.testing.AsyncTestCase.prototype.enableDebugLogging = function() {535  this.enableDebugLogs_ = true;536};537538539/**540 * Logs the given debug message to the console (when enabled).541 * @param {string} message The message to log.542 * @private543 */544goog.testing.AsyncTestCase.prototype.dbgLog_ = function(message) {545  if (this.enableDebugLogs_) {546    this.log('AsyncTestCase - ' + message);547  }548};549550551/**552 * Wraps doAsyncError() for when we are sure that the test runner has no user553 * code above it in the stack.554 * @param {string|Error=} opt_e The exception object associated with the555 *     failure or a string.556 * @private557 */558goog.testing.AsyncTestCase.prototype.doTopOfStackAsyncError_ =559    function(opt_e) {560  /** @preserveTry */561  try {562    this.doAsyncError(opt_e);563  } catch (e) {564    // We know that we are on the top of the stack, so there is no need to565    // throw this exception in this case.566    if (e.isControlBreakingException) {567      this.numControlExceptionsExpected_ -= 1;568      this.dbgLog_('doTopOfStackAsyncError_: numControlExceptionsExpected_ = ' +569          this.numControlExceptionsExpected_ + ' and catching exception.');570    } else {571      throw e;572    }573  }574};575576577/**578 * Calls the tearDown function, catching any errors, and then moves on to579 * the next step in the testing cycle.580 * @private581 */582goog.testing.AsyncTestCase.prototype.doAsyncErrorTearDown_ = function() {583  if (this.inException_) {584    // We get here if tearDown is throwing the error.585    // Upon calling continueTesting, the inline function 'doAsyncError' (set586    // below) is run.587    this.endCurrentStep_();588  } else {589    this.inException_ = true;590    this.isReady_ = true;591592    // The continue point is different depending on if the error happened in593    // setUpPage() or in setUp()/test*()/tearDown().594    var stepFuncAfterError = this.nextStepFunc_;595    var stepNameAfterError = 'TestCase.execute (after error)';596    if (this.activeTest) {597      stepFuncAfterError = this.doIteration_;598      stepNameAfterError = 'doIteration (after error)';599    }600601    // We must set the next step before calling tearDown.602    this.setNextStep_(function() {603      this.inException_ = false;604      // This is null when an error happens in setUpPage.605      this.setNextStep_(stepFuncAfterError, stepNameAfterError);606    }, 'doAsyncError');607608    // Call the test's tearDown().609    if (!this.cleanedUp_) {610      this.cleanedUp_ = true;611      this.tearDown();612    }613  }614};615616617/**618 * Replaces the asserts.js assert_() and fail() functions with a wrappers to619 * catch the exceptions.620 * @private621 */622goog.testing.AsyncTestCase.prototype.hookAssert_ = function() {623  if (!this.origAssert_) {624    this.origAssert_ = _assert;625    this.origFail_ = fail;626    var self = this;627    _assert = function() {628      /** @preserveTry */629      try {630        self.origAssert_.apply(this, arguments);631      } catch (e) {632        self.dbgLog_('Wrapping failed assert()');633        self.doAsyncError(e);634      }635    };636    fail = function() {637      /** @preserveTry */638      try {639        self.origFail_.apply(this, arguments);640      } catch (e) {641        self.dbgLog_('Wrapping fail()');642        self.doAsyncError(e);643      }644    };645  }646};647648649/**650 * Sets a window.onerror handler for catching exceptions that happen in async651 * callbacks. Note that as of Safari 3.1, Safari does not support this.652 * @private653 */654goog.testing.AsyncTestCase.prototype.hookOnError_ = function() {655  if (!this.origOnError_) {656    this.origOnError_ = window.onerror;657    var self = this;658    window.onerror = function(error, url, line) {659      // Ignore exceptions that we threw on purpose.660      var cbe =661          goog.testing.AsyncTestCase.ControlBreakingException.TO_STRING;662      if (String(error).indexOf(cbe) != -1 &&663          self.numControlExceptionsExpected_) {664        self.numControlExceptionsExpected_ -= 1;665        self.dbgLog_('window.onerror: numControlExceptionsExpected_ = ' +666            self.numControlExceptionsExpected_ + ' and ignoring exception. ' +667            error);668        // Tell the browser not to compain about the error.669        return true;670      } else {671        self.dbgLog_('window.onerror caught exception.');672        var message = error + '\nURL: ' + url + '\nLine: ' + line;673        self.doTopOfStackAsyncError_(message);674        // Tell the browser to complain about the error.675        return false;676      }677    };678  }679};680681682/**683 * Unhooks window.onerror and _assert.684 * @private685 */686goog.testing.AsyncTestCase.prototype.unhookAll_ = function() {687  if (this.origOnError_) {688    window.onerror = this.origOnError_;689    this.origOnError_ = null;690    _assert = this.origAssert_;691    this.origAssert_ = null;692    fail = this.origFail_;693    this.origFail_ = null;694  }695};696697698/**699 * Enables the timeout timer. This timer fires unless continueTesting is700 * called.701 * @private702 */703goog.testing.AsyncTestCase.prototype.startTimeoutTimer_ = function() {704  if (!this.timeoutHandle_ && this.stepTimeout > 0) {705    this.timeoutHandle_ = this.timeout(goog.bind(function() {706      this.dbgLog_('Timeout timer fired with id ' + this.timeoutHandle_);707      this.timeoutHandle_ = 0;708709      this.doTopOfStackAsyncError_('Timed out while waiting for ' +710          'continueTesting() to be called.');711    }, this, null), this.stepTimeout);712    this.dbgLog_('Started timeout timer with id ' + this.timeoutHandle_);713  }714};715716717/**718 * Disables the timeout timer.719 * @private720 */721goog.testing.AsyncTestCase.prototype.stopTimeoutTimer_ = function() {722  if (this.timeoutHandle_) {723    this.dbgLog_('Clearing timeout timer with id ' + this.timeoutHandle_);724    this.clearTimeout(this.timeoutHandle_);725    this.timeoutHandle_ = 0;726  }727};728729730/**731 * Sets the next function to call in our sequence of async callbacks.732 * @param {Function} func The function that executes the next step.733 * @param {string} name A description of the next step.734 * @private735 */736goog.testing.AsyncTestCase.prototype.setNextStep_ = function(func, name) {737  this.nextStepFunc_ = func && goog.bind(func, this);738  this.nextStepName_ = name;739};740741742/**743 * Calls the given function, redirecting any exceptions to doAsyncError.744 * @param {Function} func The function to call.745 * @return {!goog.testing.AsyncTestCase.TopStackFuncResult_} Returns a746 * TopStackFuncResult_.747 * @private748 */749goog.testing.AsyncTestCase.prototype.callTopOfStackFunc_ = function(func) {750  /** @preserveTry */751  try {752    func.call(this);753    return {controlBreakingExceptionThrown: false, message: ''};754  } catch (e) {755    this.dbgLog_('Caught exception in callTopOfStackFunc_');756    /** @preserveTry */757    try {758      this.doAsyncError(e);759      return {controlBreakingExceptionThrown: false, message: ''};760    } catch (e2) {761      if (!e2.isControlBreakingException) {762        throw e2;763      }764      return {controlBreakingExceptionThrown: true, message: e2.message};765    }766  }767};768769770/**771 * Calls the next callback when the isReady_ flag is true.772 * @param {Function=} opt_doFirst A function to call before pumping.773 * @private774 * @throws Throws a ControlBreakingException if there were any failing steps.775 */776goog.testing.AsyncTestCase.prototype.pump_ = function(opt_doFirst) {777  // If this function is already above us in the call-stack, then we should778  // return rather than pumping in order to minimize call-stack depth.779  if (!this.returnWillPump_) {780    this.setBatchTime(this.now());781    this.returnWillPump_ = true;782    var topFuncResult = {};783784    if (opt_doFirst) {785      topFuncResult = this.callTopOfStackFunc_(opt_doFirst);786    }787    // Note: we don't check for this.running here because it is not set to true788    // while executing setUpPage and tearDownPage.789    // Also, if isReady_ is false, then one of two things will happen:790    // 1. Our timeout callback will be called.791    // 2. The tests will call continueTesting(), which will call pump_() again.792    while (this.isReady_ && this.nextStepFunc_ &&793        !topFuncResult.controlBreakingExceptionThrown) {794      this.curStepFunc_ = this.nextStepFunc_;795      this.curStepName_ = this.nextStepName_;796      this.nextStepFunc_ = null;797      this.nextStepName_ = '';798799      this.dbgLog_('Performing step: ' + this.curStepName_);800      topFuncResult =801          this.callTopOfStackFunc_(/** @type {Function} */(this.curStepFunc_));802803      // If the max run time is exceeded call this function again async so as804      // not to block the browser.805      var delta = this.now() - this.getBatchTime();806      if (delta > goog.testing.TestCase.maxRunTime &&807          !topFuncResult.controlBreakingExceptionThrown) {808        this.saveMessage('Breaking async');809        var self = this;810        this.timeout(function() { self.pump_(); }, 100);811        break;812      }813    }814    this.returnWillPump_ = false;815  } else if (opt_doFirst) {816    opt_doFirst.call(this);817  }818};819820821/**822 * Sets up the test page and then waits untill the test case has been marked823 * as ready before executing the tests.824 * @private825 */826goog.testing.AsyncTestCase.prototype.doSetUpPage_ = function() {827  this.setNextStep_(this.execute, 'TestCase.execute');828  this.setUpPage();829};830831832/**833 * Step 1: Move to the next test.834 * @private835 */836goog.testing.AsyncTestCase.prototype.doIteration_ = function() {837  this.expectedSignalCount_ = 0;838  this.receivedSignalCount_ = 0;839  this.activeTest = this.next();840  if (this.activeTest && this.running) {841    this.result_.runCount++;842    // If this test should be marked as having failed, doIteration will go843    // straight to the next test.844    if (this.maybeFailTestEarly(this.activeTest)) {845      this.setNextStep_(this.doIteration_, 'doIteration');846    } else {847      this.setNextStep_(this.doSetUp_, 'setUp');848    }849  } else {850    // All tests done.851    this.finalize();852  }853};854855856/**857 * Step 2: Call setUp().858 * @private859 */860goog.testing.AsyncTestCase.prototype.doSetUp_ = function() {861  this.log('Running test: ' + this.activeTest.name);862  this.cleanedUp_ = false;863  this.setNextStep_(this.doExecute_, this.activeTest.name);864  this.setUp();865};866867868/**869 * Step 3: Call test.execute().870 * @private871 */872goog.testing.AsyncTestCase.prototype.doExecute_ = function() {873  this.setNextStep_(this.doTearDown_, 'tearDown');874  this.activeTest.execute();875};876877878/**879 * Step 4: Call tearDown().880 * @private881 */882goog.testing.AsyncTestCase.prototype.doTearDown_ = function() {883  this.cleanedUp_ = true;884  this.setNextStep_(this.doNext_, 'doNext');885  this.tearDown();886};887888889/**890 * Step 5: Call doSuccess()891 * @private892 */893goog.testing.AsyncTestCase.prototype.doNext_ = function() {894  this.setNextStep_(this.doIteration_, 'doIteration');895  this.doSuccess(/** @type {goog.testing.TestCase.Test} */(this.activeTest));896};