testing/mochitest/server.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 // Note that the server script itself already defines Cc, Ci, and Cr for us,
michael@0 8 // and because they're constants it's not safe to redefine them. Scope leakage
michael@0 9 // sucks.
michael@0 10
michael@0 11 // Disable automatic network detection, so tests work correctly when
michael@0 12 // not connected to a network.
michael@0 13 let (ios = Cc["@mozilla.org/network/io-service;1"]
michael@0 14 .getService(Ci.nsIIOService2)) {
michael@0 15 ios.manageOfflineStatus = false;
michael@0 16 ios.offline = false;
michael@0 17 }
michael@0 18
michael@0 19 var server; // for use in the shutdown handler, if necessary
michael@0 20
michael@0 21 //
michael@0 22 // HTML GENERATION
michael@0 23 //
michael@0 24 var tags = ['A', 'ABBR', 'ACRONYM', 'ADDRESS', 'APPLET', 'AREA', 'B', 'BASE',
michael@0 25 'BASEFONT', 'BDO', 'BIG', 'BLOCKQUOTE', 'BODY', 'BR', 'BUTTON',
michael@0 26 'CAPTION', 'CENTER', 'CITE', 'CODE', 'COL', 'COLGROUP', 'DD',
michael@0 27 'DEL', 'DFN', 'DIR', 'DIV', 'DL', 'DT', 'EM', 'FIELDSET', 'FONT',
michael@0 28 'FORM', 'FRAME', 'FRAMESET', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6',
michael@0 29 'HEAD', 'HR', 'HTML', 'I', 'IFRAME', 'IMG', 'INPUT', 'INS',
michael@0 30 'ISINDEX', 'KBD', 'LABEL', 'LEGEND', 'LI', 'LINK', 'MAP', 'MENU',
michael@0 31 'META', 'NOFRAMES', 'NOSCRIPT', 'OBJECT', 'OL', 'OPTGROUP',
michael@0 32 'OPTION', 'P', 'PARAM', 'PRE', 'Q', 'S', 'SAMP', 'SCRIPT',
michael@0 33 'SELECT', 'SMALL', 'SPAN', 'STRIKE', 'STRONG', 'STYLE', 'SUB',
michael@0 34 'SUP', 'TABLE', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD',
michael@0 35 'TITLE', 'TR', 'TT', 'U', 'UL', 'VAR'];
michael@0 36
michael@0 37 /**
michael@0 38 * Below, we'll use makeTagFunc to create a function for each of the
michael@0 39 * strings in 'tags'. This will allow us to use s-expression like syntax
michael@0 40 * to create HTML.
michael@0 41 */
michael@0 42 function makeTagFunc(tagName)
michael@0 43 {
michael@0 44 return function (attrs /* rest... */)
michael@0 45 {
michael@0 46 var startChildren = 0;
michael@0 47 var response = "";
michael@0 48
michael@0 49 // write the start tag and attributes
michael@0 50 response += "<" + tagName;
michael@0 51 // if attr is an object, write attributes
michael@0 52 if (attrs && typeof attrs == 'object') {
michael@0 53 startChildren = 1;
michael@0 54 for (var [key,value] in attrs) {
michael@0 55 var val = "" + value;
michael@0 56 response += " " + key + '="' + val.replace('"','&quot;') + '"';
michael@0 57 }
michael@0 58 }
michael@0 59 response += ">";
michael@0 60
michael@0 61 // iterate through the rest of the args
michael@0 62 for (var i = startChildren; i < arguments.length; i++) {
michael@0 63 if (typeof arguments[i] == 'function') {
michael@0 64 response += arguments[i]();
michael@0 65 } else {
michael@0 66 response += arguments[i];
michael@0 67 }
michael@0 68 }
michael@0 69
michael@0 70 // write the close tag
michael@0 71 response += "</" + tagName + ">\n";
michael@0 72 return response;
michael@0 73 }
michael@0 74 }
michael@0 75
michael@0 76 function makeTags() {
michael@0 77 // map our global HTML generation functions
michael@0 78 for each (var tag in tags) {
michael@0 79 this[tag] = makeTagFunc(tag.toLowerCase());
michael@0 80 }
michael@0 81 }
michael@0 82
michael@0 83 var _quitting = false;
michael@0 84
michael@0 85 /** Quit when all activity has completed. */
michael@0 86 function serverStopped()
michael@0 87 {
michael@0 88 _quitting = true;
michael@0 89 }
michael@0 90
michael@0 91 // only run the "main" section if httpd.js was loaded ahead of us
michael@0 92 if (this["nsHttpServer"]) {
michael@0 93 //
michael@0 94 // SCRIPT CODE
michael@0 95 //
michael@0 96 runServer();
michael@0 97
michael@0 98 // We can only have gotten here if the /server/shutdown path was requested.
michael@0 99 if (_quitting)
michael@0 100 {
michael@0 101 dumpn("HTTP server stopped, all pending requests complete");
michael@0 102 quit(0);
michael@0 103 }
michael@0 104
michael@0 105 // Impossible as the stop callback should have been called, but to be safe...
michael@0 106 dumpn("TEST-UNEXPECTED-FAIL | failure to correctly shut down HTTP server");
michael@0 107 quit(1);
michael@0 108 }
michael@0 109
michael@0 110 var serverBasePath;
michael@0 111 var displayResults = true;
michael@0 112
michael@0 113 //
michael@0 114 // SERVER SETUP
michael@0 115 //
michael@0 116 function runServer()
michael@0 117 {
michael@0 118 serverBasePath = __LOCATION__.parent;
michael@0 119 server = createMochitestServer(serverBasePath);
michael@0 120
michael@0 121 //verify server address
michael@0 122 //if a.b.c.d or 'localhost'
michael@0 123 if (typeof(_SERVER_ADDR) != "undefined") {
michael@0 124 if (_SERVER_ADDR == "localhost") {
michael@0 125 gServerAddress = _SERVER_ADDR;
michael@0 126 } else {
michael@0 127 var quads = _SERVER_ADDR.split('.');
michael@0 128 if (quads.length == 4) {
michael@0 129 var invalid = false;
michael@0 130 for (var i=0; i < 4; i++) {
michael@0 131 if (quads[i] < 0 || quads[i] > 255)
michael@0 132 invalid = true;
michael@0 133 }
michael@0 134 if (!invalid)
michael@0 135 gServerAddress = _SERVER_ADDR;
michael@0 136 else
michael@0 137 throw "invalid _SERVER_ADDR, please specify a valid IP Address";
michael@0 138 }
michael@0 139 }
michael@0 140 } else {
michael@0 141 throw "please defined _SERVER_ADDR (as an ip address) before running server.js";
michael@0 142 }
michael@0 143
michael@0 144 if (typeof(_SERVER_PORT) != "undefined") {
michael@0 145 if (parseInt(_SERVER_PORT) > 0 && parseInt(_SERVER_PORT) < 65536)
michael@0 146 SERVER_PORT = _SERVER_PORT;
michael@0 147 } else {
michael@0 148 throw "please define _SERVER_PORT (as a port number) before running server.js";
michael@0 149 }
michael@0 150
michael@0 151 // If DISPLAY_RESULTS is not specified, it defaults to true
michael@0 152 if (typeof(_DISPLAY_RESULTS) != "undefined") {
michael@0 153 displayResults = _DISPLAY_RESULTS;
michael@0 154 }
michael@0 155
michael@0 156 server._start(SERVER_PORT, gServerAddress);
michael@0 157
michael@0 158 // touch a file in the profile directory to indicate we're alive
michael@0 159 var foStream = Cc["@mozilla.org/network/file-output-stream;1"]
michael@0 160 .createInstance(Ci.nsIFileOutputStream);
michael@0 161 var serverAlive = Cc["@mozilla.org/file/local;1"]
michael@0 162 .createInstance(Ci.nsILocalFile);
michael@0 163
michael@0 164 if (typeof(_PROFILE_PATH) == "undefined") {
michael@0 165 serverAlive.initWithFile(serverBasePath);
michael@0 166 serverAlive.append("mochitesttestingprofile");
michael@0 167 } else {
michael@0 168 serverAlive.initWithPath(_PROFILE_PATH);
michael@0 169 }
michael@0 170
michael@0 171 // If we're running outside of the test harness, there might
michael@0 172 // not be a test profile directory present
michael@0 173 if (serverAlive.exists()) {
michael@0 174 serverAlive.append("server_alive.txt");
michael@0 175 foStream.init(serverAlive,
michael@0 176 0x02 | 0x08 | 0x20, 436, 0); // write, create, truncate
michael@0 177 data = "It's alive!";
michael@0 178 foStream.write(data, data.length);
michael@0 179 foStream.close();
michael@0 180 }
michael@0 181
michael@0 182 makeTags();
michael@0 183
michael@0 184 //
michael@0 185 // The following is threading magic to spin an event loop -- this has to
michael@0 186 // happen manually in xpcshell for the server to actually work.
michael@0 187 //
michael@0 188 var thread = Cc["@mozilla.org/thread-manager;1"]
michael@0 189 .getService()
michael@0 190 .currentThread;
michael@0 191 while (!server.isStopped())
michael@0 192 thread.processNextEvent(true);
michael@0 193
michael@0 194 // Server stopped by /server/shutdown handler -- go through pending events
michael@0 195 // and return.
michael@0 196
michael@0 197 // get rid of any pending requests
michael@0 198 while (thread.hasPendingEvents())
michael@0 199 thread.processNextEvent(true);
michael@0 200 }
michael@0 201
michael@0 202 /** Creates and returns an HTTP server configured to serve Mochitests. */
michael@0 203 function createMochitestServer(serverBasePath)
michael@0 204 {
michael@0 205 var server = new nsHttpServer();
michael@0 206
michael@0 207 server.registerDirectory("/", serverBasePath);
michael@0 208 server.registerPathHandler("/server/shutdown", serverShutdown);
michael@0 209 server.registerPathHandler("/server/debug", serverDebug);
michael@0 210 server.registerContentType("sjs", "sjs"); // .sjs == CGI-like functionality
michael@0 211 server.registerContentType("jar", "application/x-jar");
michael@0 212 server.registerContentType("ogg", "application/ogg");
michael@0 213 server.registerContentType("pdf", "application/pdf");
michael@0 214 server.registerContentType("ogv", "video/ogg");
michael@0 215 server.registerContentType("oga", "audio/ogg");
michael@0 216 server.registerContentType("opus", "audio/ogg; codecs=opus");
michael@0 217 server.registerContentType("dat", "text/plain; charset=utf-8");
michael@0 218 server.registerContentType("frag", "text/plain"); // .frag == WebGL fragment shader
michael@0 219 server.registerContentType("vert", "text/plain"); // .vert == WebGL vertex shader
michael@0 220 server.setIndexHandler(defaultDirHandler);
michael@0 221
michael@0 222 var serverRoot =
michael@0 223 {
michael@0 224 getFile: function getFile(path)
michael@0 225 {
michael@0 226 var file = serverBasePath.clone().QueryInterface(Ci.nsILocalFile);
michael@0 227 path.split("/").forEach(function(p) {
michael@0 228 file.appendRelativePath(p);
michael@0 229 });
michael@0 230 return file;
michael@0 231 },
michael@0 232 QueryInterface: function(aIID) { return this; }
michael@0 233 };
michael@0 234
michael@0 235 server.setObjectState("SERVER_ROOT", serverRoot);
michael@0 236
michael@0 237 processLocations(server);
michael@0 238
michael@0 239 return server;
michael@0 240 }
michael@0 241
michael@0 242 /**
michael@0 243 * Notifies the HTTP server about all the locations at which it might receive
michael@0 244 * requests, so that it can properly respond to requests on any of the hosts it
michael@0 245 * serves.
michael@0 246 */
michael@0 247 function processLocations(server)
michael@0 248 {
michael@0 249 var serverLocations = serverBasePath.clone();
michael@0 250 serverLocations.append("server-locations.txt");
michael@0 251
michael@0 252 const PR_RDONLY = 0x01;
michael@0 253 var fis = new FileInputStream(serverLocations, PR_RDONLY, 292 /* 0444 */,
michael@0 254 Ci.nsIFileInputStream.CLOSE_ON_EOF);
michael@0 255
michael@0 256 var lis = new ConverterInputStream(fis, "UTF-8", 1024, 0x0);
michael@0 257 lis.QueryInterface(Ci.nsIUnicharLineInputStream);
michael@0 258
michael@0 259 const LINE_REGEXP =
michael@0 260 new RegExp("^([a-z][-a-z0-9+.]*)" +
michael@0 261 "://" +
michael@0 262 "(" +
michael@0 263 "\\d+\\.\\d+\\.\\d+\\.\\d+" +
michael@0 264 "|" +
michael@0 265 "(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\\.)*" +
michael@0 266 "[a-z](?:[-a-z0-9]*[a-z0-9])?" +
michael@0 267 ")" +
michael@0 268 ":" +
michael@0 269 "(\\d+)" +
michael@0 270 "(?:" +
michael@0 271 "\\s+" +
michael@0 272 "(\\S+(?:,\\S+)*)" +
michael@0 273 ")?$");
michael@0 274
michael@0 275 var line = {};
michael@0 276 var lineno = 0;
michael@0 277 var seenPrimary = false;
michael@0 278 do
michael@0 279 {
michael@0 280 var more = lis.readLine(line);
michael@0 281 lineno++;
michael@0 282
michael@0 283 var lineValue = line.value;
michael@0 284 if (lineValue.charAt(0) == "#" || lineValue == "")
michael@0 285 continue;
michael@0 286
michael@0 287 var match = LINE_REGEXP.exec(lineValue);
michael@0 288 if (!match)
michael@0 289 throw "Syntax error in server-locations.txt, line " + lineno;
michael@0 290
michael@0 291 var [, scheme, host, port, options] = match;
michael@0 292 if (options)
michael@0 293 {
michael@0 294 if (options.split(",").indexOf("primary") >= 0)
michael@0 295 {
michael@0 296 if (seenPrimary)
michael@0 297 {
michael@0 298 throw "Multiple primary locations in server-locations.txt, " +
michael@0 299 "line " + lineno;
michael@0 300 }
michael@0 301
michael@0 302 server.identity.setPrimary(scheme, host, port);
michael@0 303 seenPrimary = true;
michael@0 304 continue;
michael@0 305 }
michael@0 306 }
michael@0 307
michael@0 308 server.identity.add(scheme, host, port);
michael@0 309 }
michael@0 310 while (more);
michael@0 311 }
michael@0 312
michael@0 313
michael@0 314 // PATH HANDLERS
michael@0 315
michael@0 316 // /server/shutdown
michael@0 317 function serverShutdown(metadata, response)
michael@0 318 {
michael@0 319 response.setStatusLine("1.1", 200, "OK");
michael@0 320 response.setHeader("Content-type", "text/plain", false);
michael@0 321
michael@0 322 var body = "Server shut down.";
michael@0 323 response.bodyOutputStream.write(body, body.length);
michael@0 324
michael@0 325 dumpn("Server shutting down now...");
michael@0 326 server.stop(serverStopped);
michael@0 327 }
michael@0 328
michael@0 329 // /server/debug?[012]
michael@0 330 function serverDebug(metadata, response)
michael@0 331 {
michael@0 332 response.setStatusLine(metadata.httpVersion, 400, "Bad debugging level");
michael@0 333 if (metadata.queryString.length !== 1)
michael@0 334 return;
michael@0 335
michael@0 336 var mode;
michael@0 337 if (metadata.queryString === "0") {
michael@0 338 // do this now so it gets logged with the old mode
michael@0 339 dumpn("Server debug logs disabled.");
michael@0 340 DEBUG = false;
michael@0 341 DEBUG_TIMESTAMP = false;
michael@0 342 mode = "disabled";
michael@0 343 } else if (metadata.queryString === "1") {
michael@0 344 DEBUG = true;
michael@0 345 DEBUG_TIMESTAMP = false;
michael@0 346 mode = "enabled";
michael@0 347 } else if (metadata.queryString === "2") {
michael@0 348 DEBUG = true;
michael@0 349 DEBUG_TIMESTAMP = true;
michael@0 350 mode = "enabled, with timestamps";
michael@0 351 } else {
michael@0 352 return;
michael@0 353 }
michael@0 354
michael@0 355 response.setStatusLine(metadata.httpVersion, 200, "OK");
michael@0 356 response.setHeader("Content-type", "text/plain", false);
michael@0 357 var body = "Server debug logs " + mode + ".";
michael@0 358 response.bodyOutputStream.write(body, body.length);
michael@0 359 dumpn(body);
michael@0 360 }
michael@0 361
michael@0 362 //
michael@0 363 // DIRECTORY LISTINGS
michael@0 364 //
michael@0 365
michael@0 366 /**
michael@0 367 * Creates a generator that iterates over the contents of
michael@0 368 * an nsIFile directory.
michael@0 369 */
michael@0 370 function dirIter(dir)
michael@0 371 {
michael@0 372 var en = dir.directoryEntries;
michael@0 373 while (en.hasMoreElements()) {
michael@0 374 var file = en.getNext();
michael@0 375 yield file.QueryInterface(Ci.nsILocalFile);
michael@0 376 }
michael@0 377 }
michael@0 378
michael@0 379 /**
michael@0 380 * Builds an optionally nested object containing links to the
michael@0 381 * files and directories within dir.
michael@0 382 */
michael@0 383 function list(requestPath, directory, recurse)
michael@0 384 {
michael@0 385 var count = 0;
michael@0 386 var path = requestPath;
michael@0 387 if (path.charAt(path.length - 1) != "/") {
michael@0 388 path += "/";
michael@0 389 }
michael@0 390
michael@0 391 var dir = directory.QueryInterface(Ci.nsIFile);
michael@0 392 var links = {};
michael@0 393
michael@0 394 // The SimpleTest directory is hidden
michael@0 395 var files = [file for (file in dirIter(dir))
michael@0 396 if (file.exists() && file.path.indexOf("SimpleTest") == -1)];
michael@0 397
michael@0 398 // Sort files by name, so that tests can be run in a pre-defined order inside
michael@0 399 // a given directory (see bug 384823)
michael@0 400 function leafNameComparator(first, second) {
michael@0 401 if (first.leafName < second.leafName)
michael@0 402 return -1;
michael@0 403 if (first.leafName > second.leafName)
michael@0 404 return 1;
michael@0 405 return 0;
michael@0 406 }
michael@0 407 files.sort(leafNameComparator);
michael@0 408
michael@0 409 count = files.length;
michael@0 410 for each (var file in files) {
michael@0 411 var key = path + file.leafName;
michael@0 412 var childCount = 0;
michael@0 413 if (file.isDirectory()) {
michael@0 414 key += "/";
michael@0 415 }
michael@0 416 if (recurse && file.isDirectory()) {
michael@0 417 [links[key], childCount] = list(key, file, recurse);
michael@0 418 count += childCount;
michael@0 419 } else {
michael@0 420 if (file.leafName.charAt(0) != '.') {
michael@0 421 links[key] = true;
michael@0 422 }
michael@0 423 }
michael@0 424 }
michael@0 425
michael@0 426 return [links, count];
michael@0 427 }
michael@0 428
michael@0 429 /**
michael@0 430 * Heuristic function that determines whether a given path
michael@0 431 * is a test case to be executed in the harness, or just
michael@0 432 * a supporting file.
michael@0 433 */
michael@0 434 function isTest(filename, pattern)
michael@0 435 {
michael@0 436 if (pattern)
michael@0 437 return pattern.test(filename);
michael@0 438
michael@0 439 // File name is a URL style path to a test file, make sure that we check for
michael@0 440 // tests that start with the appropriate prefix.
michael@0 441 var testPrefix = typeof(_TEST_PREFIX) == "string" ? _TEST_PREFIX : "test_";
michael@0 442 var testPattern = new RegExp("^" + testPrefix);
michael@0 443
michael@0 444 pathPieces = filename.split('/');
michael@0 445
michael@0 446 return testPattern.test(pathPieces[pathPieces.length - 1]) &&
michael@0 447 filename.indexOf(".js") == -1 &&
michael@0 448 filename.indexOf(".css") == -1 &&
michael@0 449 !/\^headers\^$/.test(filename);
michael@0 450 }
michael@0 451
michael@0 452 /**
michael@0 453 * Transform nested hashtables of paths to nested HTML lists.
michael@0 454 */
michael@0 455 function linksToListItems(links)
michael@0 456 {
michael@0 457 var response = "";
michael@0 458 var children = "";
michael@0 459 for (var [link, value] in links) {
michael@0 460 var classVal = (!isTest(link) && !(value instanceof Object))
michael@0 461 ? "non-test invisible"
michael@0 462 : "test";
michael@0 463 if (value instanceof Object) {
michael@0 464 children = UL({class: "testdir"}, linksToListItems(value));
michael@0 465 } else {
michael@0 466 children = "";
michael@0 467 }
michael@0 468
michael@0 469 var bug_title = link.match(/test_bug\S+/);
michael@0 470 var bug_num = null;
michael@0 471 if (bug_title != null) {
michael@0 472 bug_num = bug_title[0].match(/\d+/);
michael@0 473 }
michael@0 474
michael@0 475 if ((bug_title == null) || (bug_num == null)) {
michael@0 476 response += LI({class: classVal}, A({href: link}, link), children);
michael@0 477 } else {
michael@0 478 var bug_url = "https://bugzilla.mozilla.org/show_bug.cgi?id="+bug_num;
michael@0 479 response += LI({class: classVal}, A({href: link}, link), " - ", A({href: bug_url}, "Bug "+bug_num), children);
michael@0 480 }
michael@0 481
michael@0 482 }
michael@0 483 return response;
michael@0 484 }
michael@0 485
michael@0 486 /**
michael@0 487 * Transform nested hashtables of paths to a flat table rows.
michael@0 488 */
michael@0 489 function linksToTableRows(links, recursionLevel)
michael@0 490 {
michael@0 491 var response = "";
michael@0 492 for (var [link, value] in links) {
michael@0 493 var classVal = (!isTest(link) && !(value instanceof Object))
michael@0 494 ? "non-test invisible"
michael@0 495 : "";
michael@0 496
michael@0 497 spacer = "padding-left: " + (10 * recursionLevel) + "px";
michael@0 498
michael@0 499 if (value instanceof Object) {
michael@0 500 response += TR({class: "dir", id: "tr-" + link },
michael@0 501 TD({colspan: "3"}, "&#160;"),
michael@0 502 TD({style: spacer},
michael@0 503 A({href: link}, link)));
michael@0 504 response += linksToTableRows(value, recursionLevel + 1);
michael@0 505 } else {
michael@0 506 var bug_title = link.match(/test_bug\S+/);
michael@0 507 var bug_num = null;
michael@0 508 if (bug_title != null) {
michael@0 509 bug_num = bug_title[0].match(/\d+/);
michael@0 510 }
michael@0 511 if ((bug_title == null) || (bug_num == null)) {
michael@0 512 response += TR({class: classVal, id: "tr-" + link },
michael@0 513 TD("0"),
michael@0 514 TD("0"),
michael@0 515 TD("0"),
michael@0 516 TD({style: spacer},
michael@0 517 A({href: link}, link)));
michael@0 518 } else {
michael@0 519 var bug_url = "https://bugzilla.mozilla.org/show_bug.cgi?id=" + bug_num;
michael@0 520 response += TR({class: classVal, id: "tr-" + link },
michael@0 521 TD("0"),
michael@0 522 TD("0"),
michael@0 523 TD("0"),
michael@0 524 TD({style: spacer},
michael@0 525 A({href: link}, link), " - ",
michael@0 526 A({href: bug_url}, "Bug " + bug_num)));
michael@0 527 }
michael@0 528 }
michael@0 529 }
michael@0 530 return response;
michael@0 531 }
michael@0 532
michael@0 533 function arrayOfTestFiles(linkArray, fileArray, testPattern) {
michael@0 534 for (var [link, value] in Iterator(linkArray)) {
michael@0 535 if (value instanceof Object) {
michael@0 536 arrayOfTestFiles(value, fileArray, testPattern);
michael@0 537 } else if (isTest(link, testPattern)) {
michael@0 538 fileArray.push(link)
michael@0 539 }
michael@0 540 }
michael@0 541 }
michael@0 542 /**
michael@0 543 * Produce a flat array of test file paths to be executed in the harness.
michael@0 544 */
michael@0 545 function jsonArrayOfTestFiles(links)
michael@0 546 {
michael@0 547 var testFiles = [];
michael@0 548 arrayOfTestFiles(links, testFiles);
michael@0 549 testFiles = ['"' + file + '"' for each(file in testFiles)];
michael@0 550 return "[" + testFiles.join(",\n") + "]";
michael@0 551 }
michael@0 552
michael@0 553 /**
michael@0 554 * Produce a normal directory listing.
michael@0 555 */
michael@0 556 function regularListing(metadata, response)
michael@0 557 {
michael@0 558 var [links, count] = list(metadata.path,
michael@0 559 metadata.getProperty("directory"),
michael@0 560 false);
michael@0 561 response.write(
michael@0 562 HTML(
michael@0 563 HEAD(
michael@0 564 TITLE("mochitest index ", metadata.path)
michael@0 565 ),
michael@0 566 BODY(
michael@0 567 BR(),
michael@0 568 A({href: ".."}, "Up a level"),
michael@0 569 UL(linksToListItems(links))
michael@0 570 )
michael@0 571 )
michael@0 572 );
michael@0 573 }
michael@0 574
michael@0 575 /**
michael@0 576 * Produce a test harness page containing all the test cases
michael@0 577 * below it, recursively.
michael@0 578 */
michael@0 579 function testListing(metadata, response)
michael@0 580 {
michael@0 581 var links = {};
michael@0 582 var count = 0;
michael@0 583 if (metadata.queryString.indexOf('manifestFile') == -1) {
michael@0 584 [links, count] = list(metadata.path,
michael@0 585 metadata.getProperty("directory"),
michael@0 586 true);
michael@0 587 }
michael@0 588 var table_class = metadata.queryString.indexOf("hideResultsTable=1") > -1 ? "invisible": "";
michael@0 589
michael@0 590 let testname = (metadata.queryString.indexOf("testname=") > -1)
michael@0 591 ? metadata.queryString.match(/testname=([^&]+)/)[1]
michael@0 592 : "";
michael@0 593
michael@0 594 dumpn("count: " + count);
michael@0 595 var tests = testname
michael@0 596 ? "['/" + testname + "']"
michael@0 597 : jsonArrayOfTestFiles(links);
michael@0 598 response.write(
michael@0 599 HTML(
michael@0 600 HEAD(
michael@0 601 TITLE("MochiTest | ", metadata.path),
michael@0 602 LINK({rel: "stylesheet",
michael@0 603 type: "text/css", href: "/static/harness.css"}
michael@0 604 ),
michael@0 605 SCRIPT({type: "text/javascript",
michael@0 606 src: "/tests/SimpleTest/LogController.js"}),
michael@0 607 SCRIPT({type: "text/javascript",
michael@0 608 src: "/tests/SimpleTest/MemoryStats.js"}),
michael@0 609 SCRIPT({type: "text/javascript",
michael@0 610 src: "/tests/SimpleTest/TestRunner.js"}),
michael@0 611 SCRIPT({type: "text/javascript",
michael@0 612 src: "/tests/SimpleTest/MozillaLogger.js"}),
michael@0 613 SCRIPT({type: "text/javascript",
michael@0 614 src: "/chunkifyTests.js"}),
michael@0 615 SCRIPT({type: "text/javascript",
michael@0 616 src: "/manifestLibrary.js"}),
michael@0 617 SCRIPT({type: "text/javascript",
michael@0 618 src: "/tests/SimpleTest/setup.js"}),
michael@0 619 SCRIPT({type: "text/javascript"},
michael@0 620 "window.onload = hookup; gTestList=" + tests + ";"
michael@0 621 )
michael@0 622 ),
michael@0 623 BODY(
michael@0 624 DIV({class: "container"},
michael@0 625 H2("--> ", A({href: "#", id: "runtests"}, "Run Tests"), " <--"),
michael@0 626 P({style: "float: right;"},
michael@0 627 SMALL(
michael@0 628 "Based on the ",
michael@0 629 A({href:"http://www.mochikit.com/"}, "MochiKit"),
michael@0 630 " unit tests."
michael@0 631 )
michael@0 632 ),
michael@0 633 DIV({class: "status"},
michael@0 634 H1({id: "indicator"}, "Status"),
michael@0 635 H2({id: "pass"}, "Passed: ", SPAN({id: "pass-count"},"0")),
michael@0 636 H2({id: "fail"}, "Failed: ", SPAN({id: "fail-count"},"0")),
michael@0 637 H2({id: "fail"}, "Todo: ", SPAN({id: "todo-count"},"0"))
michael@0 638 ),
michael@0 639 DIV({class: "clear"}),
michael@0 640 DIV({id: "current-test"},
michael@0 641 B("Currently Executing: ",
michael@0 642 SPAN({id: "current-test-path"}, "_")
michael@0 643 )
michael@0 644 ),
michael@0 645 DIV({class: "clear"}),
michael@0 646 DIV({class: "frameholder"},
michael@0 647 IFRAME({scrolling: "no", id: "testframe", width: "500", height: "300"})
michael@0 648 ),
michael@0 649 DIV({class: "clear"}),
michael@0 650 DIV({class: "toggle"},
michael@0 651 A({href: "#", id: "toggleNonTests"}, "Show Non-Tests"),
michael@0 652 BR()
michael@0 653 ),
michael@0 654
michael@0 655 (
michael@0 656 displayResults ?
michael@0 657 TABLE({cellpadding: 0, cellspacing: 0, class: table_class, id: "test-table"},
michael@0 658 TR(TD("Passed"), TD("Failed"), TD("Todo"), TD("Test Files")),
michael@0 659 linksToTableRows(links, 0)
michael@0 660 ) : ""
michael@0 661 ),
michael@0 662
michael@0 663 BR(),
michael@0 664 TABLE({cellpadding: 0, cellspacing: 0, border: 1, bordercolor: "red", id: "fail-table"}
michael@0 665 ),
michael@0 666
michael@0 667 DIV({class: "clear"})
michael@0 668 )
michael@0 669 )
michael@0 670 )
michael@0 671 );
michael@0 672 }
michael@0 673
michael@0 674 /**
michael@0 675 * Respond to requests that match a file system directory.
michael@0 676 * Under the tests/ directory, return a test harness page.
michael@0 677 */
michael@0 678 function defaultDirHandler(metadata, response)
michael@0 679 {
michael@0 680 response.setStatusLine("1.1", 200, "OK");
michael@0 681 response.setHeader("Content-type", "text/html;charset=utf-8", false);
michael@0 682 try {
michael@0 683 if (metadata.path.indexOf("/tests") != 0) {
michael@0 684 regularListing(metadata, response);
michael@0 685 } else {
michael@0 686 testListing(metadata, response);
michael@0 687 }
michael@0 688 } catch (ex) {
michael@0 689 response.write(ex);
michael@0 690 }
michael@0 691 }

mercurial