Skip to content

Commit b9d3dca

Browse files
committed
test: refactor unit tests to be more clear
1 parent a82228b commit b9d3dca

File tree

1 file changed

+133
-115
lines changed

1 file changed

+133
-115
lines changed

test/unit/utils.test.js

Lines changed: 133 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
const {
33
eachAsync,
44
executeLegacyOperation,
5-
now,
65
makeInterruptibleAsyncInterval,
76
BufferPool
87
} = require('../../src/utils');
@@ -41,133 +40,152 @@ describe('utils', function () {
4140
});
4241
});
4342

44-
context('makeInterruptibleAsyncInterval', function () {
45-
before(function () {
46-
this.clock = sinon.useFakeTimers();
47-
});
43+
describe('#makeInterruptibleAsyncInterval', function () {
44+
let clock;
4845

49-
after(function () {
50-
this.clock.restore();
46+
beforeEach(function () {
47+
clock = sinon.useFakeTimers();
5148
});
5249

53-
it('should execute a method in an repeating interval', function (done) {
54-
let lastTime = now();
55-
const marks = [];
56-
const executor = makeInterruptibleAsyncInterval(
57-
callback => {
58-
marks.push(now() - lastTime);
59-
lastTime = now();
60-
callback();
61-
},
62-
{ interval: 10 }
63-
);
64-
65-
setTimeout(() => {
66-
expect(marks).to.eql([10, 10, 10, 10, 10]);
67-
expect(marks.every(mark => marks[0] === mark)).to.be.true;
68-
executor.stop();
69-
done();
70-
}, 51);
71-
72-
this.clock.tick(51);
50+
afterEach(function () {
51+
clock.restore();
7352
});
7453

75-
it('should schedule execution sooner if requested within min interval threshold', function (done) {
76-
let lastTime = now();
77-
const marks = [];
78-
const executor = makeInterruptibleAsyncInterval(
79-
callback => {
80-
marks.push(now() - lastTime);
81-
lastTime = now();
82-
callback();
83-
},
84-
{ interval: 50, minInterval: 10 }
85-
);
86-
87-
// immediately schedule execution
88-
executor.wake();
89-
90-
setTimeout(() => {
91-
expect(marks).to.eql([10, 50]);
92-
executor.stop();
93-
done();
94-
}, 100);
95-
96-
this.clock.tick(100);
54+
context('when the immediate option is provided', function () {
55+
const fn = callback => {
56+
callback();
57+
};
58+
const fnSpy = sinon.spy(fn);
59+
60+
it('executes the function immediately', function (done) {
61+
const executor = makeInterruptibleAsyncInterval(fnSpy, { immediate: true, interval: 20 });
62+
setTimeout(() => {
63+
// The provided function should be called exactly once, since we wait 10ms
64+
// to perform the assertion and the interval is 20ms, so the executor is
65+
// stopped before the scheduled next call.
66+
expect(fnSpy.calledOnce).to.be.true;
67+
executor.stop();
68+
done();
69+
}, 10);
70+
clock.tick(10);
71+
});
9772
});
9873

99-
it('should debounce multiple requests to wake the interval sooner', function (done) {
100-
let lastTime = now();
101-
const marks = [];
102-
const executor = makeInterruptibleAsyncInterval(
103-
callback => {
104-
marks.push(now() - lastTime);
105-
lastTime = now();
106-
callback();
107-
},
108-
{ interval: 50, minInterval: 10 }
109-
);
110-
111-
for (let i = 0; i < 100; ++i) {
112-
executor.wake();
113-
}
114-
115-
setTimeout(() => {
116-
expect(marks).to.eql([10, 50, 50, 50, 50]);
117-
executor.stop();
118-
done();
119-
}, 250);
120-
121-
this.clock.tick(250);
74+
context('when the immediate option is not provided', function () {
75+
const fn = callback => {
76+
callback();
77+
};
78+
const fnSpy = sinon.spy(fn);
79+
80+
it('executes the function on the provided interval', function (done) {
81+
const executor = makeInterruptibleAsyncInterval(fnSpy, { interval: 10 });
82+
setTimeout(() => {
83+
// The provided function should be called exactly twice, since we wait 21ms
84+
// to perform the assertion and the interval is 10ms, so the executor is
85+
// stopped before the third call.
86+
expect(fnSpy.calledTwice).to.be.true;
87+
executor.stop();
88+
done();
89+
}, 21);
90+
clock.tick(21);
91+
});
12292
});
12393

124-
it('should immediately schedule if the clock is unreliable', function (done) {
125-
let clockCalled = 0;
126-
let lastTime = now();
127-
const marks = [];
128-
const executor = makeInterruptibleAsyncInterval(
129-
callback => {
130-
marks.push(now() - lastTime);
131-
lastTime = now();
94+
describe('#wake', function () {
95+
context('when the time until next call is negative', function () {
96+
const fn = callback => {
13297
callback();
133-
},
134-
{
135-
interval: 50,
136-
minInterval: 10,
137-
immediate: true,
138-
clock() {
139-
clockCalled += 1;
140-
141-
// needs to happen on the third call because `wake` checks
142-
// the `currentTime` at the beginning of the function
143-
// The value of now() is not actually negative in the case of
144-
// the unreliable check so we force to a negative value now
145-
// for this test.
146-
if (clockCalled === 3) {
147-
return -1;
98+
};
99+
const fnSpy = sinon.spy(fn);
100+
101+
it('calls the function immediately', function (done) {
102+
const executor = makeInterruptibleAsyncInterval(fnSpy, {
103+
interval: 10,
104+
clock: () => {
105+
// We have our fake clock return a value that will force
106+
// the time until the next call to be a negative value,
107+
// which will in turn force an immediate execution upon
108+
// wake.
109+
return 11;
148110
}
111+
});
112+
113+
// This will reset the last call time to 0 and ensure the function has
114+
// not been called yet.
115+
executor.stop();
116+
// Now we call our method under test with the expectation it will force
117+
// an immediate execution.
118+
executor.wake();
119+
120+
setTimeout(() => {
121+
// The provided function should be called exactly once in this section.
122+
// This is because we immediately stopped the executor, then force woke
123+
// it to get an immediate call with time until the next call being a
124+
// negative value.
125+
expect(fnSpy.calledOnce).to.be.true;
126+
executor.stop();
127+
done();
128+
}, 10);
129+
clock.tick(11);
130+
});
131+
});
149132

150-
return now();
151-
}
152-
}
153-
);
133+
context('when time since last wake is less than the minimum interval', function () {
134+
const fn = callback => {
135+
callback();
136+
};
137+
const fnSpy = sinon.spy(fn);
138+
139+
it('does not call the function', function (done) {
140+
const executor = makeInterruptibleAsyncInterval(fnSpy, { interval: 10 });
141+
142+
// This will reset the last wake time to 0 and ensure the function has
143+
// not been called yet.
144+
executor.stop();
145+
// Now we call our method under test with the expectation it will not be
146+
// called immediately since our current time is still under the interval
147+
// time.
148+
executor.wake();
149+
150+
setTimeout(() => {
151+
// The provided function should never be called in this case.
152+
// This is because we immediately stopped the executor, then force woke
153+
// it but the current time is still under the interval time.
154+
expect(fnSpy.callCount).to.equal(0);
155+
executor.stop();
156+
done();
157+
}, 9);
158+
clock.tick(9);
159+
});
160+
});
154161

155-
// force mark at 20ms, and then the unreliable system clock
156-
// will report a very stale `lastCallTime` on this mark.
157-
setTimeout(() => executor.wake(), 10);
158-
159-
// try to wake again in another `minInterval + immediate`, now
160-
// using a very old `lastCallTime`. This should result in an
161-
// immediate scheduling: 0ms (immediate), 20ms (wake with minIterval)
162-
// and then 10ms for another immediate.
163-
setTimeout(() => executor.wake(), 30);
164-
165-
setTimeout(() => {
166-
executor.stop();
167-
expect(marks).to.eql([0, 20, 10, 50, 50, 50, 50]);
168-
done();
169-
}, 250);
170-
this.clock.tick(250);
162+
context('when time since last call is greater than the minimum interval', function () {
163+
const fn = callback => {
164+
callback();
165+
};
166+
const fnSpy = sinon.spy(fn);
167+
168+
it('reschedules the function call for the minimum interval', function (done) {
169+
const executor = makeInterruptibleAsyncInterval(fnSpy, {
170+
interval: 50,
171+
minInterval: 10
172+
});
173+
174+
// Calling wake here will force the reschedule to happen at the minimum interval
175+
// provided, which is 10ms.
176+
executor.wake();
177+
178+
setTimeout(() => {
179+
// We expect function calls to happen after 10ms, which is the minimum interval,
180+
// and then in 50ms intervals after that. The second call would happen at 60ms
181+
// time from the original call so we've stopped the executor before a third.
182+
expect(fnSpy.calledTwice).to.be.true;
183+
executor.stop();
184+
done();
185+
}, 61);
186+
clock.tick(61);
187+
});
188+
});
171189
});
172190
});
173191

0 commit comments

Comments
 (0)