bug 510505 - add unit tests for breakpad exception handler. r=bsmedberg

This commit is contained in:
Ted Mielczarek 2009-09-10 07:49:42 -04:00
Родитель 078429ef66
Коммит ec60db8584
9 изменённых файлов: 240 добавлений и 2 удалений

Просмотреть файл

@ -70,6 +70,11 @@ TEST_HARNESS_COMPONENTS := \
httpd.js \
$(NULL)
ifdef MOZ_CRASHREPORTER
#XXX: should find a better way to do this
TEST_HARNESS_COMPONENTS += crashreporter_test.xpt
endif
# Rules for staging the necessary harness bits for a test package
PKG_STAGE = $(DIST)/test-package-stage

Просмотреть файл

@ -110,7 +110,7 @@ CPPSRCS = \
FORCE_STATIC_LIB = 1
ifdef ENABLE_TESTS
DIRS += test
TOOL_DIRS = test
endif
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -43,9 +43,23 @@ relativesrcdir = toolkit/crashreporter/test
include $(DEPTH)/config/autoconf.mk
MODULE = crashreporter
MODULE = crashreporter_test
XPCSHELL_TESTS = unit
LIBRARY_NAME = testcrasher
IS_COMPONENT = 1
USE_STATIC_LIBS = 1
XPIDLSRCS = nsITestCrasher.idl
CPPSRCS = \
nsTestCrasher.cpp \
$(NULL)
EXTRA_DSO_LIBS += xpcom
EXTRA_DSO_LDOPTS += $(LIBS_DIR) $(MOZ_COMPONENT_LIBS) $(XPCOM_GLUE_LDOPTS)
LOCAL_INCLUDES += -I$(XPIDL_GEN_DIR)
include $(topsrcdir)/config/rules.mk
_BROWSER_FILES = \
@ -58,3 +72,6 @@ _BROWSER_FILES = \
libs:: $(_BROWSER_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)/browser
$(NSINSTALL) -D $(DEPTH)/_tests/xpcshell/$(MODULE)/unit/components
$(INSTALL) $(SHARED_LIBRARY) \
$(DEPTH)/_tests/xpcshell/$(MODULE)/unit/components

Просмотреть файл

@ -0,0 +1,7 @@
#include "nsISupports.idl"
[scriptable, uuid(95464a04-6949-46cb-b621-d167790704a0)]
interface nsITestCrasher : nsISupports
{
void crash();
};

Просмотреть файл

@ -0,0 +1,44 @@
#include "nsServiceManagerUtils.h"
#include "nsIComponentManager.h"
#include "nsIGenericFactory.h"
#include "nsITestCrasher.h"
class nsTestCrasher : public nsITestCrasher
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSITESTCRASHER
nsTestCrasher() {}
private:
~nsTestCrasher() {};
};
NS_IMPL_ISUPPORTS1(nsTestCrasher, nsITestCrasher)
/* void crash (); */
NS_IMETHODIMP nsTestCrasher::Crash()
{
volatile int* foo = (int*)0x42;
*foo = 0;
// not reached
return NS_OK;
}
// 54afce51-38d7-4df0-9750-2f90f9ffbca2
#define NS_TESTCRASHER_CID \
{ 0x54afce51, 0x38d7, 0x4df0, {0x97, 0x50, 0x2f, 0x90, 0xf9, 0xff, 0xbc, 0xa2} }
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTestCrasher)
static const nsModuleComponentInfo components[] = {
{ "Test Crasher",
NS_TESTCRASHER_CID,
"@mozilla.org/testcrasher;1",
nsTestCrasherConstructor
}
};
NS_IMPL_NSGETMODULE(nsTestCrasherModule, components)

Просмотреть файл

@ -0,0 +1,9 @@
// enable crash reporting first
let cwd = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("CurWorkD", Components.interfaces.nsILocalFile);
let crashReporter =
Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
.getService(Components.interfaces.nsICrashReporter);
crashReporter.enabled = true;
crashReporter.minidumpPath = cwd;

Просмотреть файл

@ -0,0 +1,7 @@
// now actually crash
let cd = cwd.clone();
cd.append("components");
Components.manager instanceof Components.interfaces.nsIComponentRegistrar;
Components.manager.autoRegister(cd);
let crasher = Components.classes["@mozilla.org/testcrasher;1"].createInstance(Components.interfaces.nsITestCrasher);
crasher.crash();

Просмотреть файл

