@@ -41,133 +41,152 @@ describe('utils', function () {
41
41
} ) ;
42
42
} ) ;
43
43
44
- context ( 'makeInterruptibleAsyncInterval' , function ( ) {
45
- before ( function ( ) {
46
- this . clock = sinon . useFakeTimers ( ) ;
47
- } ) ;
44
+ describe ( '#makeInterruptibleAsyncInterval' , function ( ) {
45
+ let clock ;
48
46
49
- after ( function ( ) {
50
- this . clock . restore ( ) ;
47
+ beforeEach ( function ( ) {
48
+ clock = sinon . useFakeTimers ( ) ;
51
49
} ) ;
52
50
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 ) ;
51
+ afterEach ( function ( ) {
52
+ clock . restore ( ) ;
73
53
} ) ;
74
54
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 ) ;
55
+ context ( 'when the immediate option is provided' , function ( ) {
56
+ const fn = callback => {
57
+ callback ( ) ;
58
+ } ;
59
+ const fnSpy = sinon . spy ( fn ) ;
60
+
61
+ it ( 'executes the function immediately' , function ( done ) {
62
+ const executor = makeInterruptibleAsyncInterval ( fnSpy , { immediate : true , interval : 20 } ) ;
63
+ setTimeout ( ( ) => {
64
+ // The provided function should be called exactly once, since we wait 10ms
65
+ // to perform the assertion and the interval is 20ms, so the executor is
66
+ // stopped before the scheduled next call.
67
+ expect ( fnSpy . calledOnce ) . to . be . true ;
68
+ executor . stop ( ) ;
69
+ done ( ) ;
70
+ } , 10 ) ;
71
+ clock . tick ( 10 ) ;
72
+ } ) ;
97
73
} ) ;
98
74
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 ) ;
75
+ context ( 'when the immediate option is not provided' , function ( ) {
76
+ const fn = callback => {
77
+ callback ( ) ;
78
+ } ;
79
+ const fnSpy = sinon . spy ( fn ) ;
80
+
81
+ it ( 'executes the function on the provided interval' , function ( done ) {
82
+ const executor = makeInterruptibleAsyncInterval ( fnSpy , { interval : 10 } ) ;
83
+ setTimeout ( ( ) => {
84
+ // The provided function should be called exactly twice, since we wait 21ms
85
+ // to perform the assertion and the interval is 10ms, so the executor is
86
+ // stopped before the third call.
87
+ expect ( fnSpy . calledTwice ) . to . be . true ;
88
+ executor . stop ( ) ;
89
+ done ( ) ;
90
+ } , 21 ) ;
91
+ clock . tick ( 21 ) ;
92
+ } ) ;
122
93
} ) ;
123
94
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 ( ) ;
95
+ describe ( '#wake' , function ( ) {
96
+ context ( 'when the time until next call is negative' , function ( ) {
97
+ const fn = callback => {
132
98
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 ;
99
+ } ;
100
+ const fnSpy = sinon . spy ( fn ) ;
101
+
102
+ it ( 'calls the function immediately' , function ( done ) {
103
+ const executor = makeInterruptibleAsyncInterval ( fnSpy , {
104
+ interval : 10 ,
105
+ clock : ( ) => {
106
+ // We have our fake clock return a value that will force
107
+ // the time until the next call to be a negative value,
108
+ // which will in turn force an immediate execution upon
109
+ // wake.
110
+ return 11 ;
148
111
}
112
+ } ) ;
113
+
114
+ // This will reset the last call time to 0 and ensure the function has
115
+ // not been called yet.
116
+ executor . stop ( ) ;
117
+ // Now we call our method under test with the expectation it will force
118
+ // an immediate execution.
119
+ executor . wake ( ) ;
120
+
121
+ setTimeout ( ( ) => {
122
+ // The provided function should be called exactly once in this section.
123
+ // This is because we immediately stopped the executor, then force woke
124
+ // it to get an immediate call with time until the next call being a
125
+ // negative value.
126
+ expect ( fnSpy . calledOnce ) . to . be . true ;
127
+ executor . stop ( ) ;
128
+ done ( ) ;
129
+ } , 10 ) ;
130
+ clock . tick ( 11 ) ;
131
+ } ) ;
132
+ } ) ;
149
133
150
- return now ( ) ;
151
- }
152
- }
153
- ) ;
134
+ context ( 'when time since last wake is less than the minimum interval' , function ( ) {
135
+ const fn = callback => {
136
+ callback ( ) ;
137
+ } ;
138
+ const fnSpy = sinon . spy ( fn ) ;
139
+
140
+ it ( 'does not call the function' , function ( done ) {
141
+ const executor = makeInterruptibleAsyncInterval ( fnSpy , { interval : 10 } ) ;
142
+
143
+ // This will reset the last wake time to 0 and ensure the function has
144
+ // not been called yet.
145
+ executor . stop ( ) ;
146
+ // Now we call our method under test with the expectation it will not be
147
+ // called immediately since our current time is still under the interval
148
+ // time.
149
+ executor . wake ( ) ;
150
+
151
+ setTimeout ( ( ) => {
152
+ // The provided function should never be called in this case.
153
+ // This is because we immediately stopped the executor, then force woke
154
+ // it but the current time is still under the interval time.
155
+ expect ( fnSpy . callCount ) . to . equal ( 0 ) ;
156
+ executor . stop ( ) ;
157
+ done ( ) ;
158
+ } , 9 ) ;
159
+ clock . tick ( 9 ) ;
160
+ } ) ;
161
+ } ) ;
154
162
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 ) ;
163
+ context ( 'when time since last call is greater than the minimum interval' , function ( ) {
164
+ const fn = callback => {
165
+ callback ( ) ;
166
+ } ;
167
+ const fnSpy = sinon . spy ( fn ) ;
168
+
169
+ it ( 'reschedules the function call for the minimum interval' , function ( done ) {
170
+ const executor = makeInterruptibleAsyncInterval ( fnSpy , {
171
+ interval : 50 ,
172
+ minInterval : 10
173
+ } ) ;
174
+
175
+ // Calling wake here will force the reschedule to happen at the minimum interval
176
+ // provided, which is 10ms.
177
+ executor . wake ( ) ;
178
+
179
+ setTimeout ( ( ) => {
180
+ // We expect function calls to happen after 10ms, which is the minimum interval,
181
+ // and then in 50ms intervals after that. The second call would happen at 60ms
182
+ // time from the original call so we've stopped the executor before a third.
183
+ expect ( fnSpy . calledTwice ) . to . be . true ;
184
+ executor . stop ( ) ;
185
+ done ( ) ;
186
+ } , 61 ) ;
187
+ clock . tick ( 61 ) ;
188
+ } ) ;
189
+ } ) ;
171
190
} ) ;
172
191
} ) ;
173
192
0 commit comments