1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mochitest/server.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,691 @@ 1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// Note that the server script itself already defines Cc, Ci, and Cr for us, 1.11 +// and because they're constants it's not safe to redefine them. Scope leakage 1.12 +// sucks. 1.13 + 1.14 +// Disable automatic network detection, so tests work correctly when 1.15 +// not connected to a network. 1.16 +let (ios = Cc["@mozilla.org/network/io-service;1"] 1.17 + .getService(Ci.nsIIOService2)) { 1.18 + ios.manageOfflineStatus = false; 1.19 + ios.offline = false; 1.20 +} 1.21 + 1.22 +var server; // for use in the shutdown handler, if necessary 1.23 + 1.24 +// 1.25 +// HTML GENERATION 1.26 +// 1.27 +var tags = ['A', 'ABBR', 'ACRONYM', 'ADDRESS', 'APPLET', 'AREA', 'B', 'BASE', 1.28 + 'BASEFONT', 'BDO', 'BIG', 'BLOCKQUOTE', 'BODY', 'BR', 'BUTTON', 1.29 + 'CAPTION', 'CENTER', 'CITE', 'CODE', 'COL', 'COLGROUP', 'DD', 1.30 + 'DEL', 'DFN', 'DIR', 'DIV', 'DL', 'DT', 'EM', 'FIELDSET', 'FONT', 1.31 + 'FORM', 'FRAME', 'FRAMESET', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 1.32 + 'HEAD', 'HR', 'HTML', 'I', 'IFRAME', 'IMG', 'INPUT', 'INS', 1.33 + 'ISINDEX', 'KBD', 'LABEL', 'LEGEND', 'LI', 'LINK', 'MAP', 'MENU', 1.34 + 'META', 'NOFRAMES', 'NOSCRIPT', 'OBJECT', 'OL', 'OPTGROUP', 1.35 + 'OPTION', 'P', 'PARAM', 'PRE', 'Q', 'S', 'SAMP', 'SCRIPT', 1.36 + 'SELECT', 'SMALL', 'SPAN', 'STRIKE', 'STRONG', 'STYLE', 'SUB', 1.37 + 'SUP', 'TABLE', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 1.38 + 'TITLE', 'TR', 'TT', 'U', 'UL', 'VAR']; 1.39 + 1.40 +/** 1.41 + * Below, we'll use makeTagFunc to create a function for each of the 1.42 + * strings in 'tags'. This will allow us to use s-expression like syntax 1.43 + * to create HTML. 1.44 + */ 1.45 +function makeTagFunc(tagName) 1.46 +{ 1.47 + return function (attrs /* rest... */) 1.48 + { 1.49 + var startChildren = 0; 1.50 + var response = ""; 1.51 + 1.52 + // write the start tag and attributes 1.53 + response += "<" + tagName; 1.54 + // if attr is an object, write attributes 1.55 + if (attrs && typeof attrs == 'object') { 1.56 + startChildren = 1; 1.57 + for (var [key,value] in attrs) { 1.58 + var val = "" + value; 1.59 + response += " " + key + '="' + val.replace('"','"') + '"'; 1.60 + } 1.61 + } 1.62 + response += ">"; 1.63 + 1.64 + // iterate through the rest of the args 1.65 + for (var i = startChildren; i < arguments.length; i++) { 1.66 + if (typeof arguments[i] == 'function') { 1.67 + response += arguments[i](); 1.68 + } else { 1.69 + response += arguments[i]; 1.70 + } 1.71 + } 1.72 + 1.73 + // write the close tag 1.74 + response += "</" + tagName + ">\n"; 1.75 + return response; 1.76 + } 1.77 +} 1.78 + 1.79 +function makeTags() { 1.80 + // map our global HTML generation functions 1.81 + for each (var tag in tags) { 1.82 + this[tag] = makeTagFunc(tag.toLowerCase()); 1.83 + } 1.84 +} 1.85 + 1.86 +var _quitting = false; 1.87 + 1.88 +/** Quit when all activity has completed. */ 1.89 +function serverStopped() 1.90 +{ 1.91 + _quitting = true; 1.92 +} 1.93 + 1.94 +// only run the "main" section if httpd.js was loaded ahead of us 1.95 +if (this["nsHttpServer"]) { 1.96 + // 1.97 + // SCRIPT CODE 1.98 + // 1.99 + runServer(); 1.100 + 1.101 + // We can only have gotten here if the /server/shutdown path was requested. 1.102 + if (_quitting) 1.103 + { 1.104 + dumpn("HTTP server stopped, all pending requests complete"); 1.105 + quit(0); 1.106 + } 1.107 + 1.108 + // Impossible as the stop callback should have been called, but to be safe... 1.109 + dumpn("TEST-UNEXPECTED-FAIL | failure to correctly shut down HTTP server"); 1.110 + quit(1); 1.111 +} 1.112 + 1.113 +var serverBasePath; 1.114 +var displayResults = true; 1.115 + 1.116 +// 1.117 +// SERVER SETUP 1.118 +// 1.119 +function runServer() 1.120 +{ 1.121 + serverBasePath = __LOCATION__.parent; 1.122 + server = createMochitestServer(serverBasePath); 1.123 + 1.124 + //verify server address 1.125 + //if a.b.c.d or 'localhost' 1.126 + if (typeof(_SERVER_ADDR) != "undefined") { 1.127 + if (_SERVER_ADDR == "localhost") { 1.128 + gServerAddress = _SERVER_ADDR; 1.129 + } else { 1.130 + var quads = _SERVER_ADDR.split('.'); 1.131 + if (quads.length == 4) { 1.132 + var invalid = false; 1.133 + for (var i=0; i < 4; i++) { 1.134 + if (quads[i] < 0 || quads[i] > 255) 1.135 + invalid = true; 1.136 + } 1.137 + if (!invalid) 1.138 + gServerAddress = _SERVER_ADDR; 1.139 + else 1.140 + throw "invalid _SERVER_ADDR, please specify a valid IP Address"; 1.141 + } 1.142 + } 1.143 + } else { 1.144 + throw "please defined _SERVER_ADDR (as an ip address) before running server.js"; 1.145 + } 1.146 + 1.147 + if (typeof(_SERVER_PORT) != "undefined") { 1.148 + if (parseInt(_SERVER_PORT) > 0 && parseInt(_SERVER_PORT) < 65536) 1.149 + SERVER_PORT = _SERVER_PORT; 1.150 + } else { 1.151 + throw "please define _SERVER_PORT (as a port number) before running server.js"; 1.152 + } 1.153 + 1.154 + // If DISPLAY_RESULTS is not specified, it defaults to true 1.155 + if (typeof(_DISPLAY_RESULTS) != "undefined") { 1.156 + displayResults = _DISPLAY_RESULTS; 1.157 + } 1.158 + 1.159 + server._start(SERVER_PORT, gServerAddress); 1.160 + 1.161 + // touch a file in the profile directory to indicate we're alive 1.162 + var foStream = Cc["@mozilla.org/network/file-output-stream;1"] 1.163 + .createInstance(Ci.nsIFileOutputStream); 1.164 + var serverAlive = Cc["@mozilla.org/file/local;1"] 1.165 + .createInstance(Ci.nsILocalFile); 1.166 + 1.167 + if (typeof(_PROFILE_PATH) == "undefined") { 1.168 + serverAlive.initWithFile(serverBasePath); 1.169 + serverAlive.append("mochitesttestingprofile"); 1.170 + } else { 1.171 + serverAlive.initWithPath(_PROFILE_PATH); 1.172 + } 1.173 + 1.174 + // If we're running outside of the test harness, there might 1.175 + // not be a test profile directory present 1.176 + if (serverAlive.exists()) { 1.177 + serverAlive.append("server_alive.txt"); 1.178 + foStream.init(serverAlive, 1.179 + 0x02 | 0x08 | 0x20, 436, 0); // write, create, truncate 1.180 + data = "It's alive!"; 1.181 + foStream.write(data, data.length); 1.182 + foStream.close(); 1.183 + } 1.184 + 1.185 + makeTags(); 1.186 + 1.187 + // 1.188 + // The following is threading magic to spin an event loop -- this has to 1.189 + // happen manually in xpcshell for the server to actually work. 1.190 + // 1.191 + var thread = Cc["@mozilla.org/thread-manager;1"] 1.192 + .getService() 1.193 + .currentThread; 1.194 + while (!server.isStopped()) 1.195 + thread.processNextEvent(true); 1.196 + 1.197 + // Server stopped by /server/shutdown handler -- go through pending events 1.198 + // and return. 1.199 + 1.200 + // get rid of any pending requests 1.201 + while (thread.hasPendingEvents()) 1.202 + thread.processNextEvent(true); 1.203 +} 1.204 + 1.205 +/** Creates and returns an HTTP server configured to serve Mochitests. */ 1.206 +function createMochitestServer(serverBasePath) 1.207 +{ 1.208 + var server = new nsHttpServer(); 1.209 + 1.210 + server.registerDirectory("/", serverBasePath); 1.211 + server.registerPathHandler("/server/shutdown", serverShutdown); 1.212 + server.registerPathHandler("/server/debug", serverDebug); 1.213 + server.registerContentType("sjs", "sjs"); // .sjs == CGI-like functionality 1.214 + server.registerContentType("jar", "application/x-jar"); 1.215 + server.registerContentType("ogg", "application/ogg"); 1.216 + server.registerContentType("pdf", "application/pdf"); 1.217 + server.registerContentType("ogv", "video/ogg"); 1.218 + server.registerContentType("oga", "audio/ogg"); 1.219 + server.registerContentType("opus", "audio/ogg; codecs=opus"); 1.220 + server.registerContentType("dat", "text/plain; charset=utf-8"); 1.221 + server.registerContentType("frag", "text/plain"); // .frag == WebGL fragment shader 1.222 + server.registerContentType("vert", "text/plain"); // .vert == WebGL vertex shader 1.223 + server.setIndexHandler(defaultDirHandler); 1.224 + 1.225 + var serverRoot = 1.226 + { 1.227 + getFile: function getFile(path) 1.228 + { 1.229 + var file = serverBasePath.clone().QueryInterface(Ci.nsILocalFile); 1.230 + path.split("/").forEach(function(p) { 1.231 + file.appendRelativePath(p); 1.232 + }); 1.233 + return file; 1.234 + }, 1.235 + QueryInterface: function(aIID) { return this; } 1.236 + }; 1.237 + 1.238 + server.setObjectState("SERVER_ROOT", serverRoot); 1.239 + 1.240 + processLocations(server); 1.241 + 1.242 + return server; 1.243 +} 1.244 + 1.245 +/** 1.246 + * Notifies the HTTP server about all the locations at which it might receive 1.247 + * requests, so that it can properly respond to requests on any of the hosts it 1.248 + * serves. 1.249 + */ 1.250 +function processLocations(server) 1.251 +{ 1.252 + var serverLocations = serverBasePath.clone(); 1.253 + serverLocations.append("server-locations.txt"); 1.254 + 1.255 + const PR_RDONLY = 0x01; 1.256 + var fis = new FileInputStream(serverLocations, PR_RDONLY, 292 /* 0444 */, 1.257 + Ci.nsIFileInputStream.CLOSE_ON_EOF); 1.258 + 1.259 + var lis = new ConverterInputStream(fis, "UTF-8", 1024, 0x0); 1.260 + lis.QueryInterface(Ci.nsIUnicharLineInputStream); 1.261 + 1.262 + const LINE_REGEXP = 1.263 + new RegExp("^([a-z][-a-z0-9+.]*)" + 1.264 + "://" + 1.265 + "(" + 1.266 + "\\d+\\.\\d+\\.\\d+\\.\\d+" + 1.267 + "|" + 1.268 + "(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\\.)*" + 1.269 + "[a-z](?:[-a-z0-9]*[a-z0-9])?" + 1.270 + ")" + 1.271 + ":" + 1.272 + "(\\d+)" + 1.273 + "(?:" + 1.274 + "\\s+" + 1.275 + "(\\S+(?:,\\S+)*)" + 1.276 + ")?$"); 1.277 + 1.278 + var line = {}; 1.279 + var lineno = 0; 1.280 + var seenPrimary = false; 1.281 + do 1.282 + { 1.283 + var more = lis.readLine(line); 1.284 + lineno++; 1.285 + 1.286 + var lineValue = line.value; 1.287 + if (lineValue.charAt(0) == "#" || lineValue == "") 1.288 + continue; 1.289 + 1.290 + var match = LINE_REGEXP.exec(lineValue); 1.291 + if (!match) 1.292 + throw "Syntax error in server-locations.txt, line " + lineno; 1.293 + 1.294 + var [, scheme, host, port, options] = match; 1.295 + if (options) 1.296 + { 1.297 + if (options.split(",").indexOf("primary") >= 0) 1.298 + { 1.299 + if (seenPrimary) 1.300 + { 1.301 + throw "Multiple primary locations in server-locations.txt, " + 1.302 + "line " + lineno; 1.303 + } 1.304 + 1.305 + server.identity.setPrimary(scheme, host, port); 1.306 + seenPrimary = true; 1.307 + continue; 1.308 + } 1.309 + } 1.310 + 1.311 + server.identity.add(scheme, host, port); 1.312 + } 1.313 + while (more); 1.314 +} 1.315 + 1.316 + 1.317 +// PATH HANDLERS 1.318 + 1.319 +// /server/shutdown 1.320 +function serverShutdown(metadata, response) 1.321 +{ 1.322 + response.setStatusLine("1.1", 200, "OK"); 1.323 + response.setHeader("Content-type", "text/plain", false); 1.324 + 1.325 + var body = "Server shut down."; 1.326 + response.bodyOutputStream.write(body, body.length); 1.327 + 1.328 + dumpn("Server shutting down now..."); 1.329 + server.stop(serverStopped); 1.330 +} 1.331 + 1.332 +// /server/debug?[012] 1.333 +function serverDebug(metadata, response) 1.334 +{ 1.335 + response.setStatusLine(metadata.httpVersion, 400, "Bad debugging level"); 1.336 + if (metadata.queryString.length !== 1) 1.337 + return; 1.338 + 1.339 + var mode; 1.340 + if (metadata.queryString === "0") { 1.341 + // do this now so it gets logged with the old mode 1.342 + dumpn("Server debug logs disabled."); 1.343 + DEBUG = false; 1.344 + DEBUG_TIMESTAMP = false; 1.345 + mode = "disabled"; 1.346 + } else if (metadata.queryString === "1") { 1.347 + DEBUG = true; 1.348 + DEBUG_TIMESTAMP = false; 1.349 + mode = "enabled"; 1.350 + } else if (metadata.queryString === "2") { 1.351 + DEBUG = true; 1.352 + DEBUG_TIMESTAMP = true; 1.353 + mode = "enabled, with timestamps"; 1.354 + } else { 1.355 + return; 1.356 + } 1.357 + 1.358 + response.setStatusLine(metadata.httpVersion, 200, "OK"); 1.359 + response.setHeader("Content-type", "text/plain", false); 1.360 + var body = "Server debug logs " + mode + "."; 1.361 + response.bodyOutputStream.write(body, body.length); 1.362 + dumpn(body); 1.363 +} 1.364 + 1.365 +// 1.366 +// DIRECTORY LISTINGS 1.367 +// 1.368 + 1.369 +/** 1.370 + * Creates a generator that iterates over the contents of 1.371 + * an nsIFile directory. 1.372 + */ 1.373 +function dirIter(dir) 1.374 +{ 1.375 + var en = dir.directoryEntries; 1.376 + while (en.hasMoreElements()) { 1.377 + var file = en.getNext(); 1.378 + yield file.QueryInterface(Ci.nsILocalFile); 1.379 + } 1.380 +} 1.381 + 1.382 +/** 1.383 + * Builds an optionally nested object containing links to the 1.384 + * files and directories within dir. 1.385 + */ 1.386 +function list(requestPath, directory, recurse) 1.387 +{ 1.388 + var count = 0; 1.389 + var path = requestPath; 1.390 + if (path.charAt(path.length - 1) != "/") { 1.391 + path += "/"; 1.392 + } 1.393 + 1.394 + var dir = directory.QueryInterface(Ci.nsIFile); 1.395 + var links = {}; 1.396 + 1.397 + // The SimpleTest directory is hidden 1.398 + var files = [file for (file in dirIter(dir)) 1.399 + if (file.exists() && file.path.indexOf("SimpleTest") == -1)]; 1.400 + 1.401 + // Sort files by name, so that tests can be run in a pre-defined order inside 1.402 + // a given directory (see bug 384823) 1.403 + function leafNameComparator(first, second) { 1.404 + if (first.leafName < second.leafName) 1.405 + return -1; 1.406 + if (first.leafName > second.leafName) 1.407 + return 1; 1.408 + return 0; 1.409 + } 1.410 + files.sort(leafNameComparator); 1.411 + 1.412 + count = files.length; 1.413 + for each (var file in files) { 1.414 + var key = path + file.leafName; 1.415 + var childCount = 0; 1.416 + if (file.isDirectory()) { 1.417 + key += "/"; 1.418 + } 1.419 + if (recurse && file.isDirectory()) { 1.420 + [links[key], childCount] = list(key, file, recurse); 1.421 + count += childCount; 1.422 + } else { 1.423 + if (file.leafName.charAt(0) != '.') { 1.424 + links[key] = true; 1.425 + } 1.426 + } 1.427 + } 1.428 + 1.429 + return [links, count]; 1.430 +} 1.431 + 1.432 +/** 1.433 + * Heuristic function that determines whether a given path 1.434 + * is a test case to be executed in the harness, or just 1.435 + * a supporting file. 1.436 + */ 1.437 +function isTest(filename, pattern) 1.438 +{ 1.439 + if (pattern) 1.440 + return pattern.test(filename); 1.441 + 1.442 + // File name is a URL style path to a test file, make sure that we check for 1.443 + // tests that start with the appropriate prefix. 1.444 + var testPrefix = typeof(_TEST_PREFIX) == "string" ? _TEST_PREFIX : "test_"; 1.445 + var testPattern = new RegExp("^" + testPrefix); 1.446 + 1.447 + pathPieces = filename.split('/'); 1.448 + 1.449 + return testPattern.test(pathPieces[pathPieces.length - 1]) && 1.450 + filename.indexOf(".js") == -1 && 1.451 + filename.indexOf(".css") == -1 && 1.452 + !/\^headers\^$/.test(filename); 1.453 +} 1.454 + 1.455 +/** 1.456 + * Transform nested hashtables of paths to nested HTML lists. 1.457 + */ 1.458 +function linksToListItems(links) 1.459 +{ 1.460 + var response = ""; 1.461 + var children = ""; 1.462 + for (var [link, value] in links) { 1.463 + var classVal = (!isTest(link) && !(value instanceof Object)) 1.464 + ? "non-test invisible" 1.465 + : "test"; 1.466 + if (value instanceof Object) { 1.467 + children = UL({class: "testdir"}, linksToListItems(value)); 1.468 + } else { 1.469 + children = ""; 1.470 + } 1.471 + 1.472 + var bug_title = link.match(/test_bug\S+/); 1.473 + var bug_num = null; 1.474 + if (bug_title != null) { 1.475 + bug_num = bug_title[0].match(/\d+/); 1.476 + } 1.477 + 1.478 + if ((bug_title == null) || (bug_num == null)) { 1.479 + response += LI({class: classVal}, A({href: link}, link), children); 1.480 + } else { 1.481 + var bug_url = "https://bugzilla.mozilla.org/show_bug.cgi?id="+bug_num; 1.482 + response += LI({class: classVal}, A({href: link}, link), " - ", A({href: bug_url}, "Bug "+bug_num), children); 1.483 + } 1.484 + 1.485 + } 1.486 + return response; 1.487 +} 1.488 + 1.489 +/** 1.490 + * Transform nested hashtables of paths to a flat table rows. 1.491 + */ 1.492 +function linksToTableRows(links, recursionLevel) 1.493 +{ 1.494 + var response = ""; 1.495 + for (var [link, value] in links) { 1.496 + var classVal = (!isTest(link) && !(value instanceof Object)) 1.497 + ? "non-test invisible" 1.498 + : ""; 1.499 + 1.500 + spacer = "padding-left: " + (10 * recursionLevel) + "px"; 1.501 + 1.502 + if (value instanceof Object) { 1.503 + response += TR({class: "dir", id: "tr-" + link }, 1.504 + TD({colspan: "3"}, " "), 1.505 + TD({style: spacer}, 1.506 + A({href: link}, link))); 1.507 + response += linksToTableRows(value, recursionLevel + 1); 1.508 + } else { 1.509 + var bug_title = link.match(/test_bug\S+/); 1.510 + var bug_num = null; 1.511 + if (bug_title != null) { 1.512 + bug_num = bug_title[0].match(/\d+/); 1.513 + } 1.514 + if ((bug_title == null) || (bug_num == null)) { 1.515 + response += TR({class: classVal, id: "tr-" + link }, 1.516 + TD("0"), 1.517 + TD("0"), 1.518 + TD("0"), 1.519 + TD({style: spacer}, 1.520 + A({href: link}, link))); 1.521 + } else { 1.522 + var bug_url = "https://bugzilla.mozilla.org/show_bug.cgi?id=" + bug_num; 1.523 + response += TR({class: classVal, id: "tr-" + link }, 1.524 + TD("0"), 1.525 + TD("0"), 1.526 + TD("0"), 1.527 + TD({style: spacer}, 1.528 + A({href: link}, link), " - ", 1.529 + A({href: bug_url}, "Bug " + bug_num))); 1.530 + } 1.531 + } 1.532 + } 1.533 + return response; 1.534 +} 1.535 + 1.536 +function arrayOfTestFiles(linkArray, fileArray, testPattern) { 1.537 + for (var [link, value] in Iterator(linkArray)) { 1.538 + if (value instanceof Object) { 1.539 + arrayOfTestFiles(value, fileArray, testPattern); 1.540 + } else if (isTest(link, testPattern)) { 1.541 + fileArray.push(link) 1.542 + } 1.543 + } 1.544 +} 1.545 +/** 1.546 + * Produce a flat array of test file paths to be executed in the harness. 1.547 + */ 1.548 +function jsonArrayOfTestFiles(links) 1.549 +{ 1.550 + var testFiles = []; 1.551 + arrayOfTestFiles(links, testFiles); 1.552 + testFiles = ['"' + file + '"' for each(file in testFiles)]; 1.553 + return "[" + testFiles.join(",\n") + "]"; 1.554 +} 1.555 + 1.556 +/** 1.557 + * Produce a normal directory listing. 1.558 + */ 1.559 +function regularListing(metadata, response) 1.560 +{ 1.561 + var [links, count] = list(metadata.path, 1.562 + metadata.getProperty("directory"), 1.563 + false); 1.564 + response.write( 1.565 + HTML( 1.566 + HEAD( 1.567 + TITLE("mochitest index ", metadata.path) 1.568 + ), 1.569 + BODY( 1.570 + BR(), 1.571 + A({href: ".."}, "Up a level"), 1.572 + UL(linksToListItems(links)) 1.573 + ) 1.574 + ) 1.575 + ); 1.576 +} 1.577 + 1.578 +/** 1.579 + * Produce a test harness page containing all the test cases 1.580 + * below it, recursively. 1.581 + */ 1.582 +function testListing(metadata, response) 1.583 +{ 1.584 + var links = {}; 1.585 + var count = 0; 1.586 + if (metadata.queryString.indexOf('manifestFile') == -1) { 1.587 + [links, count] = list(metadata.path, 1.588 + metadata.getProperty("directory"), 1.589 + true); 1.590 + } 1.591 + var table_class = metadata.queryString.indexOf("hideResultsTable=1") > -1 ? "invisible": ""; 1.592 + 1.593 + let testname = (metadata.queryString.indexOf("testname=") > -1) 1.594 + ? metadata.queryString.match(/testname=([^&]+)/)[1] 1.595 + : ""; 1.596 + 1.597 + dumpn("count: " + count); 1.598 + var tests = testname 1.599 + ? "['/" + testname + "']" 1.600 + : jsonArrayOfTestFiles(links); 1.601 + response.write( 1.602 + HTML( 1.603 + HEAD( 1.604 + TITLE("MochiTest | ", metadata.path), 1.605 + LINK({rel: "stylesheet", 1.606 + type: "text/css", href: "/static/harness.css"} 1.607 + ), 1.608 + SCRIPT({type: "text/javascript", 1.609 + src: "/tests/SimpleTest/LogController.js"}), 1.610 + SCRIPT({type: "text/javascript", 1.611 + src: "/tests/SimpleTest/MemoryStats.js"}), 1.612 + SCRIPT({type: "text/javascript", 1.613 + src: "/tests/SimpleTest/TestRunner.js"}), 1.614 + SCRIPT({type: "text/javascript", 1.615 + src: "/tests/SimpleTest/MozillaLogger.js"}), 1.616 + SCRIPT({type: "text/javascript", 1.617 + src: "/chunkifyTests.js"}), 1.618 + SCRIPT({type: "text/javascript", 1.619 + src: "/manifestLibrary.js"}), 1.620 + SCRIPT({type: "text/javascript", 1.621 + src: "/tests/SimpleTest/setup.js"}), 1.622 + SCRIPT({type: "text/javascript"}, 1.623 + "window.onload = hookup; gTestList=" + tests + ";" 1.624 + ) 1.625 + ), 1.626 + BODY( 1.627 + DIV({class: "container"}, 1.628 + H2("--> ", A({href: "#", id: "runtests"}, "Run Tests"), " <--"), 1.629 + P({style: "float: right;"}, 1.630 + SMALL( 1.631 + "Based on the ", 1.632 + A({href:"http://www.mochikit.com/"}, "MochiKit"), 1.633 + " unit tests." 1.634 + ) 1.635 + ), 1.636 + DIV({class: "status"}, 1.637 + H1({id: "indicator"}, "Status"), 1.638 + H2({id: "pass"}, "Passed: ", SPAN({id: "pass-count"},"0")), 1.639 + H2({id: "fail"}, "Failed: ", SPAN({id: "fail-count"},"0")), 1.640 + H2({id: "fail"}, "Todo: ", SPAN({id: "todo-count"},"0")) 1.641 + ), 1.642 + DIV({class: "clear"}), 1.643 + DIV({id: "current-test"}, 1.644 + B("Currently Executing: ", 1.645 + SPAN({id: "current-test-path"}, "_") 1.646 + ) 1.647 + ), 1.648 + DIV({class: "clear"}), 1.649 + DIV({class: "frameholder"}, 1.650 + IFRAME({scrolling: "no", id: "testframe", width: "500", height: "300"}) 1.651 + ), 1.652 + DIV({class: "clear"}), 1.653 + DIV({class: "toggle"}, 1.654 + A({href: "#", id: "toggleNonTests"}, "Show Non-Tests"), 1.655 + BR() 1.656 + ), 1.657 + 1.658 + ( 1.659 + displayResults ? 1.660 + TABLE({cellpadding: 0, cellspacing: 0, class: table_class, id: "test-table"}, 1.661 + TR(TD("Passed"), TD("Failed"), TD("Todo"), TD("Test Files")), 1.662 + linksToTableRows(links, 0) 1.663 + ) : "" 1.664 + ), 1.665 + 1.666 + BR(), 1.667 + TABLE({cellpadding: 0, cellspacing: 0, border: 1, bordercolor: "red", id: "fail-table"} 1.668 + ), 1.669 + 1.670 + DIV({class: "clear"}) 1.671 + ) 1.672 + ) 1.673 + ) 1.674 + ); 1.675 +} 1.676 + 1.677 +/** 1.678 + * Respond to requests that match a file system directory. 1.679 + * Under the tests/ directory, return a test harness page. 1.680 + */ 1.681 +function defaultDirHandler(metadata, response) 1.682 +{ 1.683 + response.setStatusLine("1.1", 200, "OK"); 1.684 + response.setHeader("Content-type", "text/html;charset=utf-8", false); 1.685 + try { 1.686 + if (metadata.path.indexOf("/tests") != 0) { 1.687 + regularListing(metadata, response); 1.688 + } else { 1.689 + testListing(metadata, response); 1.690 + } 1.691 + } catch (ex) { 1.692 + response.write(ex); 1.693 + } 1.694 +}