blob: e97a3e269b557b52af91b72d1322399dac3251b6 [file] [log] [blame]
// Copyright 2009 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.testing.ContinuationTestCaseTest');
goog.setTestOnly('goog.testing.ContinuationTestCaseTest');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.testing.ContinuationTestCase');
goog.require('goog.testing.MockClock');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.TestCase');
goog.require('goog.testing.jsunit');
/**
* @fileoverview This test file uses the ContinuationTestCase to test itself,
* which is a little confusing. It's also difficult to write a truly effective
* test, since testing a failure causes an actual failure in the test runner.
* All tests have been manually verified using a sophisticated combination of
* alerts and false assertions.
*/
var testCase = new goog.testing.ContinuationTestCase('Continuation Test Case');
testCase.autoDiscoverTests();
// Standalone Closure Test Runner.
if (typeof G_testRunner != 'undefined') {
G_testRunner.initialize(testCase);
}
var clock = new goog.testing.MockClock();
var count = 0;
var stubs = new goog.testing.PropertyReplacer();
function setUpPage() {
count = testCase.getCount();
}
/**
* Resets the mock clock. Includes a wait step to verify that setUp routines
* can contain continuations.
*/
function setUp() {
waitForTimeout(function() {
// Pointless assertion to verify that setUp methods can contain waits.
assertEquals(count, testCase.getCount());
}, 0);
clock.reset();
}
/**
* Uninstalls the mock clock if it was installed, and restores the Step timeout
* functions to the default window implementations.
*/
function tearDown() {
clock.uninstall();
stubs.reset();
waitForTimeout(function() {
// Pointless assertion to verify that tearDown methods can contain waits.
assertTrue(testCase.now() >= testCase.startTime_);
}, 0);
}
/**
* Installs the Mock Clock and replaces the Step timeouts with the mock
* implementations.
*/
function installMockClock() {
clock.install();
// Overwrite the "protected" setTimeout and clearTimeout with the versions
// replaced by MockClock. Normal tests should never do this, but we need to
// test the ContinuationTest itself.
stubs.set(goog.testing.ContinuationTestCase.Step, 'protectedClearTimeout_',
window.clearTimeout);
stubs.set(goog.testing.ContinuationTestCase.Step, 'protectedSetTimeout_',
window.setTimeout);
}
/**
* @return {goog.testing.ContinuationTestCase.Step} A generic step in a
* continuation test.
*/
function getSampleStep() {
return new goog.testing.ContinuationTestCase.Step('test', function() {});
}
/**
* @return {goog.testing.ContinuationTestCase.Test} A simple continuation test
* with generic setUp, test, and tearDown functions.
*/
function getSampleTest() {
var setupStep = new goog.testing.TestCase.Test('setup', function() {});
var testStep = new goog.testing.TestCase.Test('test', function() {});
var teardownStep = new goog.testing.TestCase.Test('teardown', function() {});
return new goog.testing.ContinuationTestCase.Test(setupStep,
testStep,
teardownStep);
}
function testStepWaiting() {
var step = getSampleStep();
assertTrue(step.waiting);
}
function testStepSetTimeout() {
installMockClock();
var step = getSampleStep();
var timeoutReached = false;
step.setTimeout(function() {timeoutReached = true}, 100);
clock.tick(50);
assertFalse(timeoutReached);
clock.tick(50);
assertTrue(timeoutReached);
}
function testStepClearTimeout() {
var step = new goog.testing.ContinuationTestCase.Step('test', function() {});
var timeoutReached = false;
step.setTimeout(function() {timeoutReached = true}, 100);
clock.tick(50);
assertFalse(timeoutReached);
step.clearTimeout();
clock.tick(50);
assertFalse(timeoutReached);
}
function testTestPhases() {
var test = getSampleTest();
assertEquals('setup', test.getCurrentPhase()[0].name);
test.cancelCurrentPhase();
assertEquals('test', test.getCurrentPhase()[0].name);
test.cancelCurrentPhase();
assertEquals('teardown', test.getCurrentPhase()[0].name);
test.cancelCurrentPhase();
assertNull(test.getCurrentPhase());
}
function testTestSetError() {
var test = getSampleTest();
var error1 = new Error('Oh noes!');
var error2 = new Error('B0rken.');
assertNull(test.getError());
test.setError(error1);
assertEquals(error1, test.getError());
test.setError(error2);
assertEquals('Once an error has been set, it should not be overwritten.',
error1, test.getError());
}
function testAddStep() {
var test = getSampleTest();
var step = getSampleStep();
// Try adding a step to each phase and then cancelling the phase.
for (var i = 0; i < 3; i++) {
assertEquals(1, test.getCurrentPhase().length);
test.addStep(step);
assertEquals(2, test.getCurrentPhase().length);
assertEquals(step, test.getCurrentPhase()[1]);
test.cancelCurrentPhase();
}
assertNull(test.getCurrentPhase());
}
function testCancelTestPhase() {
var test = getSampleTest();
test.cancelTestPhase();
assertEquals('teardown', test.getCurrentPhase()[0].name);
test = getSampleTest();
test.cancelCurrentPhase();
test.cancelTestPhase();
assertEquals('teardown', test.getCurrentPhase()[0].name);
test = getSampleTest();
test.cancelTestPhase();
test.cancelTestPhase();
assertEquals('teardown', test.getCurrentPhase()[0].name);
}
function testWaitForTimeout() {
var reachedA = false;
var reachedB = false;
var reachedC = false;
waitForTimeout(function a() {
reachedA = true;
assertTrue('A must be true at callback a.', reachedA);
assertFalse('B must be false at callback a.', reachedB);
assertFalse('C must be false at callback a.', reachedC);
}, 10);
waitForTimeout(function b() {
reachedB = true;
assertTrue('A must be true at callback b.', reachedA);
assertTrue('B must be true at callback b.', reachedB);
assertFalse('C must be false at callback b.', reachedC);
}, 20);
waitForTimeout(function c() {
reachedC = true;
assertTrue('A must be true at callback c.', reachedA);
assertTrue('B must be true at callback c.', reachedB);
assertTrue('C must be true at callback c.', reachedC);
}, 20);
assertFalse('a', reachedA);
assertFalse('b', reachedB);
assertFalse('c', reachedC);
}
function testWaitForEvent() {
var et = new goog.events.EventTarget();
var eventFired = false;
goog.events.listen(et, 'testPrefire', function() {
eventFired = true;
et.dispatchEvent('test');
});
waitForEvent(et, 'test', function() {
assertTrue(eventFired);
});
et.dispatchEvent('testPrefire');
}
function testWaitForCondition() {
var counter = 0;
waitForCondition(function() {
return ++counter >= 2;
}, function() {
assertEquals(2, counter);
}, 10, 200);
}
function testOutOfOrderWaits() {
var counter = 0;
// Note that if the delta between the timeout is too small, two
// continuation may be invoked at the same timer tick, using the
// registration order.
waitForTimeout(function() {assertEquals(3, ++counter);}, 200);
waitForTimeout(function() {assertEquals(1, ++counter);}, 0);
waitForTimeout(function() {assertEquals(2, ++counter);}, 100);
}
/*
* Any of the test functions below (except the condition check passed into
* waitForCondition) can raise an assertion successfully. Any level of nested
* test steps should be possible, in any configuration.
*/
var testObj;
function testCrazyNestedWaitFunction() {
testObj = {
lock: true,
et: new goog.events.EventTarget(),
steps: 0
};
waitForTimeout(handleTimeout, 10);
waitForEvent(testObj.et, 'test', handleEvent);
waitForCondition(condition, handleCondition, 1);
}
function handleTimeout() {
testObj.steps++;
assertEquals('handleTimeout should be called first.', 1, testObj.steps);
waitForTimeout(fireEvent, 10);
}
function fireEvent() {
testObj.steps++;
assertEquals('fireEvent should be called second.', 2, testObj.steps);
testObj.et.dispatchEvent('test');
}
function handleEvent() {
testObj.steps++;
assertEquals('handleEvent should be called third.', 3, testObj.steps);
testObj.lock = false;
}
function condition() {
return !testObj.lock;
}
function handleCondition() {
assertFalse(testObj.lock);
testObj.steps++;
assertEquals('handleCondition should be called last.', 4, testObj.steps);
}