| // Copyright 2007 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.MockClockTest'); |
| goog.setTestOnly('goog.testing.MockClockTest'); |
| |
| goog.require('goog.Promise'); |
| goog.require('goog.Timer'); |
| goog.require('goog.events'); |
| goog.require('goog.functions'); |
| goog.require('goog.testing.MockClock'); |
| goog.require('goog.testing.PropertyReplacer'); |
| goog.require('goog.testing.jsunit'); |
| goog.require('goog.testing.recordFunction'); |
| |
| var stubs = new goog.testing.PropertyReplacer(); |
| |
| function tearDown() { |
| stubs.reset(); |
| } |
| |
| function testMockClockWasInstalled() { |
| var clock = new goog.testing.MockClock(); |
| var originalTimeout = window.setTimeout; |
| clock.install(); |
| assertNotEquals(window.setTimeout, originalTimeout); |
| setTimeout(function() {}, 100); |
| assertEquals(1, clock.getTimeoutsMade()); |
| setInterval(function() {}, 200); |
| assertEquals(2, clock.getTimeoutsMade()); |
| clock.uninstall(); |
| assertEquals(window.setTimeout, originalTimeout); |
| assertNull(clock.replacer_); |
| } |
| |
| |
| function testSetTimeoutAndTick() { |
| var clock = new goog.testing.MockClock(true); |
| var m5 = false, m10 = false, m15 = false, m20 = false; |
| setTimeout(function() { m5 = true; }, 5); |
| setTimeout(function() { m10 = true; }, 10); |
| setTimeout(function() { m15 = true; }, 15); |
| setTimeout(function() { m20 = true; }, 20); |
| assertEquals(4, clock.getTimeoutsMade()); |
| |
| assertEquals(4, clock.tick(4)); |
| assertEquals(4, clock.getCurrentTime()); |
| |
| assertFalse(m5); |
| assertFalse(m10); |
| assertFalse(m15); |
| assertFalse(m20); |
| |
| assertEquals(5, clock.tick(1)); |
| assertEquals(5, clock.getCurrentTime()); |
| |
| assertTrue('m5 should now be true', m5); |
| assertFalse(m10); |
| assertFalse(m15); |
| assertFalse(m20); |
| |
| assertEquals(10, clock.tick(5)); |
| assertEquals(10, clock.getCurrentTime()); |
| |
| assertTrue('m5 should be true', m5); |
| assertTrue('m10 should now be true', m10); |
| assertFalse(m15); |
| assertFalse(m20); |
| |
| assertEquals(15, clock.tick(5)); |
| assertEquals(15, clock.getCurrentTime()); |
| |
| assertTrue('m5 should be true', m5); |
| assertTrue('m10 should be true', m10); |
| assertTrue('m15 should now be true', m15); |
| assertFalse(m20); |
| |
| assertEquals(20, clock.tick(5)); |
| assertEquals(20, clock.getCurrentTime()); |
| |
| assertTrue('m5 should be true', m5); |
| assertTrue('m10 should be true', m10); |
| assertTrue('m15 should be true', m15); |
| assertTrue('m20 should now be true', m20); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testSetImmediateAndTick() { |
| var clock = new goog.testing.MockClock(true); |
| var tick0 = false; |
| var tick1 = false; |
| setImmediate(function() { tick0 = true; }); |
| setImmediate(function() { tick1 = true; }); |
| assertEquals(2, clock.getTimeoutsMade()); |
| |
| clock.tick(0); |
| assertTrue(tick0); |
| assertTrue(tick1); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testSetInterval() { |
| var clock = new goog.testing.MockClock(true); |
| var times = 0; |
| setInterval(function() { times++; }, 100); |
| |
| clock.tick(500); |
| assertEquals(5, times); |
| clock.tick(100); |
| assertEquals(6, times); |
| clock.tick(100); |
| assertEquals(7, times); |
| clock.tick(50); |
| assertEquals(7, times); |
| clock.tick(50); |
| assertEquals(8, times); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testRequestAnimationFrame() { |
| goog.global.requestAnimationFrame = function() { |
| }; |
| var clock = new goog.testing.MockClock(true); |
| var times = []; |
| var recFunc = goog.testing.recordFunction(function(now) { |
| times.push(now); |
| }); |
| goog.global.requestAnimationFrame(recFunc); |
| clock.tick(50); |
| assertEquals(1, recFunc.getCallCount()); |
| assertEquals(20, times[0]); |
| |
| goog.global.requestAnimationFrame(recFunc); |
| clock.tick(100); |
| assertEquals(2, recFunc.getCallCount()); |
| assertEquals(70, times[1]); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testClearTimeout() { |
| var clock = new goog.testing.MockClock(true); |
| var ran = false; |
| var c = setTimeout(function() { ran = true; }, 100); |
| clock.tick(50); |
| assertFalse(ran); |
| clearTimeout(c); |
| clock.tick(100); |
| assertFalse(ran); |
| clock.uninstall(); |
| } |
| |
| |
| function testClearInterval() { |
| var clock = new goog.testing.MockClock(true); |
| var times = 0; |
| var c = setInterval(function() { times++; }, 100); |
| |
| clock.tick(500); |
| assertEquals(5, times); |
| clock.tick(100); |
| assertEquals(6, times); |
| clock.tick(100); |
| clearInterval(c); |
| assertEquals(7, times); |
| clock.tick(50); |
| assertEquals(7, times); |
| clock.tick(50); |
| assertEquals(7, times); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testClearInterval2() { |
| // Tests that we can clear the interval from inside the function |
| var clock = new goog.testing.MockClock(true); |
| var times = 0; |
| var c = setInterval(function() { |
| times++; |
| if (times == 6) { |
| clearInterval(c); |
| } |
| }, 100); |
| |
| clock.tick(500); |
| assertEquals(5, times); |
| clock.tick(100); |
| assertEquals(6, times); |
| clock.tick(100); |
| assertEquals(6, times); |
| clock.tick(50); |
| assertEquals(6, times); |
| clock.tick(50); |
| assertEquals(6, times); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testCancelRequestAnimationFrame() { |
| goog.global.requestAnimationFrame = function() { |
| }; |
| goog.global.cancelRequestAnimationFrame = function() { |
| }; |
| var clock = new goog.testing.MockClock(true); |
| var ran = false; |
| var c = goog.global.requestAnimationFrame(function() { ran = true; }); |
| clock.tick(10); |
| assertFalse(ran); |
| goog.global.cancelRequestAnimationFrame(c); |
| clock.tick(20); |
| assertFalse(ran); |
| clock.uninstall(); |
| } |
| |
| |
| function testMockGoogNow() { |
| assertNotEquals(0, goog.now()); |
| var clock = new goog.testing.MockClock(true); |
| assertEquals(0, goog.now()); |
| clock.tick(50); |
| assertEquals(50, goog.now()); |
| clock.uninstall(); |
| assertNotEquals(50, goog.now()); |
| } |
| |
| |
| function testTimeoutDelay() { |
| var clock = new goog.testing.MockClock(true); |
| var m5 = false, m10 = false, m20 = false; |
| setTimeout(function() { m5 = true; }, 5); |
| setTimeout(function() { m10 = true; }, 10); |
| setTimeout(function() { m20 = true; }, 20); |
| |
| // Fire 3ms early, so m5 fires at t=2 |
| clock.setTimeoutDelay(-3); |
| clock.tick(1); |
| assertFalse(m5); |
| assertFalse(m10); |
| clock.tick(1); |
| assertTrue(m5); |
| assertFalse(m10); |
| |
| // Fire 3ms late, so m10 fires at t=13 |
| clock.setTimeoutDelay(3); |
| assertEquals(12, clock.tick(10)); |
| assertEquals(12, clock.getCurrentTime()); |
| assertFalse(m10); |
| clock.tick(1); |
| assertTrue(m10); |
| assertFalse(m20); |
| |
| // Fire 10ms early, so m20 fires now, since it's after t=10 |
| clock.setTimeoutDelay(-10); |
| assertFalse(m20); |
| assertEquals(14, clock.tick(1)); |
| assertEquals(14, clock.getCurrentTime()); |
| assertTrue(m20); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testTimerCallbackCanCreateIntermediateTimer() { |
| var clock = new goog.testing.MockClock(true); |
| var sequence = []; |
| |
| // Create 3 timers: 1, 2, and 3. Timer 1 should fire at T=1, timer 2 at |
| // T=2, and timer 3 at T=3. The catch: Timer 2 is created by the |
| // callback within timer 0. |
| |
| // Testing method: Create a simple string sequencing each timer and at |
| // what time it fired. |
| |
| setTimeout(function() { |
| sequence.push('timer1 at T=' + goog.now()); |
| setTimeout(function() { |
| sequence.push('timer2 at T=' + goog.now()); |
| }, 1); |
| }, 1); |
| |
| setTimeout(function() { |
| sequence.push('timer3 at T=' + goog.now()); |
| }, 3); |
| |
| clock.tick(4); |
| |
| assertEquals( |
| 'Each timer should fire in sequence at the correct time.', |
| 'timer1 at T=1, timer2 at T=2, timer3 at T=3', |
| sequence.join(', ')); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testCorrectArgumentsPassedToCallback() { |
| var clock = new goog.testing.MockClock(true); |
| var timeoutId; |
| var timeoutExecuted = false; |
| |
| timeoutId = setTimeout(function(arg) { |
| assertEquals('"this" must be goog.global', |
| goog.global, this); |
| assertEquals('The timeout ID must be the first parameter', |
| timeoutId, arg); |
| assertEquals('Exactly one argument must be passed', |
| 1, arguments.length); |
| timeoutExecuted = true; |
| }, 1); |
| |
| clock.tick(4); |
| |
| assertTrue('The timeout was not executed', timeoutExecuted); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testTickZero() { |
| var clock = new goog.testing.MockClock(true); |
| var calls = 0; |
| |
| setTimeout(function() { |
| assertEquals('I need to be first', 0, calls); |
| calls++; |
| }, 0); |
| |
| setTimeout(function() { |
| assertEquals('I need to be second', 1, calls); |
| calls++; |
| }, 0); |
| |
| clock.tick(0); |
| assertEquals(2, calls); |
| |
| setTimeout(function() { |
| assertEquals('I need to be third', 2, calls); |
| calls++; |
| }, 0); |
| |
| clock.tick(0); |
| assertEquals(3, calls); |
| |
| assertEquals('Time should still be zero', 0, goog.now()); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testReset() { |
| var clock = new goog.testing.MockClock(true); |
| |
| setTimeout(function() { |
| fail('Timeouts should be cleared after a reset'); |
| }, 0); |
| |
| clock.reset(); |
| clock.tick(999999); |
| clock.uninstall(); |
| } |
| |
| |
| function testQueueInsertionHelper() { |
| var queue = []; |
| |
| function queueToString() { |
| var buffer = []; |
| for (var i = 0; i < queue.length; i++) { |
| buffer.push(queue[i].runAtMillis); |
| } |
| return buffer.join(','); |
| } |
| |
| goog.testing.MockClock.insert_({runAtMillis: 2}, queue); |
| assertEquals('Only item', |
| '2', queueToString()); |
| |
| goog.testing.MockClock.insert_({runAtMillis: 4}, queue); |
| assertEquals('Biggest item', |
| '4,2', queueToString()); |
| |
| goog.testing.MockClock.insert_({runAtMillis: 5}, queue); |
| assertEquals('An even bigger item', |
| '5,4,2', queueToString()); |
| |
| goog.testing.MockClock.insert_({runAtMillis: 1}, queue); |
| assertEquals('Smallest item', |
| '5,4,2,1', queueToString()); |
| |
| goog.testing.MockClock.insert_({runAtMillis: 1, dup: true}, queue); |
| assertEquals('Duplicate smallest item', |
| '5,4,2,1,1', queueToString()); |
| assertTrue('Duplicate item comes at a smaller index', queue[3].dup); |
| |
| goog.testing.MockClock.insert_({runAtMillis: 3}, queue); |
| goog.testing.MockClock.insert_({runAtMillis: 3, dup: true}, queue); |
| assertEquals('Duplicate a middle item', |
| '5,4,3,3,2,1,1', queueToString()); |
| assertTrue('Duplicate item comes at a smaller index', queue[2].dup); |
| } |
| |
| |
| function testIsTimeoutSet() { |
| var clock = new goog.testing.MockClock(true); |
| var timeoutKey = setTimeout(function() {}, 1); |
| assertTrue('Timeout ' + timeoutKey + ' should be set', |
| clock.isTimeoutSet(timeoutKey)); |
| var nextTimeoutKey = timeoutKey + 1; |
| assertFalse('Timeout ' + nextTimeoutKey + ' should not be set', |
| clock.isTimeoutSet(nextTimeoutKey)); |
| clearTimeout(timeoutKey); |
| assertFalse('Timeout ' + timeoutKey + ' should no longer be set', |
| clock.isTimeoutSet(timeoutKey)); |
| var newTimeoutKey = setTimeout(function() {}, 1); |
| clock.tick(5); |
| assertFalse('Timeout ' + timeoutKey + ' should not be set', |
| clock.isTimeoutSet(timeoutKey)); |
| assertTrue('Timeout ' + newTimeoutKey + ' should be set', |
| clock.isTimeoutSet(newTimeoutKey)); |
| clock.uninstall(); |
| } |
| |
| |
| function testBalksOnTimeoutsGreaterThanMaxInt() { |
| // Browsers have trouble with timeout greater than max int, so we |
| // want Mock Clock to fail if this happens. |
| var clock = new goog.testing.MockClock(true); |
| // Functions on window don't seem to be able to throw exceptions in |
| // IE6. Explicitly reading the property makes it work. |
| var setTimeout = window.setTimeout; |
| assertThrows('Timeouts > MAX_INT should fail', |
| function() { |
| setTimeout(goog.nullFunction, 2147483648); |
| }); |
| assertThrows('Timeouts much greater than MAX_INT should fail', |
| function() { |
| setTimeout(goog.nullFunction, 2147483648 * 10); |
| }); |
| clock.uninstall(); |
| } |
| |
| |
| function testCorrectSetTimeoutIsRestored() { |
| var safe = goog.functions.error('should not have been called'); |
| stubs.set(window, 'setTimeout', safe); |
| |
| var clock = new goog.testing.MockClock(true); |
| assertNotEquals('setTimeout is replaced', safe, window.setTimeout); |
| clock.uninstall(); |
| // NOTE: If this assertion proves to be flaky in IE, the string value of |
| // the two functions have to be compared as described in |
| // goog.testing.TestCase#finalize. |
| assertEquals('setTimeout is restored', safe, window.setTimeout); |
| } |
| |
| |
| function testMozRequestAnimationFrame() { |
| // Setting this function will indirectly tell the mock clock to mock it out. |
| stubs.set(window, 'mozRequestAnimationFrame', goog.nullFunction); |
| |
| var clock = new goog.testing.MockClock(true); |
| |
| var mozBeforePaint = goog.testing.recordFunction(); |
| goog.events.listen(window, 'MozBeforePaint', mozBeforePaint); |
| |
| window.mozRequestAnimationFrame(null); |
| assertEquals(0, mozBeforePaint.getCallCount()); |
| |
| clock.tick(goog.testing.MockClock.REQUEST_ANIMATION_FRAME_TIMEOUT); |
| assertEquals(1, mozBeforePaint.getCallCount()); |
| clock.dispose(); |
| } |
| |
| |
| function testClearBeforeSet() { |
| var clock = new goog.testing.MockClock(true); |
| var expectedId = 1; |
| window.clearTimeout(expectedId); |
| |
| var fn = goog.testing.recordFunction(); |
| var actualId = window.setTimeout(fn, 0); |
| assertEquals( |
| 'In order for this test to work, we have to guess the ids in advance', |
| expectedId, actualId); |
| clock.tick(1); |
| assertEquals(1, fn.getCallCount()); |
| clock.dispose(); |
| } |
| |
| |
| function testNonFunctionArguments() { |
| var clock = new goog.testing.MockClock(true); |
| |
| // Unlike normal setTimeout and friends, we only accept functions (not |
| // strings, not undefined, etc). Make sure that if we get a non-function, we |
| // fail early rather than on the next .tick() operation. |
| |
| assertThrows('setTimeout with a non-function value should fail', |
| function() { |
| window.setTimeout(undefined, 0); |
| }); |
| clock.tick(1); |
| |
| assertThrows('setTimeout with a string should fail', |
| function() { |
| window.setTimeout('throw new Error("setTimeout string eval!");', 0); |
| }); |
| clock.tick(1); |
| |
| clock.dispose(); |
| } |
| |
| |
| function testUnspecifiedTimeout() { |
| var clock = new goog.testing.MockClock(true); |
| var m0a = false, m0b = false, m10 = false; |
| setTimeout(function() { m0a = true; }); |
| setTimeout(function() { m10 = true; }, 10); |
| assertEquals(2, clock.getTimeoutsMade()); |
| |
| assertFalse(m0a); |
| assertFalse(m0b); |
| assertFalse(m10); |
| |
| assertEquals(0, clock.tick(0)); |
| assertEquals(0, clock.getCurrentTime()); |
| |
| assertTrue(m0a); |
| assertFalse(m0b); |
| assertFalse(m10); |
| |
| setTimeout(function() { m0b = true; }); |
| assertEquals(3, clock.getTimeoutsMade()); |
| |
| assertEquals(0, clock.tick(0)); |
| assertEquals(0, clock.getCurrentTime()); |
| |
| assertTrue(m0a); |
| assertTrue(m0b); |
| assertFalse(m10); |
| |
| assertEquals(10, clock.tick(10)); |
| assertEquals(10, clock.getCurrentTime()); |
| |
| assertTrue(m0a); |
| assertTrue(m0b); |
| assertTrue(m10); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testUnspecifiedInterval() { |
| var clock = new goog.testing.MockClock(true); |
| var times = 0; |
| var handle = setInterval(function() { |
| if (++times >= 5) { |
| clearInterval(handle); |
| } |
| }); |
| |
| clock.tick(0); |
| assertEquals(5, times); |
| |
| clock.uninstall(); |
| } |
| |
| |
| function testTickPromise() { |
| var clock = new goog.testing.MockClock(true); |
| |
| var p = goog.Promise.resolve('foo'); |
| assertEquals('foo', clock.tickPromise(p)); |
| |
| var rejected = goog.Promise.reject(new Error('failed')); |
| var e = assertThrows(function() { |
| clock.tickPromise(rejected); |
| }); |
| assertEquals('failed', e.message); |
| |
| var delayed = goog.Timer.promise(500, 'delayed'); |
| e = assertThrows(function() { |
| clock.tickPromise(delayed); |
| }); |
| assertEquals('Promise was expected to be resolved after mock clock tick.', |
| e.message); |
| assertEquals('delayed', clock.tickPromise(delayed, 500)); |
| |
| clock.dispose(); |
| } |