browser-harness.xul 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <?xml version="1.0"?>
  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/. -->
  6. <window id="browserTestHarness"
  7. xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  8. onload="TestStart();"
  9. title="Browser chrome tests"
  10. width="1024">
  11. <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/MozillaLogger.js"/>
  12. <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/LogController.js"/>
  13. <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/StructuredLog.jsm"/>
  14. <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/TestRunner.js"/>
  15. <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"/>
  16. <script type="application/javascript" src="chrome://mochikit/content/manifestLibrary.js" />
  17. <script type="application/javascript" src="chrome://mochikit/content/chunkifyTests.js"/>
  18. <style xmlns="http://www.w3.org/1999/xhtml"><![CDATA[
  19. #results {
  20. margin: 5px;
  21. background-color: window;
  22. -moz-user-select: text;
  23. }
  24. #summary {
  25. color: white;
  26. border: 2px solid black;
  27. }
  28. #summary.success {
  29. background-color: #0d0;
  30. }
  31. #summary.failure {
  32. background-color: red;
  33. }
  34. #summary.todo {
  35. background-color: orange;
  36. }
  37. .info {
  38. color: grey;
  39. }
  40. .failed {
  41. color: red;
  42. font-weight: bold;
  43. }
  44. .testHeader {
  45. margin-top: 1em;
  46. }
  47. p {
  48. margin: 0.1em;
  49. }
  50. a {
  51. color: blue;
  52. text-decoration: underline;
  53. }
  54. ]]></style>
  55. <script type="application/javascript;version=1.7"><![CDATA[
  56. if (Cc === undefined) {
  57. var Cc = Components.classes;
  58. var Ci = Components.interfaces;
  59. }
  60. var gConfig;
  61. var gDumper = {
  62. get fileLogger() {
  63. let logger = null;
  64. if (gConfig.logFile) {
  65. try {
  66. logger = new MozillaFileLogger(gConfig.logFile)
  67. } catch (ex) {
  68. dump("TEST-UNEXPECTED-FAIL | (browser-harness.xul) | " +
  69. "Error trying to log to " + gConfig.logFile + ": " + ex + "\n");
  70. }
  71. }
  72. delete this.fileLogger;
  73. return this.fileLogger = logger;
  74. },
  75. structuredLogger: TestRunner.structuredLogger,
  76. dump: function (str) {
  77. this.structuredLogger.info(str);
  78. if (this.fileLogger)
  79. this.fileLogger.log(str);
  80. },
  81. done: function () {
  82. if (this.fileLogger)
  83. this.fileLogger.close();
  84. }
  85. }
  86. function TestStart() {
  87. gConfig = readConfig();
  88. // Update the title for --start-at and --end-at.
  89. if (gConfig.startAt || gConfig.endAt)
  90. document.getElementById("runTestsButton").label =
  91. "Run subset of tests";
  92. if (gConfig.autorun)
  93. setTimeout(runTests, 0);
  94. }
  95. var gErrorCount = 0;
  96. function browserTest(aTestFile) {
  97. this.path = aTestFile['url'];
  98. this.expected = aTestFile['expected'];
  99. this.dumper = gDumper;
  100. this.results = [];
  101. this.scope = null;
  102. this.duration = 0;
  103. this.unexpectedTimeouts = 0;
  104. this.lastOutputTime = 0;
  105. }
  106. browserTest.prototype = {
  107. get passCount() {
  108. return this.results.filter(t => !t.info && !t.todo && t.pass).length;
  109. },
  110. get todoCount() {
  111. return this.results.filter(t => !t.info && t.todo && t.pass).length;
  112. },
  113. get failCount() {
  114. return this.results.filter(t => !t.info && !t.pass).length;
  115. },
  116. addResult: function addResult(result) {
  117. this.lastOutputTime = Date.now();
  118. this.results.push(result);
  119. if (result.info) {
  120. if (result.msg) {
  121. this.dumper.structuredLogger.info(result.msg);
  122. }
  123. return;
  124. }
  125. this.dumper.structuredLogger.testStatus(this.path,
  126. result.name,
  127. result.status,
  128. result.expected,
  129. result.msg);
  130. },
  131. setDuration: function setDuration(duration) {
  132. this.duration = duration;
  133. },
  134. get htmlLog() {
  135. let txtToHTML = Cc["@mozilla.org/txttohtmlconv;1"].
  136. getService(Ci.mozITXTToHTMLConv);
  137. function _entityEncode(str) {
  138. return txtToHTML.scanTXT(str, Ci.mozITXTToHTMLConv.kEntities);
  139. }
  140. var path = _entityEncode(this.path);
  141. var html = this.results.map(function (t) {
  142. var classname = "result ";
  143. var result = "TEST-";
  144. if (t.info) {
  145. classname = "info";
  146. result += "INFO";
  147. }
  148. else if (t.pass) {
  149. classname += "passed";
  150. if (t.todo)
  151. result += "KNOWN-FAIL";
  152. else
  153. result += "PASS";
  154. }
  155. else {
  156. classname += "failed";
  157. result += "UNEXPECTED-" + t.status;
  158. }
  159. var message = t.name + (t.msg ? " - " + t.msg : "");
  160. var text = result + " | " + path + " | " + _entityEncode(message);
  161. if (!t.info && !t.pass) {
  162. return '<p class="' + classname + '" id=\"ERROR' + (gErrorCount++) + '">' +
  163. text + " <a href=\"javascript:scrollTo('ERROR" + gErrorCount + "')\">NEXT ERROR</a></p>";
  164. }
  165. return '<p class="' + classname + '">' + text + "</p>";
  166. }).join("\n");
  167. if (this.duration) {
  168. html += "<p class=\"info\">TEST-END | " + path + " | finished in " +
  169. this.duration + " ms</p>";
  170. }
  171. return html;
  172. }
  173. };
  174. // Returns an array of browserTest objects for all the selected tests
  175. function runTests() {
  176. gConfig.baseurl = "chrome://mochitests/content";
  177. getTestList(gConfig, loadTestList);
  178. }
  179. function loadTestList(links) {
  180. if (!links) {
  181. createTester({});
  182. return;
  183. }
  184. // load server.js in so we can share template functions
  185. var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
  186. getService(Ci.mozIJSSubScriptLoader);
  187. var srvScope = {};
  188. scriptLoader.loadSubScript('chrome://mochikit/content/server.js',
  189. srvScope);
  190. var fileNames = [];
  191. var fileNameRegexp = /browser_.+\.js$/;
  192. srvScope.arrayOfTestFiles(links, fileNames, fileNameRegexp);
  193. if (gConfig.startAt || gConfig.endAt) {
  194. fileNames = skipTests(fileNames, gConfig.startAt, gConfig.endAt);
  195. }
  196. createTester(fileNames.map(function (f) { return new browserTest(f); }));
  197. }
  198. function setStatus(aStatusString) {
  199. document.getElementById("status").value = aStatusString;
  200. }
  201. function createTester(links) {
  202. var windowMediator = Cc['@mozilla.org/appshell/window-mediator;1'].
  203. getService(Ci.nsIWindowMediator);
  204. var winType = gConfig.testRoot == "browser" ? "navigator:browser" : null;
  205. if (!winType) {
  206. throw new Error("Unrecognized gConfig.testRoot: " + gConfig.testRoot);
  207. }
  208. var testWin = windowMediator.getMostRecentWindow(winType);
  209. setStatus("Running...");
  210. // It's possible that the test harness window is not yet focused when this
  211. // function runs (in which case testWin is already focused, and focusing it
  212. // will be a no-op, and then the test harness window will steal focus later,
  213. // which will mess up tests). So wait for the test harness window to be
  214. // focused before trying to focus testWin.
  215. waitForFocus(() => {
  216. // Focus the test window and start tests.
  217. waitForFocus(() => {
  218. var Tester = new testWin.Tester(links, gDumper.structuredLogger, testsFinished);
  219. Tester.start();
  220. }, testWin);
  221. }, window);
  222. }
  223. function executeSoon(callback) {
  224. let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
  225. tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
  226. }
  227. function waitForFocus(callback, win) {
  228. // If "win" is already focused, just call the callback.
  229. let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
  230. if (fm.focusedWindow == win) {
  231. executeSoon(callback);
  232. return;
  233. }
  234. // Otherwise focus it, and wait for the focus event.
  235. win.addEventListener("focus", function listener() {
  236. win.removeEventListener("focus", listener, true);
  237. executeSoon(callback);
  238. }, true);
  239. win.focus();
  240. }
  241. function sum(a, b) {
  242. return a + b;
  243. }
  244. function getHTMLLogFromTests(aTests) {
  245. if (!aTests.length)
  246. return "<div id=\"summary\" class=\"failure\">No tests to run." +
  247. " Did you pass an invalid --test-path?</div>";
  248. var log = "";
  249. var passCount = aTests.map(f => f.passCount).reduce(sum);
  250. var failCount = aTests.map(f => f.failCount).reduce(sum);
  251. var todoCount = aTests.map(f => f.todoCount).reduce(sum);
  252. log += "<div id=\"summary\" class=\"";
  253. log += failCount != 0 ? "failure" :
  254. passCount == 0 ? "todo" : "success";
  255. log += "\">\n<p>Passed: " + passCount + "</p>\n" +
  256. "<p>Failed: " + failCount;
  257. if (failCount > 0)
  258. log += " <a href=\"javascript:scrollTo('ERROR0')\">NEXT ERROR</a>";
  259. log += "</p>\n" +
  260. "<p>Todo: " + todoCount + "</p>\n</div>\n<div id=\"log\">\n";
  261. return log + aTests.map(function (f) {
  262. return "<p class=\"testHeader\">Running " + f.path + "...</p>\n" + f.htmlLog;
  263. }).join("\n") + "</div>";
  264. }
  265. function testsFinished(aTests) {
  266. // Focus our window, to display the results
  267. window.focus();
  268. if (gConfig.closeWhenDone) {
  269. let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
  270. appStartup.quit(Ci.nsIAppStartup.eForceQuit);
  271. return;
  272. }
  273. // UI
  274. document.getElementById("results").innerHTML = getHTMLLogFromTests(aTests);
  275. setStatus("Done.");
  276. }
  277. function scrollTo(id) {
  278. var line = document.getElementById(id);
  279. if (!line)
  280. return;
  281. var boxObject = document.getElementById("results").parentNode.boxObject;
  282. boxObject.scrollToElement(line);
  283. }
  284. ]]></script>
  285. <button id="runTestsButton" oncommand="runTests();" label="Run All Tests"/>
  286. <label id="status"/>
  287. <scrollbox flex="1" style="overflow: auto" align="stretch">
  288. <div id="results" xmlns="http://www.w3.org/1999/xhtml" flex="1"/>
  289. </scrollbox>
  290. </window>