summaryrefslogtreecommitdiffstats
path: root/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/crashes/tests/xpcshell/test_crash_service.js')
-rw-r--r--toolkit/components/crashes/tests/xpcshell/test_crash_service.js215
1 files changed, 215 insertions, 0 deletions
diff --git a/toolkit/components/crashes/tests/xpcshell/test_crash_service.js b/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
new file mode 100644
index 0000000000..63ff3343d6
--- /dev/null
+++ b/toolkit/components/crashes/tests/xpcshell/test_crash_service.js
@@ -0,0 +1,215 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { getCrashManagerNoCreate } = ChromeUtils.importESModule(
+ "resource://gre/modules/CrashManager.sys.mjs"
+);
+const { makeFakeAppDir } = ChromeUtils.importESModule(
+ "resource://testing-common/AppData.sys.mjs"
+);
+
+add_task(async function test_instantiation() {
+ Assert.ok(
+ !getCrashManagerNoCreate(),
+ "CrashManager global instance not initially defined."
+ );
+
+ do_get_profile();
+ await makeFakeAppDir();
+
+ // Fake profile creation.
+ Cc["@mozilla.org/crashservice;1"]
+ .getService(Ci.nsIObserver)
+ .observe(null, "profile-after-change", null);
+
+ Assert.ok(getCrashManagerNoCreate(), "Profile creation makes it available.");
+ Assert.ok(Services.crashmanager, "CrashManager available via Services.");
+ Assert.strictEqual(
+ getCrashManagerNoCreate(),
+ Services.crashmanager,
+ "The objects are the same."
+ );
+});
+
+var gMinidumpDir = do_get_tempdir();
+
+// Ensure that the nsICrashReporter methods can find the dump
+Services.appinfo.minidumpPath = gMinidumpDir;
+
+var gDumpFile;
+var gExtraFile;
+
+// Sets up a fake crash dump and sets up the crashreporter so that it will be
+// able to find it.
+async function setup(crashId) {
+ const cwd = Services.dirsvc.get("CurWorkD", Ci.nsIFile).path;
+ const minidump = PathUtils.join(cwd, "crash.dmp");
+ const extra = PathUtils.join(cwd, "crash.extra");
+
+ // Make a copy of the files because the .extra file will be modified
+ gDumpFile = PathUtils.join(gMinidumpDir.path, `${crashId}.dmp`);
+ await IOUtils.copy(minidump, gDumpFile);
+ gExtraFile = PathUtils.join(gMinidumpDir.path, `${crashId}.extra`);
+ await IOUtils.copy(extra, gExtraFile);
+}
+
+// Cleans up the fake crash dump and resets the minidump path
+async function teardown() {
+ await IOUtils.remove(gDumpFile);
+ await IOUtils.remove(gExtraFile);
+}
+
+async function addCrash(id, type = Ci.nsICrashService.CRASH_TYPE_CRASH) {
+ let cs = Cc["@mozilla.org/crashservice;1"].getService(Ci.nsICrashService);
+ return cs.addCrash(Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT, type, id);
+}
+
+async function getCrash(crashId) {
+ let crashes = await Services.crashmanager.getCrashes();
+ return crashes.find(c => {
+ return c.id === crashId;
+ });
+}
+
+async function test_addCrashBase(crashId, allThreads) {
+ await setup(crashId);
+
+ let crashType = Ci.nsICrashService.CRASH_TYPE_CRASH;
+ if (allThreads) {
+ crashType = Ci.nsICrashService.CRASH_TYPE_HANG;
+ }
+ await addCrash(crashId, crashType);
+ let crash = await getCrash(crashId);
+ Assert.ok(crash, "Crash " + crashId + " has been stored successfully.");
+ Assert.equal(crash.metadata.ProcessType, "content");
+ Assert.equal(
+ crash.metadata.MinidumpSha256Hash,
+ "c8ad56a2096310f40c8a4b46c890625a740fdd72e409f412933011ff947c5a40"
+ );
+ Assert.ok(crash.metadata.StackTraces, "The StackTraces field is present.\n");
+
+ try {
+ let stackTraces = crash.metadata.StackTraces;
+ Assert.equal(stackTraces.status, "OK");
+ Assert.ok(stackTraces.crash_info, "The crash_info field is populated.");
+ Assert.ok(
+ stackTraces.modules && !!stackTraces.modules.length,
+ "The module list is populated."
+ );
+ Assert.ok(
+ stackTraces.threads && !!stackTraces.threads.length,
+ "The thread list is populated."
+ );
+
+ if (allThreads) {
+ Assert.ok(
+ stackTraces.threads.length > 1,
+ "The stack trace contains more than one thread."
+ );
+ } else {
+ Assert.ok(
+ stackTraces.threads.length == 1,
+ "The stack trace contains exactly one thread."
+ );
+ }
+
+ let frames = stackTraces.threads[0].frames;
+ Assert.ok(frames && !!frames.length, "The stack trace is present.\n");
+ } catch (e) {
+ Assert.ok(false, "StackTraces does not contain valid JSON.");
+ }
+
+ try {
+ let telemetryEnvironment = JSON.parse(crash.metadata.TelemetryEnvironment);
+ Assert.equal(telemetryEnvironment.EscapedField, "EscapedData\n\nfoo");
+ } catch (e) {
+ Assert.ok(
+ false,
+ "TelemetryEnvironment contents were not properly escaped\n"
+ );
+ }
+
+ await teardown();
+}
+
+add_task(async function test_addCrash() {
+ await test_addCrashBase("56cd87bc-bb26-339b-3a8e-f00c0f11380e", false);
+});
+
+add_task(async function test_addCrashAllThreads() {
+ await test_addCrashBase("071843c4-da89-4447-af9f-965163e0b253", true);
+});
+
+add_task(async function test_addCrash_shutdownOnCrash() {
+ const crashId = "de7f63dd-7516-4525-a44b-6d2f2bd3934a";
+ await setup(crashId);
+
+ // Set the MOZ_CRASHREPORTER_SHUTDOWN environment variable
+ Services.env.set("MOZ_CRASHREPORTER_SHUTDOWN", "1");
+
+ await addCrash(crashId);
+
+ let crash = await getCrash(crashId);
+ Assert.ok(crash, "Crash " + crashId + " has been stored successfully.");
+ Assert.ok(
+ crash.metadata.StackTraces === undefined,
+ "The StackTraces field is not present because the minidump " +
+ "analyzer did not start.\n"
+ );
+
+ Services.env.set("MOZ_CRASHREPORTER_SHUTDOWN", ""); // Unset the environment variable
+ await teardown();
+});
+
+add_task(async function test_addCrash_quitting() {
+ const firstCrashId = "0e578a74-a887-48cb-b270-d4775d01e715";
+ const secondCrashId = "208379e5-1979-430d-a066-f6e57a8130ce";
+
+ await setup(firstCrashId);
+
+ let minidumpAnalyzerKilledPromise = new Promise((resolve, reject) => {
+ Services.obs.addObserver((subject, topic, data) => {
+ if (topic === "test-minidump-analyzer-killed") {
+ resolve();
+ }
+
+ reject();
+ }, "test-minidump-analyzer-killed");
+ });
+
+ let addCrashPromise = addCrash(firstCrashId);
+
+ // Spin the event loop so that the minidump analyzer is launched
+ await new Promise(resolve => {
+ executeSoon(resolve);
+ });
+
+ // Pretend we're quitting
+ let cs = Cc["@mozilla.org/crashservice;1"].getService(Ci.nsICrashService);
+ let obs = cs.QueryInterface(Ci.nsIObserver);
+ obs.observe(null, "quit-application", null);
+
+ // Wait for the minidump analyzer to be killed
+ await minidumpAnalyzerKilledPromise;
+
+ // Now wait for the crash to be recorded
+ await addCrashPromise;
+ let crash = await getCrash(firstCrashId);
+ Assert.ok(crash, "Crash " + firstCrashId + " has been stored successfully.");
+
+ // Cleanup the fake crash and generate a new one
+ await teardown();
+ await setup(secondCrashId);
+
+ await addCrash(secondCrashId);
+ crash = await getCrash(secondCrashId);
+ Assert.ok(crash, "Crash " + secondCrashId + " has been stored successfully.");
+ Assert.ok(
+ crash.metadata.StackTraces === undefined,
+ "The StackTraces field is not present because the minidump " +
+ "analyzer did not start.\n"
+ );
+ await teardown();
+});