1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 // We expect this to be defined in the global scope by runtest.py.
6 /* global _TEST_PREFIX */
11 /* global A, ABBR, ACRONYM, ADDRESS, APPLET, AREA, B, BASE,
12 BASEFONT, BDO, BIG, BLOCKQUOTE, BODY, BR, BUTTON,
13 CAPTION, CENTER, CITE, CODE, COL, COLGROUP, DD,
14 DEL, DFN, DIR, DIV, DL, DT, EM, FIELDSET, FONT,
15 FORM, FRAME, FRAMESET, H1, H2, H3, H4, H5, H6,
16 HEAD, HR, HTML, I, IFRAME, IMG, INPUT, INS,
17 ISINDEX, KBD, LABEL, LEGEND, LI, LINK, MAP, MENU,
18 META, NOFRAMES, NOSCRIPT, OBJECT, OL, OPTGROUP,
19 OPTION, P, PARAM, PRE, Q, S, SAMP, SCRIPT,
20 SELECT, SMALL, SPAN, STRIKE, STRONG, STYLE, SUB,
21 SUP, TABLE, TBODY, TD, TEXTAREA, TFOOT, TH, THEAD,
22 TITLE, TR, TT, U, UL, VAR */
118 * Below, we'll use makeTagFunc to create a function for each of the
119 * strings in 'tags'. This will allow us to use s-expression like syntax
122 function makeTagFunc(tagName) {
123 return function (attrs /* rest... */) {
124 var startChildren = 0;
127 // write the start tag and attributes
128 response += "<" + tagName;
129 // if attr is an object, write attributes
130 if (attrs && typeof attrs == "object") {
133 for (let key in attrs) {
134 const value = attrs[key];
135 var val = "" + value;
136 response += " " + key + '="' + val.replace('"', """) + '"';
141 // iterate through the rest of the args
142 for (var i = startChildren; i < arguments.length; i++) {
143 if (typeof arguments[i] == "function") {
144 response += arguments[i]();
146 response += arguments[i];
150 // write the close tag
151 response += "</" + tagName + ">\n";
156 function makeTags() {
157 // map our global HTML generation functions
158 for (let tag of tags) {
159 this[tag] = makeTagFunc(tag.toLowerCase());
164 * Creates a generator that iterates over the contents of
165 * an nsIFile directory.
167 function* dirIter(dir) {
168 var en = dir.directoryEntries;
169 while (en.hasMoreElements()) {
175 * Builds an optionally nested object containing links to the
176 * files and directories within dir.
178 function list(requestPath, directory, recurse) {
180 var path = requestPath;
181 if (path.charAt(path.length - 1) != "/") {
185 var dir = directory.QueryInterface(Ci.nsIFile);
188 // The SimpleTest directory is hidden
190 for (let file of dirIter(dir)) {
191 if (file.exists() && !file.path.includes("SimpleTest")) {
196 // Sort files by name, so that tests can be run in a pre-defined order inside
197 // a given directory (see bug 384823)
198 function leafNameComparator(first, second) {
199 if (first.leafName < second.leafName) {
202 if (first.leafName > second.leafName) {
207 files.sort(leafNameComparator);
209 count = files.length;
210 for (let file of files) {
211 var key = path + file.leafName;
213 if (file.isDirectory()) {
216 if (recurse && file.isDirectory()) {
217 [links[key], childCount] = list(key, file, recurse);
219 } else if (file.leafName.charAt(0) != ".") {
220 links[key] = { test: { url: key, expected: "pass" } };
224 return [links, count];
228 * Heuristic function that determines whether a given path
229 * is a test case to be executed in the harness, or just
232 function isTest(filename, pattern) {
234 return pattern.test(filename);
237 // File name is a URL style path to a test file, make sure that we check for
238 // tests that start with the appropriate prefix.
239 var testPrefix = typeof _TEST_PREFIX == "string" ? _TEST_PREFIX : "test_";
240 var testPattern = new RegExp("^" + testPrefix);
242 var pathPieces = filename.split("/");
245 testPattern.test(pathPieces[pathPieces.length - 1]) &&
246 !filename.includes(".js") &&
247 !filename.includes(".css") &&
248 !/\^headers\^$/.test(filename)
253 * Transform nested hashtables of paths to nested HTML lists.
255 function linksToListItems(links) {
258 for (let link in links) {
259 const value = links[link];
261 !isTest(link) && !(value instanceof Object)
262 ? "non-test invisible"
264 if (value instanceof Object) {
265 children = UL({ class: "testdir" }, linksToListItems(value));
270 var bug_title = link.match(/test_bug\S+/);
272 if (bug_title != null) {
273 bug_num = bug_title[0].match(/\d+/);
276 if (bug_title == null || bug_num == null) {
277 response += LI({ class: classVal }, A({ href: link }, link), children);
279 var bug_url = "https://bugzilla.mozilla.org/show_bug.cgi?id=" + bug_num;
282 A({ href: link }, link),
284 A({ href: bug_url }, "Bug " + bug_num),
293 * Transform nested hashtables of paths to a flat table rows.
295 function linksToTableRows(links, recursionLevel) {
297 for (let link in links) {
298 const value = links[link];
300 !isTest(link) && value instanceof Object && "test" in value
301 ? "non-test invisible"
304 var spacer = "padding-left: " + 10 * recursionLevel + "px";
306 if (value instanceof Object && !("test" in value)) {
308 { class: "dir", id: "tr-" + link },
309 TD({ colspan: "3" }, " "),
310 TD({ style: spacer }, A({ href: link }, link))
312 response += linksToTableRows(value, recursionLevel + 1);
314 var bug_title = link.match(/test_bug\S+/);
316 if (bug_title != null) {
317 bug_num = bug_title[0].match(/\d+/);
319 if (bug_title == null || bug_num == null) {
321 { class: classVal, id: "tr-" + link },
325 TD({ style: spacer }, A({ href: link }, link))
328 var bug_url = "https://bugzilla.mozilla.org/show_bug.cgi?id=" + bug_num;
330 { class: classVal, id: "tr-" + link },
336 A({ href: link }, link),
338 A({ href: bug_url }, "Bug " + bug_num)
347 function arrayOfTestFiles(linkArray, fileArray, testPattern) {
348 for (let link in linkArray) {
349 const value = linkArray[link];
350 if (value instanceof Object && !("test" in value)) {
351 arrayOfTestFiles(value, fileArray, testPattern);
352 } else if (isTest(link, testPattern) && value instanceof Object) {
353 fileArray.push(value.test);
358 * Produce a flat array of test file paths to be executed in the harness.
360 function jsonArrayOfTestFiles(links) {
362 arrayOfTestFiles(links, testFiles);
363 testFiles = testFiles.map(function (file) {
364 return '"' + file.url + '"';
367 return "[" + testFiles.join(",\n") + "]";
371 * Produce a normal directory listing.
373 function regularListing(metadata, response) {
374 var [links] = list(metadata.path, metadata.getProperty("directory"), false);
376 "<!DOCTYPE html>\n" +
378 HEAD(TITLE("mochitest index ", metadata.path)),
379 BODY(BR(), A({ href: ".." }, "Up a level"), UL(linksToListItems(links)))