toolkit/components/crashes/CrashManagerTest.jsm
author Gabriele Svelto <gsvelto@mozilla.com>
Thu, 11 May 2017 14:03:50 +0200
changeset 409910 001d49708a355c9b127fc338722959874cfc7552
parent 406192 c143205c3f2025e131b8dd4efa64d53fd7e5ac0b
child 414701 9c7e4d4547b752ee3bb9b603218d143ffe9f6c49
permissions -rw-r--r--
Bug 1359326 - Run the minidump analyzer directly from the CrashService code; r=bsmedberg This patch removes the C++ code used to run the minidump analyzer when a content process crashes, and replaces it with JS code within the CrashService object. This removes the need for a separate shutdown blocker in C++ code and allows end-to-end testing of the crash service functionality. Additionally the exception handler code can be simplified since it's now only used to run the crash reporter client. The test added to test_crash_service.js covers computing the minidump SHA256 hash (bug 1322611) and of the minidump analyzer itself (bug 1280477). MozReview-Commit-ID: LO5w839NHev

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * This file provides common and shared functionality to facilitate
 * testing of the Crashes component (CrashManager.jsm).
 */

"use strict";

const {classes: Cc, interfaces: Ci, utils: Cu} = Components;

this.EXPORTED_SYMBOLS = [
  "configureLogging",
  "getManager",
  "sleep",
  "TestingCrashManager",
];

Cu.import("resource://gre/modules/CrashManager.jsm", this);
Cu.import("resource://gre/modules/Log.jsm", this);
Cu.import("resource://gre/modules/osfile.jsm", this);
Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/Timer.jsm", this);

var loggingConfigured = false;

this.configureLogging = function() {
  if (loggingConfigured) {
    return;
  }

  let log = Log.repository.getLogger("Crashes.CrashManager");
  log.level = Log.Level.All;
  let appender = new Log.DumpAppender();
  appender.level = Log.Level.All;
  log.addAppender(appender);
  loggingConfigured = true;
};

this.sleep = function(wait) {
  return new Promise(resolve => {

    setTimeout(() => {
      resolve();
    }, wait);

  });
};

this.TestingCrashManager = function(options) {
  CrashManager.call(this, options);
}

this.TestingCrashManager.prototype = {
  __proto__: CrashManager.prototype,

  createDummyDump(submitted = false, date = new Date(), hr = false) {
    let uuid = Cc["@mozilla.org/uuid-generator;1"]
                .getService(Ci.nsIUUIDGenerator)
                .generateUUID()
                .toString();
    uuid = uuid.substring(1, uuid.length - 1);

    let path;
    let mode;
    if (submitted) {
      if (hr) {
        path = OS.Path.join(this._submittedDumpsDir, "bp-hr-" + uuid + ".txt");
      } else {
        path = OS.Path.join(this._submittedDumpsDir, "bp-" + uuid + ".txt");
      }
      mode = OS.Constants.libc.S_IRUSR | OS.Constants.libc.S_IWUSR |
            OS.Constants.libc.S_IRGRP | OS.Constants.libc.S_IROTH;
    } else {
      path = OS.Path.join(this._pendingDumpsDir, uuid + ".dmp");
      mode = OS.Constants.libc.S_IRUSR | OS.Constants.libc.S_IWUSR;
    }

    return (async function() {
      let f = await OS.File.open(path, {create: true}, {unixMode: mode});
      await f.setDates(date, date);
      await f.close();
      dump("Created fake crash: " + path + "\n");

      return uuid;
    })();
  },

  createIgnoredDumpFile(filename, submitted = false) {
    let path;
    if (submitted) {
      path = OS.Path.join(this._submittedDumpsDir, filename);
    } else {
      path = OS.Path.join(this._pendingDumpsDir, filename);
    }

    return (async function() {
      let mode = OS.Constants.libc.S_IRUSR | OS.Constants.libc.S_IWUSR;
      await OS.File.open(path, {create: true}, {unixMode: mode});
      dump("Create ignored dump file: " + path + "\n");
    })();
  },

  createEventsFile(filename, type, date, content, index = 0) {
    let path = OS.Path.join(this._eventsDirs[index], filename);

    let data = type + "\n" +
               Math.floor(date.getTime() / 1000) + "\n" +
               content;
    let encoder = new TextEncoder();
    let array = encoder.encode(data);

    return (async function() {
      await OS.File.writeAtomic(path, array);
      await OS.File.setDates(path, date, date);
    })();
  },

  /**
   * Overwrite event file handling to process our test file type.
   *
   * We can probably delete this once we have actual events defined.
   */
  _handleEventFilePayload(store, entry, type, date, payload) {
    if (type == "test.1") {
      if (payload == "malformed") {
        return this.EVENT_FILE_ERROR_MALFORMED;
      } else if (payload == "success") {
        return this.EVENT_FILE_SUCCESS;
      }
      return this.EVENT_FILE_ERROR_UNKNOWN_EVENT;
    }

    return CrashManager.prototype._handleEventFilePayload.call(this,
                                                               store,
                                                               entry,
                                                               type,
                                                               date,
                                                               payload);
  },
};

var DUMMY_DIR_COUNT = 0;

this.getManager = function() {
  return (async function() {
    const dirMode = OS.Constants.libc.S_IRWXU;
    let baseFile = OS.Constants.Path.profileDir;

    function makeDir(create = true) {
      return (async function() {
        let path = OS.Path.join(baseFile, "dummy-dir-" + DUMMY_DIR_COUNT++);

        if (!create) {
          return path;
        }

        dump("Creating directory: " + path + "\n");
        await OS.File.makeDir(path, {unixMode: dirMode});

        return path;
      })();
    }

    let pendingD = await makeDir();
    let submittedD = await makeDir();
    let eventsD1 = await makeDir();
    let eventsD2 = await makeDir();

    // Store directory is created at run-time if needed. Ensure those code
    // paths are triggered.
    let storeD = await makeDir(false);

    let m = new TestingCrashManager({
      pendingDumpsDir: pendingD,
      submittedDumpsDir: submittedD,
      eventsDirs: [eventsD1, eventsD2],
      storeDir: storeD,
      telemetryStoreSizeKey: "CRASH_STORE_COMPRESSED_BYTES",
    });

    return m;
  })();
};