1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * Overrides timeout and interval callbacks to mock timing behavior.
11 * Default versions of the timing functions.
12 * @type {Object<string, !Function>}
18 * Key to assign on the next creation of a scheduled timer. Each call to
19 * setTimeout or setInterval returns a unique key that can be used for
24 this.nextTimerKey_
= 1;
27 * Details for active timers.
28 * @type {Array<{callback: Function,
37 * List of scheduled tasks.
38 * @type {Array<{when: number, key: number}>}
44 * Virtual elapsed time in milliseconds.
51 * Used to control when scheduled callbacks fire. Calling the 'tick' method
52 * inflates this parameter and triggers callbacks.
59 MockTimer
.prototype = {
61 * Replaces built-in functions for scheduled callbacks.
64 this.replace_('setTimeout', this.setTimeout_
.bind(this));
65 this.replace_('clearTimeout', this.clearTimeout_
.bind(this));
66 this.replace_('setInterval', this.setInterval_
.bind(this));
67 this.replace_('clearInterval', this.clearInterval_
.bind(this));
71 * Restores default behavior for scheduling callbacks.
73 uninstall: function() {
74 if (this.originals_
) {
75 for (var key
in this.originals_
) {
76 window
[key
] = this.originals_
[key
];
82 * Overrides a global function.
83 * @param {string} functionName The name of the function.
84 * @param {!Function} replacementFunction The function override.
87 replace_: function(functionName
, replacementFunction
) {
88 this.originals_
[functionName
] = window
[functionName
];
89 window
[functionName
] = replacementFunction
;
93 * Creates a virtual timer.
94 * @param {!Function} callback The callback function.
95 * @param {number} delayInMs The virtual delay in milliseconds.
96 * @param {boolean} repeats Indicates if the timer repeats.
97 * @return {number} Idetifier for the timer.
100 createTimer_: function(callback
, delayInMs
, repeats
) {
101 var key
= this.nextTimerKey_
++;
108 this.timers_
[key
] = task
;
109 this.scheduleTask_(task
);
114 * Schedules a callback for execution after a virtual time delay. The tasks
115 * are sorted in descending order of time delay such that the next callback
116 * to fire is at the end of the list.
117 * @param {{callback: Function,
120 * repeats: boolean}} details The timer details.
123 scheduleTask_: function(details
) {
124 var key
= details
.key
;
125 var when
= this.now_
+ details
.delay
;
126 var index
= this.schedule_
.length
;
127 while (index
> 0 && this.schedule_
[index
- 1].when
< when
) {
130 this.schedule_
.splice(index
, 0, {when
: when
, key
: key
});
134 * Override of window.setInterval.
135 * @param {!Function} callback The callback function.
136 * @param {number} intervalInMs The repeat interval.
139 setInterval_: function(callback
, intervalInMs
) {
140 return this.createTimer_(callback
, intervalInMs
, true);
144 * Override of window.clearInterval.
145 * @param {number} key The ID of the interval timer returned from
149 clearInterval_: function(key
) {
150 this.timers_
[key
] = undefined;
154 * Override of window.setTimeout.
155 * @param {!Function} callback The callback function.
156 * @param {number} delayInMs The scheduled delay.
159 setTimeout_: function(callback
, delayInMs
) {
160 return this.createTimer_(callback
, delayInMs
, false);
164 * Override of window.clearTimeout.
165 * @param {number} key The ID of the schedule timeout callback returned
169 clearTimeout_: function(key
) {
170 this.timers_
[key
] = undefined;
174 * Simulates passage of time, triggering any scheduled callbacks whose timer
176 * @param {number} elapsedMs The simulated elapsed time in milliseconds.
178 tick: function(elapsedMs
) {
179 this.until_
+= elapsedMs
;
180 this.fireElapsedCallbacks_();
184 * Triggers any callbacks that should have fired based in the simulated
188 fireElapsedCallbacks_: function() {
189 while (this.schedule_
.length
> 0) {
190 var when
= this.schedule_
[this.schedule_
.length
- 1].when
;
191 if (when
> this.until_
)
194 var task
= this.schedule_
.pop();
195 var details
= this.timers_
[task
.key
];
197 continue; // Cancelled task.
200 details
.callback
.apply(window
);
202 this.scheduleTask_(details
);
204 this.clearTimeout_(details
.key
);
206 this.now_
= this.until_
;