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_;