diff options
Diffstat (limited to '')
4 files changed, 158 insertions, 265 deletions
diff --git a/toolkit/components/terminator/tests/xpcshell/test_terminator_advance_phases.js b/toolkit/components/terminator/tests/xpcshell/test_terminator_advance_phases.js new file mode 100644 index 0000000000..42f0e3686f --- /dev/null +++ b/toolkit/components/terminator/tests/xpcshell/test_terminator_advance_phases.js @@ -0,0 +1,157 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +"use strict"; + +// Test that the Shutdown Terminator advances through the shutdown phases +// correctly. + +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +var terminator; + +var HEARTBEAT_MS = 100; + +let KEYS = [ + "quit-application", + "profile-change-net-teardown", + "profile-change-teardown", + "profile-before-change", + "profile-before-change-qm", + "xpcom-will-shutdown", + "xpcom-shutdown", + "xpcom-shutdown-threads", + "XPCOMShutdownFinal", + "CCPostLastCycleCollection", +]; + +let DATA = []; +let MeasuredDurations = []; + +add_task(async function init() { + do_get_profile(); + + // Initialize the terminator + // (normally, this is done through the manifest file, but xpcshell + // doesn't take them into account). + info("Initializing the Terminator"); + terminator = Cc["@mozilla.org/toolkit/shutdown-terminator;1"].createInstance( + Ci.nsIObserver + ); + + Assert.ok( + terminator instanceof Ci.nsITerminatorTest, + "Terminator should implement nsITerminatorTest" + ); +}); + +var currentPhase = 0; + +var advancePhase = async function () { + let key = "terminator-test-" + KEYS[currentPhase]; + let msDuration = 200 + HEARTBEAT_MS * currentPhase; + + info("Advancing shutdown phase to " + KEYS[currentPhase]); + terminator.observe(null, key, null); + await new Promise(resolve => setTimeout(resolve, msDuration)); + + let data = terminator.getTicksForShutdownPhases(); + + Assert.ok( + KEYS[currentPhase] in data, + "The KEYS object contains the expected key" + ); + Assert.equal( + Object.keys(data).length, + currentPhase + 1, + "KEYS object does not contain more durations than expected" + ); + + DATA[currentPhase] = data; + currentPhase++; + if (currentPhase < KEYS.length) { + return true; + } + return false; +}; + +// This is a timing affected test, as we want to check if the time measurements +// from the terminator are reasonable. Bug 1768795 assumes that they tend to +// be lower than wall-clock, in particular on MacOS, confirmed by the logs on +// intermittent bug 1760094. This is not a big deal for the terminator's +// general functionality (timeouts might just come a little later than +// expected nominally), but it makes testing harder and the transferred +// telemetry data slightly less reliable (shutdowns might appear shorter than +// they really were). So this test is just happy if there is any data that +// is not too long wrt what we expect. If we ever want to fix bug 1768795, +// we can check for a more reasonable lower boundary, too. +add_task(async function test_record() { + info("Collecting duration data for all known phases"); + + let morePhases = true; + while (morePhases) { + let beforeWait = Date.now(); + + morePhases = await advancePhase(); + + // We measure the effective time that passed as wall-clock and include all + // file IO overhead as the terminator will do so in its measurement, too. + MeasuredDurations[currentPhase - 1] = Math.floor( + (Date.now() - beforeWait) / HEARTBEAT_MS + ); + } + + Assert.equal(DATA.length, KEYS.length, "We have data for each phase"); + + for (let i = 0; i < KEYS.length; i++) { + let lastDuration = DATA[KEYS.length - 1][KEYS[i]]; + Assert.equal( + typeof lastDuration, + "number", + "Duration of phase " + i + ":" + KEYS[i] + " is a number" + ); + + // The durations are only meaningful after we advanced to the next phase. + if (i < KEYS.length - 1) { + // So we read it from the data written for the following phase. + let ticksDuration = DATA[i + 1][KEYS[i]]; + let measuredDuration = MeasuredDurations[i]; + info( + "measuredDuration:" + measuredDuration + " - " + typeof measuredDuration + ); + Assert.lessOrEqual( + ticksDuration, + measuredDuration + 2, + "Duration of phase " + i + ":" + KEYS[i] + " is not too long" + ); + Assert.greaterOrEqual( + ticksDuration, + 0, // TODO: Raise the lower boundary after bug 1768795. + "Duration of phase " + i + ":" + KEYS[i] + " is not too short" + ); + } + // This check is done only for phases <= xpcom-shutdown-threads + // where we have two data points. + if (i < KEYS.length - 2) { + let ticksDuration = DATA[i + 1][KEYS[i]]; + Assert.equal( + ticksDuration, + DATA[KEYS.length - 1][KEYS[i]], + "Duration of phase " + i + ":" + KEYS[i] + " hasn't changed" + ); + } + } + + // Note that after this check the KEYS array remains sorted, so this + // must be the last check to not get confused. + Assert.equal( + Object.keys(DATA[KEYS.length - 1]) + .sort() + .join(", "), + KEYS.sort().join(", "), + "The KEYS object contains all expected keys" + ); +}); diff --git a/toolkit/components/terminator/tests/xpcshell/test_terminator_record.js b/toolkit/components/terminator/tests/xpcshell/test_terminator_record.js deleted file mode 100644 index 62cee636f3..0000000000 --- a/toolkit/components/terminator/tests/xpcshell/test_terminator_record.js +++ /dev/null @@ -1,171 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ -/* eslint-disable mozilla/no-arbitrary-setTimeout */ - -"use strict"; - -// Test that the Shutdown Terminator records durations correctly - -const { setTimeout } = ChromeUtils.importESModule( - "resource://gre/modules/Timer.sys.mjs" -); - -var PATH; -var PATH_TMP; -var terminator; - -var HEARTBEAT_MS = 100; - -let KEYS = [ - "quit-application", - "profile-change-net-teardown", - "profile-change-teardown", - "profile-before-change", - "profile-before-change-qm", - "xpcom-will-shutdown", - "xpcom-shutdown", - "xpcom-shutdown-threads", - "XPCOMShutdownFinal", - "CCPostLastCycleCollection", -]; - -let DATA = []; -let MeasuredDurations = []; - -add_task(async function init() { - do_get_profile(); - PATH = PathUtils.join(PathUtils.localProfileDir, "ShutdownDuration.json"); - PATH_TMP = PATH + ".tmp"; - - // Initialize the terminator - // (normally, this is done through the manifest file, but xpcshell - // doesn't take them into account). - info("Initializing the Terminator"); - terminator = Cc["@mozilla.org/toolkit/shutdown-terminator;1"].createInstance( - Ci.nsIObserver - ); -}); - -var promiseShutdownDurationData = async function () { - // Wait until PATH exists. - // Timeout if it is never created. - while (true) { - if (await IOUtils.exists(PATH)) { - break; - } - - // Wait just a very short period to not increase measured values. - // Usually the file should appear almost immediately. - await new Promise(resolve => setTimeout(resolve, 50)); - } - - return IOUtils.readJSON(PATH); -}; - -var currentPhase = 0; - -var advancePhase = async function () { - let key = "terminator-test-" + KEYS[currentPhase]; - let msDuration = 200 + HEARTBEAT_MS * currentPhase; - - info("Advancing shutdown phase to " + KEYS[currentPhase]); - terminator.observe(null, key, null); - await new Promise(resolve => setTimeout(resolve, msDuration)); - - let data = await promiseShutdownDurationData(); - - Assert.ok(KEYS[currentPhase] in data, "The file contains the expected key"); - Assert.equal( - Object.keys(data).length, - currentPhase + 1, - "File does not contain more durations than expected" - ); - - DATA[currentPhase] = data; - currentPhase++; - if (currentPhase < KEYS.length) { - return true; - } - return false; -}; - -// This is a timing affected test, as we want to check if the time measurements -// from the terminator are reasonable. Bug 1768795 assumes that they tend to -// be lower than wall-clock, in particular on MacOS, confirmed by the logs on -// intermittent bug 1760094. This is not a big deal for the terminator's -// general functionality (timeouts might just come a little later than -// expected nominally), but it makes testing harder and the transferred -// telemetry data slightly less reliable (shutdowns might appear shorter than -// they really were). So this test is just happy if there is any data that -// is not too long wrt what we expect. If we ever want to fix bug 1768795, -// we can check for a more reasonable lower boundary, too. -add_task(async function test_record() { - info("Collecting duration data for all known phases"); - - let morePhases = true; - while (morePhases) { - let beforeWait = Date.now(); - - morePhases = await advancePhase(); - - await IOUtils.remove(PATH); - await IOUtils.remove(PATH_TMP); - - // We measure the effective time that passed as wall-clock and include all - // file IO overhead as the terminator will do so in its measurement, too. - MeasuredDurations[currentPhase - 1] = Math.floor( - (Date.now() - beforeWait) / HEARTBEAT_MS - ); - } - - Assert.equal(DATA.length, KEYS.length, "We have data for each phase"); - - for (let i = 0; i < KEYS.length; i++) { - let lastDuration = DATA[KEYS.length - 1][KEYS[i]]; - Assert.equal( - typeof lastDuration, - "number", - "Duration of phase " + i + ":" + KEYS[i] + " is a number" - ); - - // The durations are only meaningful after we advanced to the next phase. - if (i < KEYS.length - 1) { - // So we read it from the data written for the following phase. - let ticksDuration = DATA[i + 1][KEYS[i]]; - let measuredDuration = MeasuredDurations[i]; - info( - "measuredDuration:" + measuredDuration + " - " + typeof measuredDuration - ); - Assert.lessOrEqual( - ticksDuration, - measuredDuration + 2, - "Duration of phase " + i + ":" + KEYS[i] + " is not too long" - ); - Assert.greaterOrEqual( - ticksDuration, - 0, // TODO: Raise the lower boundary after bug 1768795. - "Duration of phase " + i + ":" + KEYS[i] + " is not too short" - ); - } - // This check is done only for phases <= xpcom-shutdown-threads - // where we have two data points. - if (i < KEYS.length - 2) { - let ticksDuration = DATA[i + 1][KEYS[i]]; - Assert.equal( - ticksDuration, - DATA[KEYS.length - 1][KEYS[i]], - "Duration of phase " + i + ":" + KEYS[i] + " hasn't changed" - ); - } - } - - // Note that after this check the KEYS array remains sorted, so this - // must be the last check to not get confused. - Assert.equal( - Object.keys(DATA[KEYS.length - 1]) - .sort() - .join(", "), - KEYS.sort().join(", "), - "The last file contains all expected keys" - ); -}); diff --git a/toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js b/toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js deleted file mode 100644 index 79aecd818b..0000000000 --- a/toolkit/components/terminator/tests/xpcshell/test_terminator_reload.js +++ /dev/null @@ -1,88 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -// Test that the Shutdown Terminator reloads durations correctly - -const HISTOGRAMS = { - "quit-application": "SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION", - "profile-change-net-teardown": - "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_NET_TEARDOWN", - "profile-change-teardown": - "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN", - "profile-before-change": - "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE", - "profile-before-change-qm": - "SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE_QM", - "xpcom-will-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN", - "xpcom-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_SHUTDOWN", -}; - -let PATH; - -add_setup(async function init() { - do_get_profile(); - PATH = PathUtils.join(PathUtils.localProfileDir, "ShutdownDuration.json"); -}); - -add_task(async function test_reload() { - info("Forging data"); - let data = {}; - let telemetrySnapshots = Services.telemetry.getSnapshotForHistograms( - "main", - false /* clear */ - ).parent; - let i = 0; - for (let k of Object.keys(HISTOGRAMS)) { - let id = HISTOGRAMS[k]; - data[k] = i++; - Assert.equal( - telemetrySnapshots[id] || undefined, - undefined, - "Histogram " + id + " is empty" - ); - } - - // Extra fields that nsTerminator reports that we do not have histograms for. - data["xpcom-shutdown-threads"] = 123; - data.XPCOMShutdownFinal = 456; - data.CCPostLastCycleCollection = 789; - - await IOUtils.writeJSON(PATH, data); - - const TOPIC = "shutdown-terminator-telemetry-updated"; - - let wait = new Promise(resolve => - Services.obs.addObserver(function observer() { - info("Telemetry has been updated"); - Services.obs.removeObserver(observer, TOPIC); - resolve(); - }, TOPIC) - ); - - info("Starting nsTerminatorTelemetry"); - let tt = Cc[ - "@mozilla.org/toolkit/shutdown-terminator-telemetry;1" - ].createInstance(Ci.nsIObserver); - tt.observe(null, "profile-after-change", ""); - - info("Waiting until telemetry is updated"); - // Now wait until Telemetry is updated - await wait; - - telemetrySnapshots = Services.telemetry.getSnapshotForHistograms( - "main", - false /* clear */ - ).parent; - for (let k of Object.keys(HISTOGRAMS)) { - let id = HISTOGRAMS[k]; - info("Testing histogram " + id); - let snapshot = telemetrySnapshots[id]; - let count = 0; - for (let x of Object.values(snapshot.values)) { - count += x; - } - Assert.equal(count, 1, "We have added one item"); - } -}); diff --git a/toolkit/components/terminator/tests/xpcshell/xpcshell.toml b/toolkit/components/terminator/tests/xpcshell/xpcshell.toml index cb328ca2c8..67cc36f265 100644 --- a/toolkit/components/terminator/tests/xpcshell/xpcshell.toml +++ b/toolkit/components/terminator/tests/xpcshell/xpcshell.toml @@ -1,15 +1,10 @@ [DEFAULT] head = "" -["test_terminator_record.js"] +["test_terminator_advance_phases.js"] skip-if = [ "debug", "asan", # Disabled by bug 1242084, bug 1255484 will enable it again "ccov", # Bug 1607583 tracks the ccov failure "tsan", # Bug 1683730 made this timeout for tsan. ] -run-sequentially = "very high failure rate in parallel" - -["test_terminator_reload.js"] -skip-if = ["os == 'android'"] -run-sequentially = "very high failure rate in parallel" |