diff options
Diffstat (limited to 'browser/modules/test/browser_UnsubmittedCrashHandler.js')
-rw-r--r-- | browser/modules/test/browser_UnsubmittedCrashHandler.js | 680 |
1 files changed, 0 insertions, 680 deletions
diff --git a/browser/modules/test/browser_UnsubmittedCrashHandler.js b/browser/modules/test/browser_UnsubmittedCrashHandler.js deleted file mode 100644 index 2d78c746b..000000000 --- a/browser/modules/test/browser_UnsubmittedCrashHandler.js +++ /dev/null @@ -1,680 +0,0 @@ -"use strict"; - -/** - * This suite tests the "unsubmitted crash report" notification - * that is seen when we detect pending crash reports on startup. - */ - -const { UnsubmittedCrashHandler } = - Cu.import("resource:///modules/ContentCrashHandlers.jsm", this); -const { FileUtils } = - Cu.import("resource://gre/modules/FileUtils.jsm", this); -const { makeFakeAppDir } = - Cu.import("resource://testing-common/AppData.jsm", this); -const { OS } = - Cu.import("resource://gre/modules/osfile.jsm", this); - -const DAY = 24 * 60 * 60 * 1000; // milliseconds -const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; - -/** - * Returns the directly where the browsing is storing the - * pending crash reports. - * - * @returns nsIFile - */ -function getPendingCrashReportDir() { - // The fake UAppData directory that makeFakeAppDir provides - // is just UAppData under the profile directory. - return FileUtils.getDir("ProfD", [ - "UAppData", - "Crash Reports", - "pending", - ], false); -} - -/** - * Synchronously deletes all entries inside the pending - * crash report directory. - */ -function clearPendingCrashReports() { - let dir = getPendingCrashReportDir(); - let entries = dir.directoryEntries; - - while (entries.hasMoreElements()) { - let entry = entries.getNext().QueryInterface(Ci.nsIFile); - if (entry.isFile()) { - entry.remove(false); - } - } -} - -/** - * Randomly generates howMany crash report .dmp and .extra files - * to put into the pending crash report directory. We're not - * actually creating real crash reports here, just stubbing - * out enough of the files to satisfy our notification and - * submission code. - * - * @param howMany (int) - * How many pending crash reports to put in the pending - * crash report directory. - * @param accessDate (Date, optional) - * What date to set as the last accessed time on the created - * crash reports. This defaults to the current date and time. - * @returns Promise - */ -function* createPendingCrashReports(howMany, accessDate) { - let dir = getPendingCrashReportDir(); - if (!accessDate) { - accessDate = new Date(); - } - - /** - * Helper function for creating a file in the pending crash report - * directory. - * - * @param fileName (string) - * The filename for the crash report, not including the - * extension. This is usually a UUID. - * @param extension (string) - * The file extension for the created file. - * @param accessDate (Date) - * The date to set lastAccessed to. - * @param contents (string, optional) - * Set this to whatever the file needs to contain, if anything. - * @returns Promise - */ - let createFile = (fileName, extension, accessDate, contents) => { - let file = dir.clone(); - file.append(fileName + "." + extension); - file.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE); - let promises = [OS.File.setDates(file.path, accessDate)]; - - if (contents) { - let encoder = new TextEncoder(); - let array = encoder.encode(contents); - promises.push(OS.File.writeAtomic(file.path, array, { - tmpPath: file.path + ".tmp", - })); - } - return Promise.all(promises); - } - - let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); - // CrashSubmit expects there to be a ServerURL key-value - // pair in the .extra file, so we'll satisfy it. - let extraFileContents = "ServerURL=" + SERVER_URL; - - return Task.spawn(function*() { - let uuids = []; - for (let i = 0; i < howMany; ++i) { - let uuid = uuidGenerator.generateUUID().toString(); - // Strip the {}... - uuid = uuid.substring(1, uuid.length - 1); - yield createFile(uuid, "dmp", accessDate); - yield createFile(uuid, "extra", accessDate, extraFileContents); - uuids.push(uuid); - } - return uuids; - }); -} - -/** - * Returns a Promise that resolves once CrashSubmit starts sending - * success notifications for crash submission matching the reportIDs - * being passed in. - * - * @param reportIDs (Array<string>) - * The IDs for the reports that we expect CrashSubmit to have sent. - * @returns Promise - */ -function waitForSubmittedReports(reportIDs) { - let promises = []; - for (let reportID of reportIDs) { - let promise = TestUtils.topicObserved("crash-report-status", (subject, data) => { - if (data == "success") { - let propBag = subject.QueryInterface(Ci.nsIPropertyBag2); - let dumpID = propBag.getPropertyAsAString("minidumpID"); - if (dumpID == reportID) { - return true; - } - } - return false; - }); - promises.push(promise); - } - return Promise.all(promises); -} - -/** - * Returns a Promise that resolves once a .dmp.ignore file is created for - * the crashes in the pending directory matching the reportIDs being - * passed in. - * - * @param reportIDs (Array<string>) - * The IDs for the reports that we expect CrashSubmit to have been - * marked for ignoring. - * @returns Promise - */ -function waitForIgnoredReports(reportIDs) { - let dir = getPendingCrashReportDir(); - let promises = []; - for (let reportID of reportIDs) { - let file = dir.clone(); - file.append(reportID + ".dmp.ignore"); - promises.push(OS.File.exists(file.path)); - } - return Promise.all(promises); -} - -let gNotificationBox; - -add_task(function* setup() { - // Pending crash reports are stored in the UAppData folder, - // which exists outside of the profile folder. In order to - // not overwrite / clear pending crash reports for the poor - // soul who runs this test, we use AppData.jsm to point to - // a special made-up directory inside the profile - // directory. - yield makeFakeAppDir(); - // We'll assume that the notifications will be shown in the current - // browser window's global notification box. - gNotificationBox = document.getElementById("global-notificationbox"); - - // If we happen to already be seeing the unsent crash report - // notification, it's because the developer running this test - // happened to have some unsent reports in their UAppDir. - // We'll remove the notification without touching those reports. - let notification = - gNotificationBox.getNotificationWithValue("pending-crash-reports"); - if (notification) { - notification.close(); - } - - let env = Cc["@mozilla.org/process/environment;1"] - .getService(Components.interfaces.nsIEnvironment); - let oldServerURL = env.get("MOZ_CRASHREPORTER_URL"); - env.set("MOZ_CRASHREPORTER_URL", SERVER_URL); - - // nsBrowserGlue starts up UnsubmittedCrashHandler automatically - // so at this point, it is initialized. It's possible that it - // was initialized, but is preffed off, so it's inert, so we - // shut it down, make sure it's preffed on, and then restart it. - // Note that making the component initialize even when it's - // disabled is an intentional choice, as this allows for easier - // simulation of startup and shutdown. - UnsubmittedCrashHandler.uninit(); - yield SpecialPowers.pushPrefEnv({ - set: [ - ["browser.crashReports.unsubmittedCheck.enabled", true], - ], - }); - UnsubmittedCrashHandler.init(); - - registerCleanupFunction(function() { - gNotificationBox = null; - clearPendingCrashReports(); - env.set("MOZ_CRASHREPORTER_URL", oldServerURL); - }); -}); - -/** - * Tests that if there are no pending crash reports, then the - * notification will not show up. - */ -add_task(function* test_no_pending_no_notification() { - // Make absolutely sure there are no pending crash reports first... - clearPendingCrashReports(); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.equal(notification, null, - "There should not be a notification if there are no " + - "pending crash reports"); -}); - -/** - * Tests that there is a notification if there is one pending - * crash report. - */ -add_task(function* test_one_pending() { - yield createPendingCrashReports(1); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - gNotificationBox.removeNotification(notification, true); - clearPendingCrashReports(); -}); - -/** - * Tests that there is a notification if there is more than one - * pending crash report. - */ -add_task(function* test_several_pending() { - yield createPendingCrashReports(3); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - gNotificationBox.removeNotification(notification, true); - clearPendingCrashReports(); -}); - -/** - * Tests that there is no notification if the only pending crash - * reports are over 28 days old. Also checks that if we put a newer - * crash with that older set, that we can still get a notification. - */ -add_task(function* test_several_pending() { - // Let's create some crash reports from 30 days ago. - let oldDate = new Date(Date.now() - (30 * DAY)); - yield createPendingCrashReports(3, oldDate); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.equal(notification, null, - "There should not be a notification if there are only " + - "old pending crash reports"); - // Now let's create a new one and check again - yield createPendingCrashReports(1); - notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - gNotificationBox.removeNotification(notification, true); - clearPendingCrashReports(); -}); - -/** - * Tests that the notification can submit a report. - */ -add_task(function* test_can_submit() { - let reportIDs = yield createPendingCrashReports(1); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - // Attempt to submit the notification by clicking on the submit - // button - let buttons = notification.querySelectorAll(".notification-button"); - // ...which should be the first button. - let submit = buttons[0]; - - let promiseReports = waitForSubmittedReports(reportIDs); - info("Sending crash report"); - submit.click(); - info("Sent!"); - // We'll not wait for the notification to finish its transition - - // we'll just remove it right away. - gNotificationBox.removeNotification(notification, true); - - info("Waiting on reports to be received."); - yield promiseReports; - info("Received!"); - clearPendingCrashReports(); -}); - -/** - * Tests that the notification can submit multiple reports. - */ -add_task(function* test_can_submit_several() { - let reportIDs = yield createPendingCrashReports(3); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - // Attempt to submit the notification by clicking on the submit - // button - let buttons = notification.querySelectorAll(".notification-button"); - // ...which should be the first button. - let submit = buttons[0]; - - let promiseReports = waitForSubmittedReports(reportIDs); - info("Sending crash reports"); - submit.click(); - info("Sent!"); - // We'll not wait for the notification to finish its transition - - // we'll just remove it right away. - gNotificationBox.removeNotification(notification, true); - - info("Waiting on reports to be received."); - yield promiseReports; - info("Received!"); - clearPendingCrashReports(); -}); - -/** - * Tests that choosing "Send Always" flips the autoSubmit pref - * and sends the pending crash reports. - */ -add_task(function* test_can_submit_always() { - let pref = "browser.crashReports.unsubmittedCheck.autoSubmit2"; - Assert.equal(Services.prefs.getBoolPref(pref), false, - "We should not be auto-submitting by default"); - - let reportIDs = yield createPendingCrashReports(1); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - // Attempt to submit the notification by clicking on the send all - // button - let buttons = notification.querySelectorAll(".notification-button"); - // ...which should be the second button. - let sendAll = buttons[1]; - - let promiseReports = waitForSubmittedReports(reportIDs); - info("Sending crash reports"); - sendAll.click(); - info("Sent!"); - // We'll not wait for the notification to finish its transition - - // we'll just remove it right away. - gNotificationBox.removeNotification(notification, true); - - info("Waiting on reports to be received."); - yield promiseReports; - info("Received!"); - - // Make sure the pref was set - Assert.equal(Services.prefs.getBoolPref(pref), true, - "The autoSubmit pref should have been set"); - - // And revert back to default now. - Services.prefs.clearUserPref(pref); - - clearPendingCrashReports(); -}); - -/** - * Tests that if the user has chosen to automatically send - * crash reports that no notification is displayed to the - * user. - */ -add_task(function* test_can_auto_submit() { - yield SpecialPowers.pushPrefEnv({ set: [ - ["browser.crashReports.unsubmittedCheck.autoSubmit2", true], - ]}); - - let reportIDs = yield createPendingCrashReports(3); - let promiseReports = waitForSubmittedReports(reportIDs); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.equal(notification, null, "There should be no notification"); - info("Waiting on reports to be received."); - yield promiseReports; - info("Received!"); - - clearPendingCrashReports(); - yield SpecialPowers.popPrefEnv(); -}); - -/** - * Tests that if the user chooses to dismiss the notification, - * then the current pending requests won't cause the notification - * to appear again in the future. - */ -add_task(function* test_can_ignore() { - let reportIDs = yield createPendingCrashReports(3); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - // Dismiss the notification by clicking on the "X" button. - let anonyNodes = document.getAnonymousNodes(notification)[0]; - let closeButton = anonyNodes.querySelector(".close-icon"); - closeButton.click(); - // We'll not wait for the notification to finish its transition - - // we'll just remove it right away. - gNotificationBox.removeNotification(notification, true); - yield waitForIgnoredReports(reportIDs); - - notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.equal(notification, null, "There should be no notification"); - - clearPendingCrashReports(); -}); - -/** - * Tests that if the notification is shown, then the - * lastShownDate is set for today. - */ -add_task(function* test_last_shown_date() { - yield createPendingCrashReports(1); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - let today = UnsubmittedCrashHandler.dateString(new Date()); - let lastShownDate = - UnsubmittedCrashHandler.prefs.getCharPref("lastShownDate"); - Assert.equal(today, lastShownDate, - "Last shown date should be today."); - - UnsubmittedCrashHandler.prefs.clearUserPref("lastShownDate"); - gNotificationBox.removeNotification(notification, true); - clearPendingCrashReports(); -}); - -/** - * Tests that if UnsubmittedCrashHandler is uninit with a - * notification still being shown, that - * browser.crashReports.unsubmittedCheck.shutdownWhileShowing is - * set to true. - */ -add_task(function* test_shutdown_while_showing() { - yield createPendingCrashReports(1); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - UnsubmittedCrashHandler.uninit(); - let shutdownWhileShowing = - UnsubmittedCrashHandler.prefs.getBoolPref("shutdownWhileShowing"); - Assert.ok(shutdownWhileShowing, - "We should have noticed that we uninitted while showing " + - "the notification."); - UnsubmittedCrashHandler.prefs.clearUserPref("shutdownWhileShowing"); - UnsubmittedCrashHandler.init(); - - gNotificationBox.removeNotification(notification, true); - clearPendingCrashReports(); -}); - -/** - * Tests that if UnsubmittedCrashHandler is uninit after - * the notification has been closed, that - * browser.crashReports.unsubmittedCheck.shutdownWhileShowing is - * not set in prefs. - */ -add_task(function* test_shutdown_while_not_showing() { - let reportIDs = yield createPendingCrashReports(1); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - // Dismiss the notification by clicking on the "X" button. - let anonyNodes = document.getAnonymousNodes(notification)[0]; - let closeButton = anonyNodes.querySelector(".close-icon"); - closeButton.click(); - // We'll not wait for the notification to finish its transition - - // we'll just remove it right away. - gNotificationBox.removeNotification(notification, true); - - yield waitForIgnoredReports(reportIDs); - - UnsubmittedCrashHandler.uninit(); - Assert.throws(() => { - UnsubmittedCrashHandler.prefs.getBoolPref("shutdownWhileShowing"); - }, "We should have noticed that the notification had closed before " + - "uninitting."); - UnsubmittedCrashHandler.init(); - - clearPendingCrashReports(); -}); - -/** - * Tests that if - * browser.crashReports.unsubmittedCheck.shutdownWhileShowing is - * set and the lastShownDate is today, then we don't decrement - * browser.crashReports.unsubmittedCheck.chancesUntilSuppress. - */ -add_task(function* test_dont_decrement_chances_on_same_day() { - let initChances = - UnsubmittedCrashHandler.prefs.getIntPref("chancesUntilSuppress"); - Assert.ok(initChances > 1, "We should start with at least 1 chance."); - - yield createPendingCrashReports(1); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - UnsubmittedCrashHandler.uninit(); - - gNotificationBox.removeNotification(notification, true); - - let shutdownWhileShowing = - UnsubmittedCrashHandler.prefs.getBoolPref("shutdownWhileShowing"); - Assert.ok(shutdownWhileShowing, - "We should have noticed that we uninitted while showing " + - "the notification."); - - let today = UnsubmittedCrashHandler.dateString(new Date()); - let lastShownDate = - UnsubmittedCrashHandler.prefs.getCharPref("lastShownDate"); - Assert.equal(today, lastShownDate, - "Last shown date should be today."); - - UnsubmittedCrashHandler.init(); - - notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should still be a notification"); - - let chances = - UnsubmittedCrashHandler.prefs.getIntPref("chancesUntilSuppress"); - - Assert.equal(initChances, chances, - "We should not have decremented chances."); - - gNotificationBox.removeNotification(notification, true); - clearPendingCrashReports(); -}); - -/** - * Tests that if - * browser.crashReports.unsubmittedCheck.shutdownWhileShowing is - * set and the lastShownDate is before today, then we decrement - * browser.crashReports.unsubmittedCheck.chancesUntilSuppress. - */ -add_task(function* test_decrement_chances_on_other_day() { - let initChances = - UnsubmittedCrashHandler.prefs.getIntPref("chancesUntilSuppress"); - Assert.ok(initChances > 1, "We should start with at least 1 chance."); - - yield createPendingCrashReports(1); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should be a notification"); - - UnsubmittedCrashHandler.uninit(); - - gNotificationBox.removeNotification(notification, true); - - let shutdownWhileShowing = - UnsubmittedCrashHandler.prefs.getBoolPref("shutdownWhileShowing"); - Assert.ok(shutdownWhileShowing, - "We should have noticed that we uninitted while showing " + - "the notification."); - - // Now pretend that the notification was shown yesterday. - let yesterday = UnsubmittedCrashHandler.dateString(new Date(Date.now() - DAY)); - UnsubmittedCrashHandler.prefs.setCharPref("lastShownDate", yesterday); - - UnsubmittedCrashHandler.init(); - - notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.ok(notification, "There should still be a notification"); - - let chances = - UnsubmittedCrashHandler.prefs.getIntPref("chancesUntilSuppress"); - - Assert.equal(initChances - 1, chances, - "We should have decremented our chances."); - UnsubmittedCrashHandler.prefs.clearUserPref("chancesUntilSuppress"); - - gNotificationBox.removeNotification(notification, true); - clearPendingCrashReports(); -}); - -/** - * Tests that if we've shutdown too many times showing the - * notification, and we've run out of chances, then - * browser.crashReports.unsubmittedCheck.suppressUntilDate is - * set for some days into the future. - */ -add_task(function* test_can_suppress_after_chances() { - // Pretend that a notification was shown yesterday. - let yesterday = UnsubmittedCrashHandler.dateString(new Date(Date.now() - DAY)); - UnsubmittedCrashHandler.prefs.setCharPref("lastShownDate", yesterday); - UnsubmittedCrashHandler.prefs.setBoolPref("shutdownWhileShowing", true); - UnsubmittedCrashHandler.prefs.setIntPref("chancesUntilSuppress", 0); - - yield createPendingCrashReports(1); - let notification = - yield UnsubmittedCrashHandler.checkForUnsubmittedCrashReports(); - Assert.equal(notification, null, - "There should be no notification if we've run out of chances"); - - // We should have set suppressUntilDate into the future - let suppressUntilDate = - UnsubmittedCrashHandler.prefs.getCharPref("suppressUntilDate"); - - let today = UnsubmittedCrashHandler.dateString(new Date()); - Assert.ok(suppressUntilDate > today, - "We should be suppressing until some days into the future."); - - UnsubmittedCrashHandler.prefs.clearUserPref("chancesUntilSuppress"); - UnsubmittedCrashHandler.prefs.clearUserPref("suppressUntilDate"); - UnsubmittedCrashHandler.prefs.clearUserPref("lastShownDate"); - clearPendingCrashReports(); -}); - -/** - * Tests that if there's a suppression date set, then no notification - * will be shown even if there are pending crash reports. - */ -add_task(function* test_suppression() { - let future = UnsubmittedCrashHandler.dateString(new Date(Date.now() + (DAY * 5))); - UnsubmittedCrashHandler.prefs.setCharPref("suppressUntilDate", future); - UnsubmittedCrashHandler.uninit(); - UnsubmittedCrashHandler.init(); - - Assert.ok(UnsubmittedCrashHandler.suppressed, - "The UnsubmittedCrashHandler should be suppressed."); - UnsubmittedCrashHandler.prefs.clearUserPref("suppressUntilDate"); - - UnsubmittedCrashHandler.uninit(); - UnsubmittedCrashHandler.init(); -}); - -/** - * Tests that if there's a suppression date set, but we've exceeded - * it, then we can show the notification again. - */ -add_task(function* test_end_suppression() { - let yesterday = UnsubmittedCrashHandler.dateString(new Date(Date.now() - DAY)); - UnsubmittedCrashHandler.prefs.setCharPref("suppressUntilDate", yesterday); - UnsubmittedCrashHandler.uninit(); - UnsubmittedCrashHandler.init(); - - Assert.ok(!UnsubmittedCrashHandler.suppressed, - "The UnsubmittedCrashHandler should not be suppressed."); - Assert.ok(!UnsubmittedCrashHandler.prefs.prefHasUserValue("suppressUntilDate"), - "The suppression date should been cleared from preferences."); - - UnsubmittedCrashHandler.uninit(); - UnsubmittedCrashHandler.init(); -}); |