| // 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); |
| } |