From 2af9a45fb27baecbda32534c4ac91830a860f020 Mon Sep 17 00:00:00 2001 From: Steve Gravrock Date: Mon, 2 Mar 2015 21:45:12 -0800 Subject: [PATCH] Correctly handle functions that are scheduled after the clock is uninstalled and reinstalled from within Clock#tick. Fixes #790. --- spec/core/ClockSpec.js | 71 +++++++++++++++-------- spec/core/DelayedFunctionSchedulerSpec.js | 41 ------------- src/core/Clock.js | 6 +- src/core/DelayedFunctionScheduler.js | 7 --- src/core/Env.js | 2 +- 5 files changed, 52 insertions(+), 75 deletions(-) diff --git a/spec/core/ClockSpec.js b/spec/core/ClockSpec.js index e4682857..cda0d448 100644 --- a/spec/core/ClockSpec.js +++ b/spec/core/ClockSpec.js @@ -6,7 +6,7 @@ describe("Clock", function() { delayedFunctionScheduler = jasmine.createSpyObj("delayedFunctionScheduler", ["scheduleFunction"]), delayedFn = jasmine.createSpy("delayedFn"), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); fakeGlobal.setTimeout(delayedFn, 0); @@ -28,7 +28,7 @@ describe("Clock", function() { delayedFunctionScheduler = jasmine.createSpyObj("delayedFunctionScheduler", ["removeFunctionWithId"]), delayedFn = jasmine.createSpy("delayedFn"), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); fakeGlobal.clearTimeout("foo"); @@ -50,7 +50,7 @@ describe("Clock", function() { delayedFunctionScheduler = jasmine.createSpyObj("delayedFunctionScheduler", ["scheduleFunction"]), delayedFn = jasmine.createSpy("delayedFn"), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); fakeGlobal.setInterval(delayedFn, 0); @@ -72,7 +72,7 @@ describe("Clock", function() { delayedFunctionScheduler = jasmine.createSpyObj("delayedFunctionScheduler", ["removeFunctionWithId"]), delayedFn = jasmine.createSpy("delayedFn"), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); fakeGlobal.clearInterval("foo"); @@ -102,7 +102,7 @@ describe("Clock", function() { delayedFunctionScheduler = jasmine.createSpyObj("delayedFunctionScheduler", ["scheduleFunction", "reset"]), delayedFn = jasmine.createSpy("delayedFn"), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); clock.install(); clock.uninstall(); @@ -132,7 +132,7 @@ describe("Clock", function() { delayedFunctionScheduler = jasmine.createSpyObj("delayedFunctionScheduler", ["scheduleFunction", "reset", "removeFunctionWithId"]), delayedFn = jasmine.createSpy("delayedFn"), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate), + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate), passedFunctionCalled = false; clock.withMock(function() { @@ -179,7 +179,7 @@ describe("Clock", function() { delayedFunctionScheduler = jasmine.createSpyObj("delayedFunctionScheduler", ["scheduleFunction", "reset", "removeFunctionWithId"]), delayedFn = jasmine.createSpy("delayedFn"), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate), + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate), passedFunctionCalled = false; expect(function() { @@ -222,7 +222,7 @@ describe("Clock", function() { fakeGlobal = { setTimeout: fakeSetTimeout }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); clock.install(); clock.setTimeout(delayedFn, 0, 'a', 'b'); @@ -239,7 +239,7 @@ describe("Clock", function() { fakeGlobal = { setTimeout: fakeSetTimeout }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate), + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate), timeoutId; clock.install(); @@ -254,7 +254,7 @@ describe("Clock", function() { fakeGlobal = { setTimeout: fakeClearTimeout }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); clock.install(); clock.clearTimeout(123); @@ -270,7 +270,7 @@ describe("Clock", function() { fakeGlobal = { setInterval: fakeSetInterval }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); clock.install(); clock.setInterval(delayedFn, 0, 'a', 'b'); @@ -287,7 +287,7 @@ describe("Clock", function() { fakeGlobal = { setInterval: fakeSetInterval }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate), + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate), intervalId; clock.install(); @@ -302,7 +302,7 @@ describe("Clock", function() { fakeGlobal = { setInterval: clearInterval }, delayedFn = jasmine.createSpy('delayedFn'), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); clock.install(); clock.clearInterval(123); @@ -329,7 +329,7 @@ describe("Clock", function() { setInterval: fakeSetInterval }, mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock(fakeGlobal, delayedFunctionScheduler, mockDate); + clock = new j$.Clock(fakeGlobal, function () { return delayedFunctionScheduler; }, mockDate); fakeSetTimeout.apply = null; fakeSetInterval.apply = null; @@ -348,7 +348,6 @@ describe("Clock", function() { clock.setInterval(fn, 0, 'extra'); }).toThrow(); }); - }); describe("Clock (acceptance)", function() { @@ -359,7 +358,7 @@ describe("Clock (acceptance)", function() { recurring1 = jasmine.createSpy('recurring1'), delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock({setTimeout: setTimeout}, delayedFunctionScheduler, mockDate); + clock = new j$.Clock({setTimeout: setTimeout}, function () { return delayedFunctionScheduler; }, mockDate); clock.install(); @@ -406,7 +405,7 @@ describe("Clock (acceptance)", function() { var clearedFn = jasmine.createSpy('clearedFn'), delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock({setTimeout: function() {}}, delayedFunctionScheduler, mockDate), + clock = new j$.Clock({setTimeout: function() {}}, function () { return delayedFunctionScheduler; }, mockDate), timeoutId; clock.install(); @@ -424,7 +423,7 @@ describe("Clock (acceptance)", function() { var spy = jasmine.createSpy('spy'), delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock({setInterval: function() {}}, delayedFunctionScheduler, mockDate), + clock = new j$.Clock({setInterval: function() {}}, function () { return delayedFunctionScheduler; }, mockDate), intervalId; clock.install(); @@ -442,7 +441,7 @@ describe("Clock (acceptance)", function() { var delayedFn1 = jasmine.createSpy('delayedFn1'), delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock({setTimeout: function() {}}, delayedFunctionScheduler, mockDate); + clock = new j$.Clock({setTimeout: function() {}}, function () { return delayedFunctionScheduler; }, mockDate); clock.install(); @@ -459,7 +458,7 @@ describe("Clock (acceptance)", function() { delayedFn2 = jasmine.createSpy('delayedFn2'), delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock({setTimeout: function() {}}, delayedFunctionScheduler, mockDate); + clock = new j$.Clock({setTimeout: function() {}}, function () { return delayedFunctionScheduler; }, mockDate); delayedFn1.and.callFake(function() { clock.setTimeout(delayedFn2, 0); }); clock.install(); @@ -478,7 +477,7 @@ describe("Clock (acceptance)", function() { delayedFn2 = jasmine.createSpy('delayedFn2'), delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, - clock = new j$.Clock({setTimeout: function() {}}, delayedFunctionScheduler, mockDate); + clock = new j$.Clock({setTimeout: function() {}}, function () { return delayedFunctionScheduler; }, mockDate); delayedFn1.and.callFake(function() { clock.setTimeout(delayedFn2, 1); }); clock.install(); @@ -489,11 +488,35 @@ describe("Clock (acceptance)", function() { expect(delayedFn2).toHaveBeenCalled(); }); + it("correctly schedules functions scheduled while the Clock is advancing but after the Clock is uninstalled", function() { + var delayedFn1 = jasmine.createSpy('delayedFn1'), + delayedFn2 = jasmine.createSpy('delayedFn2'), + delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), + mockDate = { install: function() {}, tick: function() {}, uninstall: function() {} }, + clock = new j$.Clock({setTimeout: function() {}}, function () { return delayedFunctionScheduler; }, mockDate); + + delayedFn1.and.callFake(function() { + clock.uninstall(); + clock.install(); + clock.setTimeout(delayedFn2, 0); + }); + + clock.install(); + clock.setTimeout(delayedFn1, 1); + + clock.tick(1); + expect(delayedFn1).toHaveBeenCalled(); + expect(delayedFn2).not.toHaveBeenCalled(); + + clock.tick(1); + expect(delayedFn2).toHaveBeenCalled(); + }); + it("does not mock the Date object by default", function() { var delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), global = {Date: Date}, mockDate = new j$.MockDate(global), - clock = new j$.Clock({setTimeout: setTimeout}, delayedFunctionScheduler, mockDate); + clock = new j$.Clock({setTimeout: setTimeout}, function () { return delayedFunctionScheduler; }, mockDate); clock.install(); @@ -510,7 +533,7 @@ describe("Clock (acceptance)", function() { var delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), global = {Date: Date}, mockDate = new j$.MockDate(global), - clock = new j$.Clock({setTimeout: setTimeout}, delayedFunctionScheduler, mockDate); + clock = new j$.Clock({setTimeout: setTimeout}, function () { return delayedFunctionScheduler; }, mockDate); clock.install().mockDate(); @@ -534,7 +557,7 @@ describe("Clock (acceptance)", function() { var delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), global = {Date: Date}, mockDate = new j$.MockDate(global), - clock = new j$.Clock({setTimeout: setTimeout}, delayedFunctionScheduler, mockDate), + clock = new j$.Clock({setTimeout: setTimeout}, function () { return delayedFunctionScheduler; }, mockDate), baseTime = new Date(2013, 9, 23); diff --git a/spec/core/DelayedFunctionSchedulerSpec.js b/spec/core/DelayedFunctionSchedulerSpec.js index 6b5e2905..9047a0a8 100644 --- a/spec/core/DelayedFunctionSchedulerSpec.js +++ b/spec/core/DelayedFunctionSchedulerSpec.js @@ -113,47 +113,6 @@ describe("DelayedFunctionScheduler", function() { expect(fn).not.toHaveBeenCalled(); }); - it("reset removes scheduled functions", function() { - var scheduler = new j$.DelayedFunctionScheduler(), - fn = jasmine.createSpy('fn'); - - scheduler.scheduleFunction(fn, 0); - - expect(fn).not.toHaveBeenCalled(); - - scheduler.reset(); - - scheduler.tick(0); - - expect(fn).not.toHaveBeenCalled(); - }); - - it("reset resets the returned ids", function() { - var scheduler = new j$.DelayedFunctionScheduler(); - expect(scheduler.scheduleFunction(function() { }, 0)).toBe(1); - expect(scheduler.scheduleFunction(function() { }, 0, [], false, 123)).toBe(123); - - scheduler.reset(); - expect(scheduler.scheduleFunction(function() { }, 0)).toBe(1); - expect(scheduler.scheduleFunction(function() { }, 0, [], false, 123)).toBe(123); - }); - - it("reset resets the current tick time", function() { - var scheduler = new j$.DelayedFunctionScheduler(), - fn = jasmine.createSpy('fn'); - - expect(fn).not.toHaveBeenCalled(); - - scheduler.tick(15); - scheduler.reset(); - - scheduler.scheduleFunction(fn, 20, [], false, 1, 20); - - scheduler.tick(5); - - expect(fn).not.toHaveBeenCalled(); - }); - it("executes recurring functions interleaved with regular functions in the correct order", function() { var scheduler = new j$.DelayedFunctionScheduler(), fn = jasmine.createSpy('fn'), diff --git a/src/core/Clock.js b/src/core/Clock.js index b0267b4d..e07e9217 100644 --- a/src/core/Clock.js +++ b/src/core/Clock.js @@ -1,5 +1,5 @@ getJasmineRequireObj().Clock = function() { - function Clock(global, delayedFunctionScheduler, mockDate) { + function Clock(global, delayedFunctionSchedulerFactory, mockDate) { var self = this, realTimingFunctions = { setTimeout: global.setTimeout, @@ -14,19 +14,21 @@ getJasmineRequireObj().Clock = function() { clearInterval: clearInterval }, installed = false, + delayedFunctionScheduler, timer; self.install = function() { replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; + delayedFunctionScheduler = delayedFunctionSchedulerFactory(); installed = true; return self; }; self.uninstall = function() { - delayedFunctionScheduler.reset(); + delayedFunctionScheduler = null; mockDate.uninstall(); replace(global, realTimingFunctions); diff --git a/src/core/DelayedFunctionScheduler.js b/src/core/DelayedFunctionScheduler.js index 99fab4e6..de947c60 100644 --- a/src/core/DelayedFunctionScheduler.js +++ b/src/core/DelayedFunctionScheduler.js @@ -72,13 +72,6 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { } }; - self.reset = function() { - currentTime = 0; - scheduledLookup = []; - scheduledFunctions = {}; - delayedFnCount = 0; - }; - return self; function indexOfFirstToPass(array, testFn) { diff --git a/src/core/Env.js b/src/core/Env.js index 7b4566b5..1754856e 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -11,7 +11,7 @@ getJasmineRequireObj().Env = function(j$) { var realSetTimeout = j$.getGlobal().setTimeout; var realClearTimeout = j$.getGlobal().clearTimeout; - this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global)); + this.clock = new j$.Clock(global, function () { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global)); var runnableLookupTable = {}; var runnableResources = {};