nexttick_test.js

// Copyright 2013 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.
goog.provide('goog.async.nextTickTest');
goog.setTestOnly('goog.async.nextTickTest');

goog.require('goog.async.nextTick');
goog.require('goog.debug.ErrorHandler');
goog.require('goog.debug.entryPointRegistry');
goog.require('goog.testing.AsyncTestCase');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.jsunit');

var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(
    'asyncNextTickTest');

var clock;

function setUp() {
  clock = null;
}

function tearDown() {
  if (clock) {
    clock.uninstall();
  }
  goog.async.nextTick.setImmediate_ = undefined;
}


function testNextTick() {
  var c = 0;
  var max = 100;
  var async = true;
  var counterStep = function(i) {
    async = false;
    assertEquals('Order correct', i, c);
    c++;
    if (c === max) {
      asyncTestCase.continueTesting();
    }
  };
  for (var i = 0; i < max; i++) {
    goog.async.nextTick(goog.partial(counterStep, i));
  }
  assertTrue(async);
  asyncTestCase.waitForAsync('Wait for callback');
}


function testNextTickContext() {
  var context = {};
  var c = 0;
  var max = 10;
  var async = true;
  var counterStep = function(i) {
    async = false;
    assertEquals('Order correct', i, c);
    assertEquals(context, this);
    c++;
    if (c === max) {
      asyncTestCase.continueTesting();
    }
  };
  for (var i = 0; i < max; i++) {
    goog.async.nextTick(goog.partial(counterStep, i), context);
  }
  assertTrue(async);
  asyncTestCase.waitForAsync('Wait for callback');
}


function testNextTickMockClock() {
  clock = new goog.testing.MockClock(true);
  var result = '';
  goog.async.nextTick(function() {
    result += 'a';
  });
  goog.async.nextTick(function() {
    result += 'b';
  });
  goog.async.nextTick(function() {
    result += 'c';
  });
  assertEquals('', result);
  clock.tick(0);
  assertEquals('abc', result);
}


function testNextTickDoesntSwallowError() {
  var before = window.onerror;
  var sentinel = 'sentinel';
  window.onerror = function(e) {
    e = '' + e;
    // Don't test for contents in IE7, which does not preserve the exception
    // message.
    if (e.indexOf('Exception thrown and not caught') == -1) {
      assertContains(sentinel, e);
    }
    window.onerror = before;
    asyncTestCase.continueTesting();
    return false;
  };
  goog.async.nextTick(function() {
    throw sentinel;
  });
  asyncTestCase.waitForAsync('Wait for error');
}


function testNextTickProtectEntryPoint() {
  var errorHandlerCallbackCalled = false;
  var errorHandler = new goog.debug.ErrorHandler(function() {
    errorHandlerCallbackCalled = true;
  });

  // This is only testing wrapping the callback with the protected entry point,
  // so it's okay to replace this function with a fake.
  goog.async.nextTick.setImmediate_ = function(cb) {
    try {
      cb();
      fail('The callback should have thrown an error.');
    } catch (e) {
      assertTrue(
          e instanceof goog.debug.ErrorHandler.ProtectedFunctionError);
    }
    asyncTestCase.continueTesting();
  };
  var origSetImmediate;
  if (window.setImmediate) {
    origSetImmediate = window.setImmediate;
    window.setImmediate = goog.async.nextTick.setImmediate_;
  }
  goog.debug.entryPointRegistry.monitorAll(errorHandler);

  function thrower() {
    throw Error('This should be caught by the protected function.');
  }
  asyncTestCase.waitForAsync('Wait for callback');
  goog.async.nextTick(thrower);
  window.setImmediate = origSetImmediate;
}