/* 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(); });