@ -0,0 +1,123 @@
/*
* Run an xpcshell subprocess and crash it.
*
* @param setup
* A string of JavaScript code to execute in the subprocess
* before crashing. If this is a function and not a string,
* it will have .toSource() called on it, and turned into
* a call to itself. (for programmer convenience)
* This code will be evaluted between crasher_subprocess_head.js
* and crasher_subprocess_tail.js, so it will have access
* to everything defined in crasher_subprocess_head.js,
* which includes "crashReporter", a variable holding
* the crash reporter service.
*
* @param callback
* A JavaScript function to be called after the subprocess
* crashes. It will be passed (minidump, extra), where
* minidump is an nsILocalFile of the minidump file produced,
* and extra is an object containing the key,value pairs from
* the .extra file.
*/
function do_crash(setup, callback)
{
// get current process filename (xpcshell)
let ds = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
let bin = ds.get("CurProcD", Components.interfaces.nsILocalFile);
bin.append("xpcshell");
if (!bin.exists()) {
bin.leafName = "xpcshell.exe";
do_check_true(bin.exists());
if (!bin.exists())
// weird, can't find xpcshell binary?
do_throw("Can't find xpcshell binary!");
}
// get Gre dir (GreD)
let greD = ds.get("GreD", Components.interfaces.nsILocalFile);
let headfile = do_get_file("crasher_subprocess_head.js");
let tailfile = do_get_file("crasher_subprocess_tail.js");
// run xpcshell -g GreD -f head -e "some setup code" -f tail
let process = Components.classes["@mozilla.org/process/util;1"]
.createInstance(Components.interfaces.nsIProcess);
process.init(bin);
let args = ['-g', greD.path,
'-f', headfile.path];
if (setup) {
if (typeof(setup) == "function")
// funky, but convenient
setup = "("+setup.toSource()+")();";
args.push('-e', setup);
}
args.push('-f', tailfile.path);
try {
process.run(true, args, args.length);
}
catch(ex) {} // on Windows we exit with a -1 status when crashing.
// should exit with an error (should have crashed)
do_check_neq(process.exitValue, 0);
// find minidump
let minidump = null;
let en = do_get_cwd().directoryEntries;
while (en.hasMoreElements()) {
let f = en.getNext().QueryInterface(Components.interfaces.nsILocalFile);
if (f.leafName.substr(-4) == ".dmp") {
minidump = f;
break;
}
}
if (minidump == null)
do_throw("No minidump found!");
let extrafile = minidump.clone();
extrafile.leafName = extrafile.leafName.slice(0, -4) + ".extra";
do_check_true(extrafile.exists());
let extra = parseKeyValuePairsFromFile(extrafile);
if (callback)
callback(minidump, extra);
if (minidump.exists())
minidump.remove(false);
if (extrafile.exists())
extrafile.remove(false);
}
// Utility functions for parsing .extra files
function parseKeyValuePairs(text) {
var lines = text.split('\n');
var data = {};
for (let i = 0; i < lines.length; i++) {
if (lines[i] == '')
continue;
// can't just .split() because the value might contain = characters
let eq = lines[i].indexOf('=');
if (eq != -1) {
let [key, value] = [lines[i].substring(0, eq),
lines[i].substring(eq + 1)];
if (key && value)
data[key] = value.replace("\\n", "\n", "g").replace("\\\\", "\\", "g");
}
}
return data;
}
function parseKeyValuePairsFromFile(file) {
var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
fstream.init(file, -1, 0, 0);
var is = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Components.interfaces.nsIConverterInputStream);
is.init(fstream, "UTF-8", 1024, Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
var str = {};
var contents = '';
while (is.readString(4096, str) != 0) {
contents += str.value;
}
is.close();
fstream.close();
return parseKeyValuePairs(contents);
}

Просмотреть файл

@ -0,0 +1,26 @@
function run_test()
{
if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
dump("INFO | test_crashreporter.js | Can't test crashreporter in a non-libxul build.\n");
return;
}
// try a basic crash
do_crash(null, function(mdump, extra) {
do_check_true(mdump.exists());
do_check_true(mdump.fileSize > 0);
do_check_true('StartupTime' in extra);
do_check_true('CrashTime' in extra);
});
// check setting some basic data
do_crash(function() {
crashReporter.annotateCrashReport("TestKey", "TestValue");
crashReporter.appendAppNotesToCrashReport("Junk");
crashReporter.appendAppNotesToCrashReport("MoreJunk");
},
function(mdump, extra) {
do_check_eq(extra.TestKey, "TestValue");
do_check_eq(extra.Notes, "JunkMoreJunk");
});
}