diff --git a/layout/tools/reftest/README.txt b/layout/tools/reftest/README.txt index b8f846554aea..f1ec5b0ad86c 100644 --- a/layout/tools/reftest/README.txt +++ b/layout/tools/reftest/README.txt @@ -396,6 +396,7 @@ so 60/zoom is an integer. Printing Tests ============== + Now that the patch for bug 374050 has landed (https://bugzilla.mozilla.org/show_bug.cgi?id=374050), it is possible to create reftests that run in a paginated context. @@ -421,3 +422,23 @@ doesn't use exactly the same codepath as real print preview/print. In particular, scripting and frames are likely to cause problems; it is untested, though. That said, it should be sufficient for testing layout issues related to pagination. + +Plugin and IPC Process Crash Tests +================================== + +If you are running a test that causes an out-of-process plugin or IPC process +under Electrolysis to crash as part of a reftest, this will cause process +crash minidump files to be left in the profile directory. The test +infrastructure that runs the reftests will notice these minidump files and +dump out information from them, and these additional error messages in the logs +can end up erroneously being associated with other errors from the reftest run. +They are also confusing, since the appearance of "PROCESS-CRASH" messages in +the test run output can seem like a real problem, when in fact it is the +expected behavior. + +To indicate to the reftest framework that a test is expecting a plugin or +IPC process crash, have the test include "reftest-expect-process-crash" as +one of the root element's classes by the time the test has finished. This will +cause any minidump files that are generated while running the test to be removed +and they won't cause any error messages in the test run output. + diff --git a/layout/tools/reftest/reftest-content.js b/layout/tools/reftest/reftest-content.js index 7e05016ccb79..ffcd1c92c55f 100644 --- a/layout/tools/reftest/reftest-content.js +++ b/layout/tools/reftest/reftest-content.js @@ -464,6 +464,7 @@ function WaitForTestEnd(contentRootElement, inPrintMode) { state = STATE_COMPLETED; gFailureReason = "timed out while taking snapshot (bug in harness?)"; RemoveListeners(); + CheckForProcessCrashExpectation(); setTimeout(RecordResult, 0); return; } @@ -527,6 +528,7 @@ function OnDocumentLoad(event) // Go into reftest-wait mode belatedly. WaitForTestEnd(contentRootElement, inPrintMode); } else { + CheckForProcessCrashExpectation(); RecordResult(); } } @@ -555,6 +557,17 @@ function OnDocumentLoad(event) } } +function CheckForProcessCrashExpectation() +{ + var contentRootElement = content.document.documentElement; + if (contentRootElement && + contentRootElement.hasAttribute('class') && + contentRootElement.getAttribute('class').split(/\s+/) + .indexOf("reftest-expect-process-crash") != -1) { + SendExpectProcessCrash(); + } +} + function RecordResult() { LogInfo("RecordResult fired"); @@ -764,11 +777,16 @@ function SendInitCanvasWithSnapshot() } function SendScriptResults(runtimeMs, error, results) - { +{ sendAsyncMessage("reftest:ScriptResults", { runtimeMs: runtimeMs, error: error, results: results }); } +function SendExpectProcessCrash(runtimeMs) +{ + sendAsyncMessage("reftest:ExpectProcessCrash"); +} + function SendTestDone(runtimeMs) { sendAsyncMessage("reftest:TestDone", { runtimeMs: runtimeMs }); diff --git a/layout/tools/reftest/reftest.js b/layout/tools/reftest/reftest.js index fbb9fd75bd3f..1fd53728ef23 100644 --- a/layout/tools/reftest/reftest.js +++ b/layout/tools/reftest/reftest.js @@ -57,6 +57,10 @@ const NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX = "@mozilla.org/network/protocol;1?name="; const NS_XREAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; +const NS_DIRECTORY_SERVICE_CONTRACTID = + "@mozilla.org/file/directory_service;1"; +const NS_OBSERVER_SERVICE_CONTRACTID = + "@mozilla.org/observer-service;1"; var gLoadTimeout = 0; var gTimeoutHook = null; @@ -115,6 +119,11 @@ var gSlowestTestURL; var gDrawWindowFlags; +var gExpectingProcessCrash = false; +var gExpectedCrashDumpFiles = []; +var gUnexpectedCrashDumpFiles = { }; +var gCrashDumpDir; + const TYPE_REFTEST_EQUAL = '=='; const TYPE_REFTEST_NOTEQUAL = '!='; const TYPE_LOAD = 'load'; // test without a reference (just test that it does @@ -205,6 +214,11 @@ function IDForEventTarget(event) function OnRefTestLoad() { + gCrashDumpDir = CC[NS_DIRECTORY_SERVICE_CONTRACTID] + .getService(CI.nsIProperties) + .get("ProfD", CI.nsIFile); + gCrashDumpDir.append("minidumps"); + var prefs = Components.classes["@mozilla.org/preferences-service;1"]. getService(Components.interfaces.nsIPrefBranch2); try { @@ -278,7 +292,9 @@ function InitAndStartRefTests() gIOService = CC[IO_SERVICE_CONTRACTID].getService(CI.nsIIOService); gDebug = CC[DEBUG_CONTRACTID].getService(CI.nsIDebug2); - + + RegisterProcessCrashObservers(); + if (gRemote) { gServer = null; } else { @@ -1141,6 +1157,7 @@ function RecordResult(testRunTime, errorMsg, scriptResults) // First document has been loaded. // Proceed to load the second document. + CleanUpCrashDumpFiles(); StartCurrentURI(2); break; case 2: @@ -1201,6 +1218,7 @@ function RecordResult(testRunTime, errorMsg, scriptResults) UpdateCanvasCache(gURLs[0].url1, gCanvas1); UpdateCanvasCache(gURLs[0].url2, gCanvas2); + CleanUpCrashDumpFiles(); FinishTestItem(); break; default: @@ -1217,6 +1235,55 @@ function LoadFailed(why) FinishTestItem(); } +function RemoveExpectedCrashDumpFiles() +{ + if (gExpectingProcessCrash) { + for each (let crashFilename in gExpectedCrashDumpFiles) { + let file = gCrashDumpDir.clone(); + file.append(crashFilename); + if (file.exists()) { + file.remove(false); + } + } + } + gExpectedCrashDumpFiles.length = 0; +} + +function FindUnexpectedCrashDumpFiles() +{ + if (!gCrashDumpDir.exists()) { + return; + } + + let entries = gCrashDumpDir.directoryEntries; + if (!entries) { + return; + } + + let foundCrashDumpFile = false; + while (entries.hasMoreElements()) { + let file = entries.getNext().QueryInterface(CI.nsIFile); + let path = String(file.path); + if (path.match(/\.(dmp|extra)$/) && !gUnexpectedCrashDumpFiles[path]) { + if (!foundCrashDumpFile) { + foundCrashDumpFile = true; + gDumpLog("REFTEST TEST-UNEXPECTED-FAIL | " + gCurrentURL + + " | This test left crash dumps behind, but we weren't expecting it to!\n"); + } + gDumpLog("REFTEST INFO | Found unexpected crash dump file" + path + + ".\n"); + gUnexpectedCrashDumpFiles[path] = true; + } + } +} + +function CleanUpCrashDumpFiles() +{ + RemoveExpectedCrashDumpFiles(); + FindUnexpectedCrashDumpFiles(); + gExpectingProcessCrash = false; +} + function FinishTestItem() { // Replace document with BLANK_URL_FOR_CLEARING in case there are @@ -1316,6 +1383,10 @@ function RegisterMessageListenersAndLoadContentScript() "reftest:UpdateCanvasForInvalidation", function (m) { RecvUpdateCanvasForInvalidation(m.json.rects); } ); + gBrowserMessageManager.addMessageListener( + "reftest:ExpectProcessCrash", + function (m) { RecvExpectProcessCrash(); } + ); gBrowserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true); } @@ -1376,6 +1447,34 @@ function RecvUpdateCanvasForInvalidation(rects) UpdateCurrentCanvasForInvalidation(rects); } +function OnProcessCrashed(subject, topic, data) +{ + var id; + subject = subject.QueryInterface(CI.nsIPropertyBag2); + if (topic == "plugin-crashed") { + id = subject.getPropertyAsAString("pluginDumpID"); + } else if (topic == "ipc:content-shutdown") { + id = subject.getPropertyAsAString("dumpID"); + } + if (id) { + gExpectedCrashDumpFiles.push(id + ".dmp"); + gExpectedCrashDumpFiles.push(id + ".extra"); + } +} + +function RegisterProcessCrashObservers() +{ + var os = CC[NS_OBSERVER_SERVICE_CONTRACTID] + .getService(CI.nsIObserverService); + os.addObserver(OnProcessCrashed, "plugin-crashed", false); + os.addObserver(OnProcessCrashed, "ipc:content-shutdown", false); +} + +function RecvExpectProcessCrash() +{ + gExpectingProcessCrash = true; +} + function SendClear() { gBrowserMessageManager.sendAsyncMessage("reftest:Clear");