Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit eed776c

Browse files
vikermanmhevery
authored andcommitted
fix(spec): fix flush() behavior in handling periodic timers (#881)
FakeAsyncTest Zone now just finds the last task currently in the timer queue and tick-s till the endTime of that task. Solves the issue when there is a short running setInterval in front of a longer setInterval/setTimeout.
1 parent a03b84b commit eed776c

File tree

2 files changed

+71
-31
lines changed

2 files changed

+71
-31
lines changed

Diff for: lib/zone-spec/fake-async-test.ts

+46-31
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,13 @@
7070
}
7171
}
7272

73-
tick(millis: number = 0): void {
73+
tick(millis: number = 0, doTick?: (elapsed: number) => void): void {
7474
let finalTime = this._currentTime + millis;
75+
let lastCurrentTime = 0;
76+
if (this._schedulerQueue.length === 0 && doTick) {
77+
doTick(millis);
78+
return;
79+
}
7580
while (this._schedulerQueue.length > 0) {
7681
let current = this._schedulerQueue[0];
7782
if (finalTime < current.endTime) {
@@ -80,7 +85,11 @@
8085
} else {
8186
// Time to run scheduled function. Remove it from the head of queue.
8287
let current = this._schedulerQueue.shift();
88+
lastCurrentTime = this._currentTime;
8389
this._currentTime = current.endTime;
90+
if (doTick) {
91+
doTick(this._currentTime - lastCurrentTime);
92+
}
8493
let retval = current.func.apply(global, current.args);
8594
if (!retval) {
8695
// Uncaught exception in the current scheduled function. Stop processing the queue.
@@ -91,45 +100,51 @@
91100
this._currentTime = finalTime;
92101
}
93102

94-
flush(limit = 20, flushPeriodic = false, tick?: (elapsed: number) => void): number {
103+
flush(limit = 20, flushPeriodic = false, doTick?: (elapsed: number) => void): number {
104+
if (flushPeriodic) {
105+
return this.flushPeriodic(doTick);
106+
} else {
107+
return this.flushNonPeriodic(limit, doTick);
108+
}
109+
}
110+
111+
private flushPeriodic(doTick?: (elapsed: number) => void): number {
112+
if (this._schedulerQueue.length === 0) {
113+
return 0;
114+
}
115+
// Find the last task currently queued in the scheduler queue and tick
116+
// till that time.
117+
const startTime = this._currentTime;
118+
const lastTask = this._schedulerQueue[this._schedulerQueue.length - 1];
119+
this.tick(lastTask.endTime - startTime, doTick);
120+
return this._currentTime - startTime;
121+
}
122+
123+
private flushNonPeriodic(limit: number, doTick?: (elapsed: number) => void): number {
95124
const startTime = this._currentTime;
96-
let lastCurrentTime = this._currentTime;
125+
let lastCurrentTime = 0;
97126
let count = 0;
98-
const seenTimers: number[] = [];
99127
while (this._schedulerQueue.length > 0) {
100128
count++;
101129
if (count > limit) {
102130
throw new Error(
103131
'flush failed after reaching the limit of ' + limit +
104132
' tasks. Does your code use a polling timeout?');
105133
}
106-
if (!flushPeriodic) {
107-
// flush only non-periodic timers.
108-
// If the only remaining tasks are periodic(or requestAnimationFrame), finish flushing.
109-
if (this._schedulerQueue.filter(task => !task.isPeriodic && !task.isRequestAnimationFrame)
110-
.length === 0) {
111-
break;
112-
}
113-
} else {
114-
// flushPeriodic has been requested.
115-
// Stop when all timer id-s have been seen at least once.
116-
if (this._schedulerQueue
117-
.filter(
118-
task =>
119-
seenTimers.indexOf(task.id) === -1 || this._currentTime === task.endTime)
120-
.length === 0) {
121-
break;
122-
}
134+
135+
// flush only non-periodic timers.
136+
// If the only remaining tasks are periodic(or requestAnimationFrame), finish flushing.
137+
if (this._schedulerQueue.filter(task => !task.isPeriodic && !task.isRequestAnimationFrame)
138+
.length === 0) {
139+
break;
123140
}
141+
124142
const current = this._schedulerQueue.shift();
125-
if (seenTimers.indexOf(current.id) === -1) {
126-
seenTimers.push(current.id);
127-
}
128143
lastCurrentTime = this._currentTime;
129144
this._currentTime = current.endTime;
130-
if (tick) {
131-
// Tick any secondary schedulers like Jasmine mock Date.
132-
tick(this._currentTime - lastCurrentTime);
145+
if (doTick) {
146+
// Update any secondary schedulers like Jasmine mock Date.
147+
doTick(this._currentTime - lastCurrentTime);
133148
}
134149
const retval = current.func.apply(global, current.args);
135150
if (!retval) {
@@ -253,10 +268,10 @@
253268
throw error;
254269
}
255270

256-
tick(millis: number = 0): void {
271+
tick(millis: number = 0, doTick?: (elapsed: number) => void): void {
257272
FakeAsyncTestZoneSpec.assertInZone();
258273
this.flushMicrotasks();
259-
this._scheduler.tick(millis);
274+
this._scheduler.tick(millis, doTick);
260275
if (this._lastError !== null) {
261276
this._resetLastErrorAndThrow();
262277
}
@@ -277,10 +292,10 @@
277292
flushErrors();
278293
}
279294

280-
flush(limit?: number, flushPeriodic?: boolean, tick?: (elapsed: number) => void): number {
295+
flush(limit?: number, flushPeriodic?: boolean, doTick?: (elapsed: number) => void): number {
281296
FakeAsyncTestZoneSpec.assertInZone();
282297
this.flushMicrotasks();
283-
const elapsed = this._scheduler.flush(limit, flushPeriodic, tick);
298+
const elapsed = this._scheduler.flush(limit, flushPeriodic, doTick);
284299
if (this._lastError !== null) {
285300
this._resetLastErrorAndThrow();
286301
}

Diff for: test/zone-spec/fake-async-test.spec.ts

+25
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,31 @@ describe('FakeAsyncTestZoneSpec', () => {
557557
expect(y).toEqual(1);
558558
});
559559
});
560+
561+
it('can flush till the last periodic task is processed', () => {
562+
fakeAsyncTestZone.run(() => {
563+
let x = 0;
564+
let y = 0;
565+
566+
setInterval(() => {
567+
x++;
568+
}, 10);
569+
570+
// This shouldn't cause the flush to throw an exception even though
571+
// it would require 100 iterations of the shorter timer.
572+
setInterval(() => {
573+
y++;
574+
}, 1000);
575+
576+
let elapsed = testZoneSpec.flush(20, true);
577+
578+
// Should stop right after the longer timer has been processed.
579+
expect(elapsed).toEqual(1000);
580+
581+
expect(x).toEqual(100);
582+
expect(y).toEqual(1);
583+
});
584+
});
560585
});
561586

562587
describe('outside of FakeAsync Zone', () => {

0 commit comments

Comments
 (0)