Loading testing/mochitest/Makefile.in +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ _SERV_FILES = \ harness.xul \ browser-test-overlay.xul \ browser-test.js \ cc-analyzer.js \ chrome-harness.js \ browser-harness.xul \ redirect.html \ Loading testing/mochitest/browser-test-overlay.xul +1 −0 Original line number Diff line number Diff line Loading @@ -8,4 +8,5 @@ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"/> <script type="application/javascript" src="chrome://mochikit/content/browser-test.js"/> <script type="application/javascript" src="chrome://mochikit/content/cc-analyzer.js"/> </overlay> testing/mochitest/browser-test.js +62 −28 Original line number Diff line number Diff line Loading @@ -7,6 +7,12 @@ if (Cc === undefined) { var Ci = Components.interfaces; var Cu = Components.utils; } Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); window.addEventListener("load", testOnLoad, false); function testOnLoad() { Loading @@ -15,20 +21,17 @@ function testOnLoad() { gConfig = readConfig(); if (gConfig.testRoot == "browser" || gConfig.testRoot == "webapprtChrome") { // Make sure to launch the test harness for the first opened window only var prefs = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefBranch); var prefs = Services.prefs; if (prefs.prefHasUserValue("testing.browserTestHarness.running")) return; prefs.setBoolPref("testing.browserTestHarness.running", true); var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. getService(Ci.nsIWindowWatcher); var sstring = Cc["@mozilla.org/supports-string;1"]. createInstance(Ci.nsISupportsString); sstring.data = location.search; ww.openWindow(window, "chrome://mochikit/content/browser-harness.xul", "browserTest", Services.ww.openWindow(window, "chrome://mochikit/content/browser-harness.xul", "browserTest", "chrome,centerscreen,dialog=no,resizable,titlebar,toolbar=no,width=800,height=600", sstring); } else { // This code allows us to redirect without requiring specialpowers for chrome and a11y tests. Loading @@ -53,15 +56,10 @@ function Tester(aTests, aDumper, aCallback) { this.dumper = aDumper; this.tests = aTests; this.callback = aCallback; this._cs = Cc["@mozilla.org/consoleservice;1"]. getService(Ci.nsIConsoleService); this._wm = Cc["@mozilla.org/appshell/window-mediator;1"]. getService(Ci.nsIWindowMediator); this._fm = Cc["@mozilla.org/focus-manager;1"]. getService(Ci.nsIFocusManager); this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. getService(Ci.mozIJSSubScriptLoader); this.openedWindows = {}; this.openedURLs = {}; this._scriptLoader = Services.scriptloader; this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", this.EventUtils); var simpleTestScope = {}; this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js", simpleTestScope); Loading @@ -79,6 +77,8 @@ Tester.prototype = { checker: null, currentTestIndex: -1, lastStartTime: null, openedWindows: null, get currentTest() { return this.tests[this.currentTestIndex]; }, Loading @@ -92,7 +92,9 @@ Tester.prototype = { gConfig = readConfig(); this.repeat = gConfig.repeat; this.dumper.dump("*** Start BrowserChrome Test Results ***\n"); this._cs.registerListener(this); Services.console.registerListener(this); Services.obs.addObserver(this, "chrome-document-global-created", false); Services.obs.addObserver(this, "content-document-global-created", false); this._globalProperties = Object.keys(window); this._globalPropertyWhitelist = ["navigator", "constructor", "Application", "__SS_tabsToRestore", "__SSi", "webConsoleCommandController", Loading Loading @@ -124,7 +126,7 @@ Tester.prototype = { } this.dumper.dump("TEST-INFO | checking window state\n"); let windowsEnum = this._wm.getEnumerator(null); let windowsEnum = Services.wm.getEnumerator(null); while (windowsEnum.hasMoreElements()) { let win = windowsEnum.getNext(); if (win != window && !win.closed && Loading Loading @@ -159,7 +161,9 @@ Tester.prototype = { this.nextTest(); } else{ this._cs.unregisterListener(this); Services.console.unregisterListener(this); Services.obs.removeObserver(this, "chrome-document-global-created"); Services.obs.removeObserver(this, "content-document-global-created"); this.dumper.dump("\nINFO TEST-START | Shutdown\n"); if (this.tests.length) { Loading @@ -186,10 +190,34 @@ Tester.prototype = { this.callback(this.tests); this.callback = null; this.tests = null; this.openedWindows = null; } }, observe: function Tester_observe(aConsoleMessage) { observe: function Tester_observe(aSubject, aTopic, aData) { if (!aTopic) { this.onConsoleMessage(aSubject); } else if (this.currentTest) { this.onDocumentCreated(aSubject); } }, onDocumentCreated: function Tester_onDocumentCreated(aWindow) { let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let outerID = utils.outerWindowID; let innerID = utils.currentInnerWindowID; if (!(outerID in this.openedWindows)) { this.openedWindows[outerID] = this.currentTest; } this.openedWindows[innerID] = this.currentTest; let url = aWindow.location.href || "about:blank"; this.openedURLs[outerID] = this.openedURLs[innerID] = url; }, onConsoleMessage: function Tester_onConsoleMessage(aConsoleMessage) { // Ignore empty messages. if (!aConsoleMessage.message) return; Loading Loading @@ -265,12 +293,20 @@ Tester.prototype = { // Schedule GC and CC runs before finishing in order to detect // DOM windows leaked by our tests or the tested code. Cu.schedulePreciseGC((function () { let winutils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); winutils.garbageCollect(); winutils.garbageCollect(); winutils.garbageCollect(); let analyzer = new CCAnalyzer(); analyzer.run(function () { for (let obj of analyzer.find("nsGlobalWindow ")) { let m = obj.name.match(/^nsGlobalWindow #(\d+)/); if (m && m[1] in this.openedWindows) { let test = this.openedWindows[m[1]]; let msg = "leaked until shutdown [" + obj.name + " " + (this.openedURLs[m[1]] || "NULL") + "]"; test.addResult(new testResult(false, msg, "", false)); } } this.finish(); }.bind(this)); }).bind(this)); return; } Loading Loading @@ -462,9 +498,7 @@ function testScope(aTester, aTest) { }; this.executeSoon = function test_executeSoon(func) { let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); tm.mainThread.dispatch({ Services.tm.mainThread.dispatch({ run: function() { func(); } Loading testing/mochitest/cc-analyzer.js 0 → 100644 +126 −0 Original line number Diff line number Diff line /* 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/. */ function CCAnalyzer() { } CCAnalyzer.prototype = { clear: function () { this.callback = null; this.processingCount = 0; this.graph = {}; this.roots = []; this.garbage = []; this.edges = []; this.listener = null; }, run: function (aCallback) { this.clear(); this.callback = aCallback; this.listener = Cc["@mozilla.org/cycle-collector-logger;1"]. createInstance(Ci.nsICycleCollectorListener); this.listener.disableLog = true; this.listener.wantAfterProcessing = true; this.runCC(3); }, runCC: function (aCounter) { let utils = window.QueryInterface(Ci.nsIInterfaceRequestor). getInterface(Ci.nsIDOMWindowUtils); if (aCounter > 1) { utils.garbageCollect(); setTimeout(this.runCC.bind(this, aCounter - 1), 0); } else { utils.garbageCollect(this.listener); this.processLog(); } }, processLog: function () { // Process entire heap step by step in 5K chunks for (let i = 0; i < 5000; i++) { if (!this.listener.processNext(this)) { this.callback(); this.clear(); return; } } // Next chunk on timeout. setTimeout(this.processLog.bind(this), 0); }, noteRefCountedObject: function (aAddress, aRefCount, aObjectDescription) { let o = this.ensureObject(aAddress); o.address = aAddress; o.refcount = aRefCount; o.name = aObjectDescription; }, noteGCedObject: function (aAddress, aMarked, aObjectDescription) { let o = this.ensureObject(aAddress); o.address = aAddress; o.gcmarked = aMarked; o.name = aObjectDescription; }, noteEdge: function (aFromAddress, aToAddress, aEdgeName) { let fromObject = this.ensureObject(aFromAddress); let toObject = this.ensureObject(aToAddress); fromObject.edges.push({name: aEdgeName, to: toObject}); toObject.owners.push({name: aEdgeName, from: fromObject}); this.edges.push({ name: aEdgeName, from: fromObject, to: toObject }); }, describeRoot: function (aAddress, aKnownEdges) { let o = this.ensureObject(aAddress); o.root = true; o.knownEdges = aKnownEdges; this.roots.push(o); }, describeGarbage: function (aAddress) { let o = this.ensureObject(aAddress); o.garbage = true; this.garbage.push(o); }, ensureObject: function (aAddress) { if (!this.graph[aAddress]) this.graph[aAddress] = new CCObject(); return this.graph[aAddress]; }, find: function (aText) { let result = []; for each (let o in this.graph) { if (!o.garbage && o.name.indexOf(aText) >= 0) result.push(o); } return result; } }; function CCObject() { this.name = ""; this.address = null; this.refcount = 0; this.gcmarked = false; this.root = false; this.garbage = false; this.knownEdges = 0; this.edges = []; this.owners = []; } testing/mochitest/jar.mn +1 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ mochikit.jar: content/browser-harness.xul (browser-harness.xul) content/browser-test.js (browser-test.js) content/browser-test-overlay.xul (browser-test-overlay.xul) content/cc-analyzer.js (cc-analyzer.js) content/chrome-harness.js (chrome-harness.js) content/harness-overlay.xul (harness-overlay.xul) content/harness.xul (harness.xul) Loading Loading
testing/mochitest/Makefile.in +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ _SERV_FILES = \ harness.xul \ browser-test-overlay.xul \ browser-test.js \ cc-analyzer.js \ chrome-harness.js \ browser-harness.xul \ redirect.html \ Loading
testing/mochitest/browser-test-overlay.xul +1 −0 Original line number Diff line number Diff line Loading @@ -8,4 +8,5 @@ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"/> <script type="application/javascript" src="chrome://mochikit/content/browser-test.js"/> <script type="application/javascript" src="chrome://mochikit/content/cc-analyzer.js"/> </overlay>
testing/mochitest/browser-test.js +62 −28 Original line number Diff line number Diff line Loading @@ -7,6 +7,12 @@ if (Cc === undefined) { var Ci = Components.interfaces; var Cu = Components.utils; } Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); window.addEventListener("load", testOnLoad, false); function testOnLoad() { Loading @@ -15,20 +21,17 @@ function testOnLoad() { gConfig = readConfig(); if (gConfig.testRoot == "browser" || gConfig.testRoot == "webapprtChrome") { // Make sure to launch the test harness for the first opened window only var prefs = Cc["@mozilla.org/preferences-service;1"]. getService(Ci.nsIPrefBranch); var prefs = Services.prefs; if (prefs.prefHasUserValue("testing.browserTestHarness.running")) return; prefs.setBoolPref("testing.browserTestHarness.running", true); var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. getService(Ci.nsIWindowWatcher); var sstring = Cc["@mozilla.org/supports-string;1"]. createInstance(Ci.nsISupportsString); sstring.data = location.search; ww.openWindow(window, "chrome://mochikit/content/browser-harness.xul", "browserTest", Services.ww.openWindow(window, "chrome://mochikit/content/browser-harness.xul", "browserTest", "chrome,centerscreen,dialog=no,resizable,titlebar,toolbar=no,width=800,height=600", sstring); } else { // This code allows us to redirect without requiring specialpowers for chrome and a11y tests. Loading @@ -53,15 +56,10 @@ function Tester(aTests, aDumper, aCallback) { this.dumper = aDumper; this.tests = aTests; this.callback = aCallback; this._cs = Cc["@mozilla.org/consoleservice;1"]. getService(Ci.nsIConsoleService); this._wm = Cc["@mozilla.org/appshell/window-mediator;1"]. getService(Ci.nsIWindowMediator); this._fm = Cc["@mozilla.org/focus-manager;1"]. getService(Ci.nsIFocusManager); this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. getService(Ci.mozIJSSubScriptLoader); this.openedWindows = {}; this.openedURLs = {}; this._scriptLoader = Services.scriptloader; this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", this.EventUtils); var simpleTestScope = {}; this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/specialpowersAPI.js", simpleTestScope); Loading @@ -79,6 +77,8 @@ Tester.prototype = { checker: null, currentTestIndex: -1, lastStartTime: null, openedWindows: null, get currentTest() { return this.tests[this.currentTestIndex]; }, Loading @@ -92,7 +92,9 @@ Tester.prototype = { gConfig = readConfig(); this.repeat = gConfig.repeat; this.dumper.dump("*** Start BrowserChrome Test Results ***\n"); this._cs.registerListener(this); Services.console.registerListener(this); Services.obs.addObserver(this, "chrome-document-global-created", false); Services.obs.addObserver(this, "content-document-global-created", false); this._globalProperties = Object.keys(window); this._globalPropertyWhitelist = ["navigator", "constructor", "Application", "__SS_tabsToRestore", "__SSi", "webConsoleCommandController", Loading Loading @@ -124,7 +126,7 @@ Tester.prototype = { } this.dumper.dump("TEST-INFO | checking window state\n"); let windowsEnum = this._wm.getEnumerator(null); let windowsEnum = Services.wm.getEnumerator(null); while (windowsEnum.hasMoreElements()) { let win = windowsEnum.getNext(); if (win != window && !win.closed && Loading Loading @@ -159,7 +161,9 @@ Tester.prototype = { this.nextTest(); } else{ this._cs.unregisterListener(this); Services.console.unregisterListener(this); Services.obs.removeObserver(this, "chrome-document-global-created"); Services.obs.removeObserver(this, "content-document-global-created"); this.dumper.dump("\nINFO TEST-START | Shutdown\n"); if (this.tests.length) { Loading @@ -186,10 +190,34 @@ Tester.prototype = { this.callback(this.tests); this.callback = null; this.tests = null; this.openedWindows = null; } }, observe: function Tester_observe(aConsoleMessage) { observe: function Tester_observe(aSubject, aTopic, aData) { if (!aTopic) { this.onConsoleMessage(aSubject); } else if (this.currentTest) { this.onDocumentCreated(aSubject); } }, onDocumentCreated: function Tester_onDocumentCreated(aWindow) { let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let outerID = utils.outerWindowID; let innerID = utils.currentInnerWindowID; if (!(outerID in this.openedWindows)) { this.openedWindows[outerID] = this.currentTest; } this.openedWindows[innerID] = this.currentTest; let url = aWindow.location.href || "about:blank"; this.openedURLs[outerID] = this.openedURLs[innerID] = url; }, onConsoleMessage: function Tester_onConsoleMessage(aConsoleMessage) { // Ignore empty messages. if (!aConsoleMessage.message) return; Loading Loading @@ -265,12 +293,20 @@ Tester.prototype = { // Schedule GC and CC runs before finishing in order to detect // DOM windows leaked by our tests or the tested code. Cu.schedulePreciseGC((function () { let winutils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); winutils.garbageCollect(); winutils.garbageCollect(); winutils.garbageCollect(); let analyzer = new CCAnalyzer(); analyzer.run(function () { for (let obj of analyzer.find("nsGlobalWindow ")) { let m = obj.name.match(/^nsGlobalWindow #(\d+)/); if (m && m[1] in this.openedWindows) { let test = this.openedWindows[m[1]]; let msg = "leaked until shutdown [" + obj.name + " " + (this.openedURLs[m[1]] || "NULL") + "]"; test.addResult(new testResult(false, msg, "", false)); } } this.finish(); }.bind(this)); }).bind(this)); return; } Loading Loading @@ -462,9 +498,7 @@ function testScope(aTester, aTest) { }; this.executeSoon = function test_executeSoon(func) { let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); tm.mainThread.dispatch({ Services.tm.mainThread.dispatch({ run: function() { func(); } Loading
testing/mochitest/cc-analyzer.js 0 → 100644 +126 −0 Original line number Diff line number Diff line /* 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/. */ function CCAnalyzer() { } CCAnalyzer.prototype = { clear: function () { this.callback = null; this.processingCount = 0; this.graph = {}; this.roots = []; this.garbage = []; this.edges = []; this.listener = null; }, run: function (aCallback) { this.clear(); this.callback = aCallback; this.listener = Cc["@mozilla.org/cycle-collector-logger;1"]. createInstance(Ci.nsICycleCollectorListener); this.listener.disableLog = true; this.listener.wantAfterProcessing = true; this.runCC(3); }, runCC: function (aCounter) { let utils = window.QueryInterface(Ci.nsIInterfaceRequestor). getInterface(Ci.nsIDOMWindowUtils); if (aCounter > 1) { utils.garbageCollect(); setTimeout(this.runCC.bind(this, aCounter - 1), 0); } else { utils.garbageCollect(this.listener); this.processLog(); } }, processLog: function () { // Process entire heap step by step in 5K chunks for (let i = 0; i < 5000; i++) { if (!this.listener.processNext(this)) { this.callback(); this.clear(); return; } } // Next chunk on timeout. setTimeout(this.processLog.bind(this), 0); }, noteRefCountedObject: function (aAddress, aRefCount, aObjectDescription) { let o = this.ensureObject(aAddress); o.address = aAddress; o.refcount = aRefCount; o.name = aObjectDescription; }, noteGCedObject: function (aAddress, aMarked, aObjectDescription) { let o = this.ensureObject(aAddress); o.address = aAddress; o.gcmarked = aMarked; o.name = aObjectDescription; }, noteEdge: function (aFromAddress, aToAddress, aEdgeName) { let fromObject = this.ensureObject(aFromAddress); let toObject = this.ensureObject(aToAddress); fromObject.edges.push({name: aEdgeName, to: toObject}); toObject.owners.push({name: aEdgeName, from: fromObject}); this.edges.push({ name: aEdgeName, from: fromObject, to: toObject }); }, describeRoot: function (aAddress, aKnownEdges) { let o = this.ensureObject(aAddress); o.root = true; o.knownEdges = aKnownEdges; this.roots.push(o); }, describeGarbage: function (aAddress) { let o = this.ensureObject(aAddress); o.garbage = true; this.garbage.push(o); }, ensureObject: function (aAddress) { if (!this.graph[aAddress]) this.graph[aAddress] = new CCObject(); return this.graph[aAddress]; }, find: function (aText) { let result = []; for each (let o in this.graph) { if (!o.garbage && o.name.indexOf(aText) >= 0) result.push(o); } return result; } }; function CCObject() { this.name = ""; this.address = null; this.refcount = 0; this.gcmarked = false; this.root = false; this.garbage = false; this.knownEdges = 0; this.edges = []; this.owners = []; }
testing/mochitest/jar.mn +1 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ mochikit.jar: content/browser-harness.xul (browser-harness.xul) content/browser-test.js (browser-test.js) content/browser-test-overlay.xul (browser-test-overlay.xul) content/cc-analyzer.js (cc-analyzer.js) content/chrome-harness.js (chrome-harness.js) content/harness-overlay.xul (harness-overlay.xul) content/harness.xul (harness.xul) Loading