/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const timerManager = Cc[
"@mozilla.org/timer-manager;1"].getService(
Ci.nsITimerManager
);
function newTimer(name, delay, type) {
let timer = Cc[
"@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(
{
QueryInterface: ChromeUtils.generateQI([
"nsITimerCallback",
"nsINamed"]),
name,
notify: () => {},
},
delay,
type
);
return timer;
}
const ignoredTimers = [
"BackgroundHangThread_timer",
// BHR is Nightly-only, just ignore it.
"CCGCScheduler::EnsureGCRunner",
// GC runner can be scheduled anytime, randomly.
"IdleMemoryCleanupWantsLaterCheck",
// When Worker is used, nsThread::ProcessNextEvent may call MayScheduleIdleMemoryCleanup.
"dom::IdleGCTimerCallback",
// When Worker is used.
"dom::PeriodicGCTimerCallback",
// When Worker is used.
"IdleRunnableWrapper::SetTimer",
// When Worker is used.
];
if (AppConstants.platform ==
"win") {
// On Windows there's a 3min timer added at startup to then add an
// idle observer that finally triggers removing leftover temp files.
// Ignore that too.
ignoredTimers.push(
"nsAnonTempFileRemover");
}
if (
Services.profiler.IsActive() &&
!Services.env.exists(
"MOZ_PROFILER_SHUTDOWN") &&
Services.env.exists(
"MOZ_UPLOAD_DIR") &&
Services.env.exists(
"MOZ_TEST_TIMEOUT_INTERVAL")
) {
// Ignore the timer used to upload profiles of test timeouts when running
// the tests with --profiler.
ignoredTimers.push(
"upload_test_timeout_profile");
}
function getTimers() {
return timerManager
.getTimers()
.filter(t => !ignoredTimers.includes(t.name.replace(/\[.*/,
"")));
}
function getTimersVerbose() {
let timers = getTimers();
for (let timer of timers) {
// To help with debugging in case of unexpected timers.
info(`${timer.name}: ${timer.delay}ms, ${timer.type}`);
}
return timers;
}
add_task(
function test_nsITimerManager_getTimers() {
Assert.equal(
getTimersVerbose().length,
0,
"there should be no timer at xpcshell startup"
);
let timerData = [
[
"t1",
500, Ci.nsITimer.TYPE_ONE_SHOT],
[
"t2",
1500, Ci.nsITimer.TYPE_REPEATING_SLACK],
[
"t3",
2500, Ci.nsITimer.TYPE_REPEATING_PRECISE],
[
"t4",
3500, Ci.nsITimer.TYPE_REPEATING_PRECISE_CAN_SKIP],
[
"t5",
5500, Ci.nsITimer.TYPE_REPEATING_SLACK_LOW_PRIORITY],
[
"t6",
7500, Ci.nsITimer.TYPE_ONE_SHOT_LOW_PRIORITY],
];
info(
"Add timers one at a time.");
for (let [name, delay, type] of timerData) {
let timer = newTimer(name, delay, type);
let timers = getTimers();
Assert.equal(timers.length,
1,
"there should be only one timer");
Assert.equal(name, timers[
0].name,
"the name is correct");
Assert.equal(delay, timers[
0].delay,
"the delay is correct");
Assert.equal(type, timers[
0].type,
"the type is correct");
timer.cancel();
Assert.equal(getTimers().length,
0,
"no timer left after cancelling");
}
info(
"Add all timers at once.");
let timers = [];
for (let [name, delay, type] of timerData) {
timers.push(newTimer(name, delay, type));
}
while (timers.length) {
Assert.equal(getTimers().length, timers.length,
"correct timer count");
timers.pop().cancel();
}
Assert.equal(getTimers().length,
0,
"no timer left after cancelling");
});
add_task(async
function test_getTimers_with_active_worker_thread() {
let worker =
new ChromeWorker(
"resource://test/data/test_timers.worker.js");
async
function queryWorker(cmd, ...args) {
return new Promise(resolve => {
// One message at a time; we are not going to send multiple messages.
worker.addEventListener(
"message",
e => {
Assert.equal(e.data.replyToCmd, cmd,
"Got reply from worker");
resolve(e.data.returnValue);
},
{ once:
true }
);
worker.postMessage([cmd, ...args]);
});
}
// A very long timeout that will certainly outlast the test execution.
const VERY_LONG_TIMEOUT_MS =
3_
600_
000;
Assert.equal(getTimersVerbose().length,
0,
"no timers before");
const handle = await queryWorker(
"do_call_setTimeout", VERY_LONG_TIMEOUT_MS);
let timers = getTimersVerbose();
Assert.equal(timers.length,
1,
"one timer after?");
Assert.equal(timers[
0].name,
"TimeoutExecutor Runnable",
"Got timer name");
// Although we created a timer with 3600000 delay, it is reported as 3599999.
// As a sanity check, just check that it is close enough. A drift of more
// than 1 hour would be quite unexpected.
Assert.less(
Math.abs(VERY_LONG_TIMEOUT_MS - timers[
0].delay),
60_
000,
`Should match the expected timer delay: ${timers[
0].delay}`
);
Assert.equal(Ci.nsITimer.TYPE_ONE_SHOT, timers[
0].type,
"Got timer type");
await queryWorker(
"do_call_clearTimeout", handle);
Assert.equal(getTimersVerbose().length,
0,
"no timer left after cancelling");
worker.terminate();
});