2 <?xml-stylesheet href=
"chrome://global/skin" type=
"text/css"?>
3 <!-- This Source Code Form is subject to the terms of the Mozilla Public
4 - License, v. 2.0. If a copy of the MPL was not distributed with this
5 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
7 <window id=
"browserTestHarness"
8 xmlns=
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
10 title=
"Browser chrome tests"
12 <script src=
"chrome://mochikit/content/tests/SimpleTest/MozillaLogger.js"/>
13 <script src=
"chrome://mochikit/content/tests/SimpleTest/LogController.js"/>
14 <script src=
"chrome://mochikit/content/tests/SimpleTest/TestRunner.js"/>
15 <script src=
"chrome://mochikit/content/chrome-harness.js"/>
16 <script src=
"chrome://mochikit/content/manifestLibrary.js" />
17 <script src=
"chrome://mochikit/content/mochitestListingsUtils.js" />
18 <script src=
"chrome://mochikit/content/chunkifyTests.js"/>
19 <style xmlns=
"http://www.w3.org/1999/xhtml"><![CDATA[
22 background-color: window;
28 border:
2px solid black;
32 background-color: #
0d0;
36 background-color: red;
40 background-color: orange;
62 text-decoration: underline;
65 <script type=
"application/javascript"><![CDATA[
71 if (gConfig.logFile) {
73 logger = new MozillaFileLogger(gConfig.logFile)
75 dump(
"TEST-UNEXPECTED-FAIL | (browser-harness.xhtml) | " +
76 "Error trying to log to " + gConfig.logFile +
": " + ex +
"\n");
79 delete this.fileLogger;
80 return this.fileLogger = logger;
82 structuredLogger: TestRunner.structuredLogger,
84 this.structuredLogger.info(str);
87 this.fileLogger.log(str);
92 this.fileLogger.close();
96 function TestStart() {
97 gConfig = readConfig();
99 // Update the title for --start-at and --end-at.
100 if (gConfig.startAt || gConfig.endAt)
101 document.getElementById(
"runTestsButton").label =
102 "Run subset of tests";
105 setTimeout(runTests,
0);
110 function browserTest(aTestFile) {
111 this.path = aTestFile.url;
112 this.expected = aTestFile.expected;
113 this.https_first_disabled = aTestFile.https_first_disabled || false;
114 this.allow_xul_xbl = aTestFile.allow_xul_xbl || false;
115 this.dumper = gDumper;
119 this.unexpectedTimeouts =
0;
120 this.lastOutputTime =
0;
122 browserTest.prototype = {
124 return this.results.filter(t =
> !t.info && !t.todo && t.pass).length;
127 return this.results.filter(t =
> !t.info && t.todo && t.pass).length;
130 return this.results.filter(t =
> !t.info && !t.pass).length;
132 get allowedFailureCount() {
133 return this.results.filter(t =
> t.allowedFailure).length;
136 addResult: function addResult(result) {
137 this.lastOutputTime = Date.now();
138 this.results.push(result);
142 ChromeUtils.addProfilerMarker(
"TEST-INFO", {category:
"Test"},
144 this.dumper.structuredLogger.info(result.msg);
149 this.dumper.structuredLogger.testStatus(this.path,
154 let markerName =
"TEST-";
156 markerName += result.todo ?
"KNOWN-FAIL" :
"PASS";
159 markerName +=
"UNEXPECTED-" + result.status;
161 let markerText = result.name;
163 markerText +=
" - " + result.msg;
165 ChromeUtils.addProfilerMarker(markerName, {category:
"Test"}, markerText);
168 setDuration: function setDuration(duration) {
169 this.duration = duration;
173 let txtToHTML = Cc[
"@mozilla.org/txttohtmlconv;1"].
174 getService(Ci.mozITXTToHTMLConv);
175 function _entityEncode(str) {
176 return txtToHTML.scanTXT(str, Ci.mozITXTToHTMLConv.kEntities);
178 var path = _entityEncode(this.path);
179 var html = this.results.map(function (t) {
180 var classname =
"result ";
181 var result =
"TEST-";
187 classname +=
"passed";
189 result +=
"KNOWN-FAIL";
194 classname +=
"failed";
195 result +=
"UNEXPECTED-" + t.status;
197 var message = t.name + (t.msg ?
" - " + t.msg :
"");
198 var text = result +
" | " + path +
" | " + _entityEncode(message);
199 if (!t.info && !t.pass) {
200 return '
<p class=
"' + classname + '" id=\
"ERROR' + (gErrorCount++) + '">' +
201 text +
" <a href=\"javascript:scrollTo('ERROR
" + gErrorCount + "')\
">NEXT ERROR</a></p>";
203 return '
<p class=
"' + classname + '">' + text +
"</p>";
206 html +=
"<p class=\"info\
">TEST-END | " + path +
" | finished in " +
207 this.duration +
" ms</p>";
213 // Returns an array of browserTest objects for all the selected tests
214 function runTests() {
215 gConfig.baseurl =
"chrome://mochitests/content";
216 getTestList(gConfig, loadTestList);
219 function loadTestList(links) {
226 var fileNameRegexp = /browser_.+\.js$/;
227 arrayOfTestFiles(links, fileNames, fileNameRegexp);
229 if (gConfig.startAt || gConfig.endAt) {
230 fileNames = skipTests(fileNames, gConfig.startAt, gConfig.endAt);
233 createTester(fileNames.map(function (f) { return new browserTest(f); }));
236 function setStatus(aStatusString) {
237 document.getElementById(
"status").value = aStatusString;
240 function createTester(links) {
242 if (gConfig.testRoot ==
"browser") {
243 const IS_THUNDERBIRD = Services.appinfo.ID ==
"{3550f703-e582-4d05-9a08-453d09bdfdc6}";
244 winType = IS_THUNDERBIRD ?
"mail:3pane" :
"navigator:browser";
247 throw new Error(
"Unrecognized gConfig.testRoot: " + gConfig.testRoot);
249 var testWin = Services.wm.getMostRecentWindow(winType);
251 setStatus(
"Running...");
253 // It's possible that the test harness window is not yet focused when this
254 // function runs (in which case testWin is already focused, and focusing it
255 // will be a no-op, and then the test harness window will steal focus later,
256 // which will mess up tests). So wait for the test harness window to be
257 // focused before trying to focus testWin.
259 // Focus the test window and start tests.
261 var Tester = new testWin.Tester(links, gDumper.structuredLogger, testsFinished);
267 function executeSoon(callback) {
268 Services.tm.dispatchToMainThread(callback);
271 function waitForFocus(callback, win) {
272 // If
"win" is already focused, just call the callback.
273 if (Services.focus.focusedWindow == win) {
274 executeSoon(callback);
278 // Otherwise focus it, and wait for the focus event.
279 win.addEventListener(
"focus", function listener() {
280 executeSoon(callback);
281 }, { capture: true, once: true});
289 function getHTMLLogFromTests(aTests) {
291 return
"<div id=\"summary\
" class=\"failure\
">No tests to run." +
292 " Did you pass an invalid --test-path?</div>";
296 var passCount = aTests.map(f =
> f.passCount).reduce(sum);
297 var failCount = aTests.map(f =
> f.failCount).reduce(sum);
298 var todoCount = aTests.map(f =
> f.todoCount).reduce(sum);
299 log +=
"<div id=\"summary\
" class=\"";
300 if (failCount != 0) {
303 log += passCount == 0 ? "todo
" : "success
";
305 log += "\
">\n<p>Passed: " + passCount +
"</p>\n" +
306 "<p>Failed: " + failCount;
308 log +=
" <a href=\"javascript:scrollTo('ERROR0')\
">NEXT ERROR</a>";
310 "<p>Todo: " + todoCount +
"</p>\n</div>\n<div id=\"log\
">\n";
312 return log + aTests.map(function (f) {
313 return
"<p class=\"testHeader\
">Running " + f.path +
"...</p>\n" + f.htmlLog;
314 }).join(
"\n") +
"</div>";
317 function testsFinished(aTests) {
318 if (gConfig.closeWhenDone) {
319 const {AppConstants} = ChromeUtils.importESModule(
320 "resource://gre/modules/AppConstants.sys.mjs"
323 !AppConstants.RELEASE_OR_BETA &&
324 !AppConstants.DEBUG &&
325 !AppConstants.MOZ_CODE_COVERAGE &&
326 !AppConstants.ASAN &&
330 Services.profiler.IsActive() &&
331 Services.env.get(
"MOZ_PROFILER_SHUTDOWN");
333 Cu.exitIfInAutomation();
336 .dumpProfileToFileAsync(filename)
337 .then(() =
> Services.profiler.StopProfiler())
338 .then(() =
> Cu.exitIfInAutomation());
340 Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
345 // Focus our window, to display the results
349 // eslint-disable-next-line no-unsanitized/property
350 document.getElementById(
"results").innerHTML = getHTMLLogFromTests(aTests);
354 function scrollTo(id) {
355 var line = document.getElementById(id);
359 line.scrollIntoView();
362 <button id=
"runTestsButton" oncommand=
"runTests();" label=
"Run All Tests"/>
364 <scrollbox flex=
"1" style=
"overflow: auto" align=
"stretch">
365 <div id=
"results" xmlns=
"http://www.w3.org/1999/xhtml" flex=
"1"/>