22
33const { tspl } = require ( '@matteo.collina/tspl' )
44const { describe, test } = require ( 'node:test' )
5+ const FakeTimers = require ( '@sinonjs/fake-timers' )
6+
7+ const clock = FakeTimers . install ( )
58
69const timers = require ( '../lib/util/timers' )
710const { eventLoopBlocker } = require ( './utils/event-loop-blocker' )
811
12+ // timers.setTimeout implements a low resolution timer with a 500 ms granularity
13+ // It is expected that in the worst case, a timer will fire about 500 ms after the
14+ // intended amount of time, an extra 200 ms is added to account event loop overhead
15+ // Timers should never fire excessively early, 1ms early is tolerated
16+ const ACCEPTABLE_DELTA = 700
17+
18+ function tick ( duration ) {
19+ for ( let i = 0 ; i < duration ; ++ i ) {
20+ clock . tick ( 1 )
21+ }
22+ }
23+
924describe ( 'timers' , ( ) => {
1025 test ( 'timers exports a clearTimeout' , ( t ) => {
1126 t = tspl ( t , { plan : 1 } )
@@ -26,20 +41,19 @@ describe('timers', () => {
2641 t . strictEqual ( timers . setTimeout ( ( ) => { } , 1e3 ) [ timers . kFastTimer ] , undefined )
2742 } )
2843
29- test ( 'setTimeout instantiates a FastTimer when delay is smaller than 1e3 ms' , ( t ) => {
44+ test ( 'setTimeout instantiates a FastTimer when delay is bigger than 1e3 ms' , ( t ) => {
3045 t = tspl ( t , { plan : 1 } )
3146
3247 const timeout = timers . setTimeout ( ( ) => { } , 1001 )
3348 t . strictEqual ( timeout [ timers . kFastTimer ] , true )
3449 } )
3550
3651 test ( 'clearTimeout can clear a node native Timeout' , ( t ) => {
37- t = tspl ( t , { plan : 3 } )
52+ t = tspl ( t , { plan : 1 } )
3853
39- const nativeTimeoutId = setTimeout ( ( ) => { } , 1e6 )
40- t . equal ( nativeTimeoutId . _idleTimeout , 1e6 )
54+ const nativeTimeoutId = setTimeout ( ( ) => { t . fail ( ) } , 1 )
4155 t . ok ( timers . clearTimeout ( nativeTimeoutId ) === undefined )
42- t . equal ( nativeTimeoutId . _idleTimeout , - 1 )
56+ tick ( 10 )
4357 } )
4458
4559 test ( 'a FastTimer will get a _idleStart value after short time' , async ( t ) => {
@@ -51,7 +65,8 @@ describe('timers', () => {
5165
5266 t . strictEqual ( timer [ timers . kFastTimer ] , true )
5367 t . strictEqual ( timer . _idleStart , - 1 )
54- await new Promise ( ( resolve ) => setTimeout ( resolve , 750 ) )
68+
69+ tick ( 1e3 )
5570 t . notStrictEqual ( timer . _idleStart , - 1 )
5671
5772 timers . clearTimeout ( timer )
@@ -66,7 +81,7 @@ describe('timers', () => {
6681
6782 t . strictEqual ( timer [ timers . kFastTimer ] , true )
6883 t . strictEqual ( timer . _idleStart , - 1 )
69- await new Promise ( ( resolve ) => setTimeout ( resolve , 750 ) )
84+ tick ( 750 )
7085 t . notStrictEqual ( timer . _idleStart , - 1 )
7186 timers . clearTimeout ( timer )
7287 t . strictEqual ( timer . _idleStart , - 1 )
@@ -83,100 +98,113 @@ describe('timers', () => {
8398 timers . clearTimeout ( timer )
8499
85100 t . strictEqual ( timer . _idleStart , - 1 )
86- await new Promise ( ( resolve ) => setTimeout ( resolve , 750 ) )
101+ tick ( 750 )
87102 t . strictEqual ( timer . _idleStart , - 1 )
88103 } )
89104
90105 test ( 'a cleared FastTimer can be refreshed' , async ( t ) => {
91106 t = tspl ( t , { plan : 2 } )
92107
93- const timer = timers . setTimeout ( ( ) => {
108+ const timer = timers . setFastTimeout ( ( ) => {
94109 t . ok ( 'pass' )
95110 } , 1001 )
96111
97112 t . strictEqual ( timer [ timers . kFastTimer ] , true )
98113 timers . clearTimeout ( timer )
99114 timer . refresh ( )
100- await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) )
115+ tick ( 2000 )
101116 timers . clearTimeout ( timer )
102117 } )
103118
104119 const getDelta = ( start , target ) => {
105- const end = process . hrtime . bigint ( )
106- const actual = ( end - start ) / 1_000_000n
107- return actual - BigInt ( target )
120+ const end = performance . now ( )
121+ const actual = end - start
122+ return actual - target
108123 }
109124
110- // timers.setTimeout implements a low resolution timer with a 500 ms granularity
111- // It is expected that in the worst case, a timer will fire about 500 ms after the
112- // intended amount of time, an extra 200 ms is added to account event loop overhead
113- // Timers should never fire excessively early, 1ms early is tolerated
114- const ACCEPTABLE_DELTA = 700n
115-
116- test ( 'meet acceptable resolution time' , async ( t ) => {
117- const testTimeouts = [ 0 , 1 , 499 , 500 , 501 , 990 , 999 , 1000 , 1001 , 1100 , 1400 , 1499 , 1500 , 4000 , 5000 ]
118-
119- t = tspl ( t , { plan : 1 + testTimeouts . length * 2 } )
120-
121- const start = process . hrtime . bigint ( )
122-
123- for ( const target of testTimeouts ) {
124- timers . setTimeout ( ( ) => {
125- const delta = getDelta ( start , target )
126-
127- t . ok ( delta >= - 1n , `${ target } ms fired early` )
128- t . ok ( delta < ACCEPTABLE_DELTA , `${ target } ms fired late, got difference of ${ delta } ms` )
129- } , target )
130- }
131-
132- setTimeout ( ( ) => t . ok ( true ) , 6000 )
133- await t . completed
134- } )
135-
136125 test ( 'refresh correctly with timeout < TICK_MS' , async ( t ) => {
137126 t = tspl ( t , { plan : 3 } )
138127
139- const start = process . hrtime . bigint ( )
128+ const start = performance . now ( )
140129
141130 const timeout = timers . setTimeout ( ( ) => {
142- // 400 ms timer was refreshed after 600ms ; total target is 1000
143- const delta = getDelta ( start , 1000 )
131+ // 80 ms timer was refreshed after 120 ms ; total target is 200 ms
132+ const delta = getDelta ( start , 200 )
144133
145- t . ok ( delta >= - 1n , 'refreshed timer fired early' )
134+ t . ok ( delta >= - 1 , 'refreshed timer fired early' )
146135 t . ok ( delta < ACCEPTABLE_DELTA , 'refreshed timer fired late' )
147- } , 400 )
136+ } , 80 )
148137
149- setTimeout ( ( ) => timeout . refresh ( ) , 200 )
150- setTimeout ( ( ) => timeout . refresh ( ) , 400 )
151- setTimeout ( ( ) => timeout . refresh ( ) , 600 )
138+ setTimeout ( ( ) => timeout . refresh ( ) , 40 )
139+ setTimeout ( ( ) => timeout . refresh ( ) , 80 )
140+ setTimeout ( ( ) => timeout . refresh ( ) , 120 )
152141
153- setTimeout ( ( ) => t . ok ( true ) , 1500 )
142+ setTimeout ( ( ) => t . ok ( true ) , 260 )
143+
144+ tick ( 500 )
154145 await t . completed
155146 } )
156147
157148 test ( 'refresh correctly with timeout > TICK_MS' , async ( t ) => {
158149 t = tspl ( t , { plan : 3 } )
159150
160- const start = process . hrtime . bigint ( )
151+ const start = performance . now ( )
161152
162153 const timeout = timers . setTimeout ( ( ) => {
163154 // 501ms timer was refreshed after 1250ms; total target is 1751
164155 const delta = getDelta ( start , 1751 )
165156
166- t . ok ( delta >= - 1n , 'refreshed timer fired early' )
157+ t . ok ( delta >= - 1 , 'refreshed timer fired early' )
167158 t . ok ( delta < ACCEPTABLE_DELTA , 'refreshed timer fired late' )
168159 } , 501 )
169160
170161 setTimeout ( ( ) => timeout . refresh ( ) , 250 )
171162 setTimeout ( ( ) => timeout . refresh ( ) , 750 )
172163 setTimeout ( ( ) => timeout . refresh ( ) , 1250 )
173164
174- setTimeout ( ( ) => t . ok ( true ) , 3000 )
165+ setTimeout ( ( ) => t . ok ( true ) , 1800 )
166+
167+ tick ( 2000 )
168+ await t . completed
169+ } )
170+
171+ test ( 'refresh correctly FastTimer with timeout > TICK_MS' , async ( t ) => {
172+ t = tspl ( t , { plan : 3 } )
173+
174+ // The long running FastTimer will ensure that the internal clock is
175+ // incremented by the TICK_MS value in the onTick function
176+ const longRunningFastTimer = timers . setTimeout ( ( ) => { } , 1e10 )
177+
178+ const start = timers . now ( )
179+
180+ const timeout = timers . setFastTimeout ( ( ) => {
181+ const delta = ( timers . now ( ) - start ) - 2493
182+
183+ t . ok ( delta >= - 1 , `refreshed timer fired early (${ delta } ms)` )
184+ t . ok ( delta < ACCEPTABLE_DELTA , `refreshed timer fired late (${ delta } ms)` )
185+ } , 1001 )
186+
187+ tick ( 250 )
188+ timeout . refresh ( )
189+
190+ tick ( 250 )
191+ timeout . refresh ( )
192+
193+ tick ( 250 )
194+ timeout . refresh ( )
195+
196+ tick ( 250 )
197+ timeout . refresh ( )
198+
199+ timers . clearTimeout ( longRunningFastTimer )
200+ setTimeout ( ( ) => t . ok ( true ) , 500 )
201+
202+ tick ( 5000 )
175203 await t . completed
176204 } )
177205
178206 test ( 'a FastTimer will only increment by the defined TICK_MS value' , async ( t ) => {
179- t = tspl ( t , { plan : 2 } )
207+ t = tspl ( t , { plan : 6 } )
180208
181209 const startInternalClock = timers . now ( )
182210
@@ -187,12 +215,44 @@ describe('timers', () => {
187215 eventLoopBlocker ( 1000 )
188216
189217 // wait to ensure the timer has fired in the next loop
190- await new Promise ( ( resolve ) => setTimeout ( resolve , 1 ) )
218+ await new Promise ( ( resolve ) => resolve ( ) )
191219
220+ tick ( 250 )
221+ t . strictEqual ( timers . now ( ) - startInternalClock , 0 )
222+ tick ( 250 )
223+ t . strictEqual ( timers . now ( ) - startInternalClock , 499 )
224+ tick ( 250 )
192225 t . strictEqual ( timers . now ( ) - startInternalClock , 499 )
193- await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) )
194- t . ok ( timers . now ( ) - startInternalClock <= 1497 )
226+ tick ( 250 )
227+ t . strictEqual ( timers . now ( ) - startInternalClock , 998 )
228+ tick ( 250 )
229+ t . strictEqual ( timers . now ( ) - startInternalClock , 998 )
230+ tick ( 250 )
231+ t . strictEqual ( timers . now ( ) - startInternalClock , 1497 )
195232
196233 timers . clearTimeout ( longRunningFastTimer )
197234 } )
235+
236+ test ( 'meet acceptable resolution time' , async ( t ) => {
237+ const testTimeouts = [ 0 , 1 , 499 , 500 , 501 , 990 , 999 , 1000 , 1001 , 1100 , 1400 , 1499 , 1500 , 4000 , 5000 ]
238+
239+ t = tspl ( t , { plan : testTimeouts . length * 2 } )
240+
241+ const start = performance . now ( )
242+
243+ for ( const target of testTimeouts ) {
244+ timers . setTimeout ( ( ) => {
245+ const delta = getDelta ( start , target )
246+
247+ t . ok ( delta >= - 1 , `${ target } ms fired early` )
248+ t . ok ( delta < ACCEPTABLE_DELTA , `${ target } ms fired late, got difference of ${ delta } ms` )
249+ } , target )
250+ }
251+
252+ for ( let i = 0 ; i < 6000 ; ++ i ) {
253+ clock . tick ( 1 )
254+ }
255+
256+ await t . completed
257+ } )
198258} )
0 commit comments