summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/tools/runner/runner.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/tools/runner/runner.js')
-rw-r--r--testing/web-platform/tests/tools/runner/runner.js854
1 files changed, 854 insertions, 0 deletions
diff --git a/testing/web-platform/tests/tools/runner/runner.js b/testing/web-platform/tests/tools/runner/runner.js
new file mode 100644
index 000000000..4319c234e
--- /dev/null
+++ b/testing/web-platform/tests/tools/runner/runner.js
@@ -0,0 +1,854 @@
+/*jshint nonew: false */
+(function() {
+"use strict";
+var runner;
+var testharness_properties = {output:false,
+ timeout_multiplier:1};
+
+function Manifest(path) {
+ this.data = null;
+ this.path = path;
+ this.num_tests = null;
+}
+
+Manifest.prototype = {
+ load: function(loaded_callback) {
+ this.generate(loaded_callback);
+ },
+
+ do_load: function(loaded_callback) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState !== 4) {
+ return;
+ }
+ if (!(xhr.status === 200 || xhr.status === 0)) {
+ throw new Error("Manifest " + this.path + " failed to load");
+ }
+ this.data = JSON.parse(xhr.responseText);
+ loaded_callback();
+ }.bind(this);
+ xhr.open("GET", this.path);
+ xhr.send(null);
+ },
+
+ generate: function(loaded_callback) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState !== 4) {
+ return;
+ }
+ if (!(xhr.status === 200 || xhr.status === 0)) {
+ throw new Error("Manifest generation failed");
+ }
+ this.do_load(loaded_callback);
+ }.bind(this);
+ xhr.open("POST", "update_manifest.py");
+ xhr.send(null);
+ },
+
+ by_type:function(type) {
+ var ret = [] ;
+ if (this.data.items.hasOwnProperty(type)) {
+ ret = this.data.items[type].slice(0) ;
+ }
+ // local_changes.items in manifest is an Object just as
+ // items is. However, the properties of local_changes.items
+ // are Objects and the properties of items are Arrays.
+ // So we need to extract any relevant local changes by iterating
+ // over the Object and pulling out the referenced nodes as array items.
+ if (this.data.hasOwnProperty("local_changes")) {
+ var local = this.data.local_changes ;
+ // add in any local items
+ if (local.items.hasOwnProperty(type)) {
+ Object.keys(local.items[type]).forEach(function(ref) {
+ ret.push(local.items[type][ref][0]) ;
+ }.bind(this));
+ }
+ // remove any items that are locally deleted but not yet committed
+ // note that the deleted and deleted_reftests properties of the local_changes
+ // object are always present, even if they are empty
+ if (ret.length && local.deleted.length) {
+ // make a hash of the deleted to speed searching
+ var dels = {} ;
+ local.deleted.forEach(function(x) { dels[x] = true; } );
+ for (var j = ret.length-1; j >= 0; j--) {
+ if ( dels[ret[j].path] || (type === "reftest" && local.deleted_reftests[ret[j].path]) ){
+ // we have a match
+ ret.splice(j, 1) ;
+ }
+ }
+ }
+ }
+ return ret ;
+ }
+};
+
+function ManifestIterator(manifest, path, test_types, use_regex) {
+ this.manifest = manifest;
+ this.paths = null;
+ this.regex_pattern = null;
+ this.test_types = test_types;
+ this.test_types_index = -1;
+ this.test_list = null;
+ this.test_index = null;
+
+ if (use_regex) {
+ this.regex_pattern = path;
+ } else {
+ // Split paths by either a comma or whitespace, and ignore empty sub-strings.
+ this.paths = path.split(/[,\s]+/).filter(function(s) { return s.length > 0; });
+ }
+}
+
+ManifestIterator.prototype = {
+ next: function() {
+ var manifest_item = null;
+
+ if (this.test_types.length === 0) {
+ return null;
+ }
+
+ while (!manifest_item) {
+ while (this.test_list === null || this.test_index >= this.test_list.length) {
+ this.test_types_index++;
+ if (this.test_types_index >= this.test_types.length) {
+ return null;
+ }
+ this.test_index = 0;
+ this.test_list = this.manifest.by_type(this.test_types[this.test_types_index]);
+ }
+
+ manifest_item = this.test_list[this.test_index++];
+ while (manifest_item && !this.matches(manifest_item)) {
+ manifest_item = this.test_list[this.test_index++];
+ }
+ if (manifest_item) {
+ return this.to_test(manifest_item);
+ }
+ }
+ },
+
+ matches: function(manifest_item) {
+ if (this.regex_pattern !== null) {
+ return manifest_item.url.match(this.regex_pattern);
+ } else {
+ return this.paths.some(function(p) {
+ return manifest_item.url.indexOf(p) === 0;
+ });
+ }
+ },
+
+ to_test: function(manifest_item) {
+ var test = {
+ type: this.test_types[this.test_types_index],
+ url: manifest_item.url
+ };
+ if (manifest_item.hasOwnProperty("references")) {
+ test.ref_length = manifest_item.references.length;
+ test.ref_type = manifest_item.references[0][1];
+ test.ref_url = manifest_item.references[0][0];
+ }
+ return test;
+ },
+
+ count: function() {
+ return this.test_types.reduce(function(prev, current) {
+ var matches = this.manifest.by_type(current).filter(function(x) {
+ return this.matches(x);
+ }.bind(this));
+ return prev + matches.length;
+ }.bind(this), 0);
+ }
+};
+
+function VisualOutput(elem, runner) {
+ this.elem = elem;
+ this.runner = runner;
+ this.results_table = null;
+ this.section_wrapper = null;
+ this.results_table = this.elem.querySelector(".results > table");
+ this.section = null;
+ this.manifest_status = this.elem.querySelector("#manifest");
+ this.progress = this.elem.querySelector(".summary .progress");
+ this.meter = this.progress.querySelector(".progress-bar");
+ this.result_count = null;
+ this.json_results_area = this.elem.querySelector("textarea");
+ this.instructions = document.querySelector(".instructions");
+
+ this.elem.style.display = "none";
+ this.runner.manifest_wait_callbacks.push(this.on_manifest_wait.bind(this));
+ this.runner.start_callbacks.push(this.on_start.bind(this));
+ this.runner.result_callbacks.push(this.on_result.bind(this));
+ this.runner.done_callbacks.push(this.on_done.bind(this));
+
+ this.display_filter_state = {};
+
+ var visual_output = this;
+ var display_filter_inputs = this.elem.querySelectorAll(".result-display-filter");
+ for (var i = 0; i < display_filter_inputs.length; ++i) {
+ var display_filter_input = display_filter_inputs[i];
+ this.display_filter_state[display_filter_input.value] = display_filter_input.checked;
+ display_filter_input.addEventListener("change", function(e) {
+ visual_output.apply_display_filter(e.target.value, e.target.checked);
+ })
+ }
+}
+
+VisualOutput.prototype = {
+ clear: function() {
+ this.result_count = {"PASS":0,
+ "FAIL":0,
+ "ERROR":0,
+ "TIMEOUT":0,
+ "NOTRUN":0};
+ for (var p in this.result_count) {
+ if (this.result_count.hasOwnProperty(p)) {
+ this.elem.querySelector("td." + p).textContent = 0;
+ }
+ }
+ if (this.json_results_area) {
+ this.json_results_area.parentNode.removeChild(this.json_results_area);
+ }
+ this.meter.style.width = '0px';
+ this.meter.textContent = '0%';
+ this.manifest_status.style.display = "none";
+ this.elem.querySelector(".jsonResults").style.display = "none";
+ this.results_table.removeChild(this.results_table.tBodies[0]);
+ this.results_table.appendChild(document.createElement("tbody"));
+ },
+
+ on_manifest_wait: function() {
+ this.clear();
+ this.instructions.style.display = "none";
+ this.elem.style.display = "block";
+ this.manifest_status.style.display = "inline";
+ },
+
+ on_start: function() {
+ this.clear();
+ this.instructions.style.display = "none";
+ this.elem.style.display = "block";
+ this.meter.classList.remove("stopped");
+ this.meter.classList.add("progress-striped", "active");
+ },
+
+ on_result: function(test, status, message, subtests) {
+ var row = document.createElement("tr");
+
+ var subtest_pass_count = subtests.reduce(function(prev, current) {
+ return (current.status === "PASS") ? prev + 1 : prev;
+ }, 0);
+
+ var subtest_notrun_count = subtests.reduce(function(prev, current) {
+ return (current.status === "NOTRUN") ? prev +1 : prev;
+ }, 0);
+
+ var subtests_count = subtests.length;
+
+ var test_status;
+ if (subtest_pass_count === subtests_count &&
+ (status == "OK" || status == "PASS")) {
+ test_status = "PASS";
+ } else if (subtest_notrun_count == subtests_count) {
+ test_status = "NOTRUN";
+ } else if (subtests_count > 0 && status === "OK") {
+ test_status = "FAIL";
+ } else {
+ test_status = status;
+ }
+
+ subtests.forEach(function(subtest) {
+ if (this.result_count.hasOwnProperty(subtest.status)) {
+ this.result_count[subtest.status] += 1;
+ }
+ }.bind(this));
+ if (this.result_count.hasOwnProperty(status)) {
+ this.result_count[status] += 1;
+ }
+
+ var name_node = row.appendChild(document.createElement("td"));
+ name_node.appendChild(this.test_name_node(test));
+
+ var status_node = row.appendChild(document.createElement("td"));
+ status_node.textContent = test_status;
+ status_node.className = test_status;
+
+ var message_node = row.appendChild(document.createElement("td"));
+ message_node.textContent = message || "";
+
+ var subtests_node = row.appendChild(document.createElement("td"));
+ if (subtests_count) {
+ subtests_node.textContent = subtest_pass_count + "/" + subtests_count;
+ } else {
+ if (status == "PASS") {
+ subtests_node.textContent = "1/1";
+ } else {
+ subtests_node.textContent = "0/1";
+ }
+ }
+
+ var status_arr = ["PASS", "FAIL", "ERROR", "TIMEOUT", "NOTRUN"];
+ for (var i = 0; i < status_arr.length; i++) {
+ this.elem.querySelector("td." + status_arr[i]).textContent = this.result_count[status_arr[i]];
+ }
+
+ this.apply_display_filter_to_result_row(row, this.display_filter_state[test_status]);
+ this.results_table.tBodies[0].appendChild(row);
+ this.update_meter(this.runner.progress(), this.runner.results.count(), this.runner.test_count());
+ },
+
+ on_done: function() {
+ this.meter.setAttribute("aria-valuenow", this.meter.getAttribute("aria-valuemax"));
+ this.meter.style.width = "100%";
+ if (this.runner.stop_flag) {
+ this.meter.textContent = "Stopped";
+ this.meter.classList.add("stopped");
+ } else {
+ this.meter.textContent = "Done!";
+ }
+ this.meter.classList.remove("progress-striped", "active");
+ this.runner.test_div.textContent = "";
+ //add the json serialization of the results
+ var a = this.elem.querySelector(".jsonResults");
+ var json = this.runner.results.to_json();
+
+ if (document.getElementById("dumpit").checked) {
+ this.json_results_area = Array.prototype.slice.call(this.elem.querySelectorAll("textarea"));
+ for(var i = 0,t = this.json_results_area.length; i < t; i++){
+ this.elem.removeChild(this.json_results_area[i]);
+ }
+ this.json_results_area = document.createElement("textarea");
+ this.json_results_area.style.width = "100%";
+ this.json_results_area.setAttribute("rows", "50");
+ this.elem.appendChild(this.json_results_area);
+ this.json_results_area.textContent = json;
+ }
+ var blob = new Blob([json], { type: "application/json" });
+ a.href = window.URL.createObjectURL(blob);
+ a.download = "runner-results.json";
+ a.textContent = "Download JSON results";
+ if (!a.getAttribute("download")) a.textContent += " (right-click and save as to download)";
+ a.style.display = "inline";
+ },
+
+ test_name_node: function(test) {
+ if (!test.hasOwnProperty("ref_url")) {
+ return this.link(test.url);
+ } else {
+ var wrapper = document.createElement("span");
+ wrapper.appendChild(this.link(test.url));
+ wrapper.appendChild(document.createTextNode(" " + test.ref_type + " "));
+ wrapper.appendChild(this.link(test.ref_url));
+ return wrapper;
+ }
+ },
+
+ link: function(href) {
+ var link = document.createElement("a");
+ link.href = this.runner.server + href;
+ link.textContent = href;
+ return link;
+ },
+
+ update_meter: function(progress, count, total) {
+ this.meter.setAttribute("aria-valuenow", count);
+ this.meter.setAttribute("aria-valuemax", total);
+ this.meter.textContent = this.meter.style.width = (progress * 100).toFixed(1) + "%";
+ },
+
+ apply_display_filter: function(test_status, display_state) {
+ this.display_filter_state[test_status] = display_state;
+ var result_cells = this.elem.querySelectorAll(".results > table tr td." + test_status);
+ for (var i = 0; i < result_cells.length; ++i) {
+ this.apply_display_filter_to_result_row(result_cells[i].parentNode, display_state)
+ }
+ },
+
+ apply_display_filter_to_result_row: function(result_row, display_state) {
+ result_row.style.display = display_state ? "" : "none";
+ }
+};
+
+function ManualUI(elem, runner) {
+ this.elem = elem;
+ this.runner = runner;
+ this.pass_button = this.elem.querySelector("button.pass");
+ this.fail_button = this.elem.querySelector("button.fail");
+ this.ref_buttons = this.elem.querySelector(".reftestUI");
+ this.ref_type = this.ref_buttons.querySelector(".refType");
+ this.ref_warning = this.elem.querySelector(".reftestWarn");
+ this.test_button = this.ref_buttons.querySelector("button.test");
+ this.ref_button = this.ref_buttons.querySelector("button.ref");
+
+ this.hide();
+
+ this.runner.test_start_callbacks.push(this.on_test_start.bind(this));
+ this.runner.test_pause_callbacks.push(this.hide.bind(this));
+ this.runner.done_callbacks.push(this.on_done.bind(this));
+
+ this.pass_button.onclick = function() {
+ this.disable_buttons();
+ this.runner.on_result("PASS", "", []);
+ }.bind(this);
+
+ this.fail_button.onclick = function() {
+ this.disable_buttons();
+ this.runner.on_result("FAIL", "", []);
+ }.bind(this);
+}
+
+ManualUI.prototype = {
+ show: function() {
+ this.elem.style.display = "block";
+ setTimeout(this.enable_buttons.bind(this), 200);
+ },
+
+ hide: function() {
+ this.elem.style.display = "none";
+ },
+
+ show_ref: function() {
+ this.ref_buttons.style.display = "block";
+ this.test_button.onclick = function() {
+ this.runner.load(this.runner.current_test.url);
+ }.bind(this);
+ this.ref_button.onclick = function() {
+ this.runner.load(this.runner.current_test.ref_url);
+ }.bind(this);
+ },
+
+ hide_ref: function() {
+ this.ref_buttons.style.display = "none";
+ },
+
+ disable_buttons: function() {
+ this.pass_button.disabled = true;
+ this.fail_button.disabled = true;
+ },
+
+ enable_buttons: function() {
+ this.pass_button.disabled = false;
+ this.fail_button.disabled = false;
+ },
+
+ on_test_start: function(test) {
+ if (test.type == "manual" || test.type == "reftest") {
+ this.show();
+ } else {
+ this.hide();
+ }
+ if (test.type == "reftest") {
+ this.show_ref();
+ this.ref_type.textContent = test.ref_type === "==" ? "equal" : "unequal";
+ if (test.ref_length > 1) {
+ this.ref_warning.textContent = "WARNING: only presenting first of " + test.ref_length + " references";
+ this.ref_warning.style.display = "inline";
+ } else {
+ this.ref_warning.textContent = "";
+ this.ref_warning.style.display = "none";
+ }
+ } else {
+ this.hide_ref();
+ }
+ },
+
+ on_done: function() {
+ this.hide();
+ }
+};
+
+function TestControl(elem, runner) {
+ this.elem = elem;
+ this.path_input = this.elem.querySelector(".path");
+ this.path_input.addEventListener("change", function() {
+ this.set_counts();
+ }.bind(this), false);
+ this.use_regex_input = this.elem.querySelector("#use_regex");
+ this.use_regex_input.addEventListener("change", function() {
+ this.set_counts();
+ }.bind(this), false);
+ this.pause_button = this.elem.querySelector("button.togglePause");
+ this.start_button = this.elem.querySelector("button.toggleStart");
+ this.type_checkboxes = Array.prototype.slice.call(
+ this.elem.querySelectorAll("input[type=checkbox].test-type"));
+ this.type_checkboxes.forEach(function(elem) {
+ elem.addEventListener("change", function() {
+ this.set_counts();
+ }.bind(this),
+ false);
+ elem.addEventListener("click", function() {
+ this.start_button.disabled = this.get_test_types().length < 1;
+ }.bind(this),
+ false);
+ }.bind(this));
+
+ this.timeout_input = this.elem.querySelector(".timeout_multiplier");
+ this.render_checkbox = this.elem.querySelector(".render");
+ this.testcount_area = this.elem.querySelector("#testcount");
+ this.runner = runner;
+ this.runner.done_callbacks.push(this.on_done.bind(this));
+ this.set_start();
+ this.set_counts();
+}
+
+TestControl.prototype = {
+ set_start: function() {
+ this.start_button.disabled = this.get_test_types().length < 1;
+ this.pause_button.disabled = true;
+ this.start_button.textContent = "Start";
+ this.path_input.disabled = false;
+ this.type_checkboxes.forEach(function(elem) {
+ elem.disabled = false;
+ });
+ this.start_button.onclick = function() {
+ var path = this.get_path();
+ var test_types = this.get_test_types();
+ var settings = this.get_testharness_settings();
+ var use_regex = this.get_use_regex();
+ this.runner.start(path, test_types, settings, use_regex);
+ this.set_stop();
+ this.set_pause();
+ }.bind(this);
+ },
+
+ set_stop: function() {
+ clearTimeout(this.runner.timeout);
+ this.pause_button.disabled = false;
+ this.start_button.textContent = "Stop";
+ this.path_input.disabled = true;
+ this.type_checkboxes.forEach(function(elem) {
+ elem.disabled = true;
+ });
+ this.start_button.onclick = function() {
+ this.runner.stop_flag = true;
+ this.runner.done();
+ }.bind(this);
+ },
+
+ set_pause: function() {
+ this.pause_button.textContent = "Pause";
+ this.pause_button.onclick = function() {
+ this.runner.pause();
+ this.set_resume();
+ }.bind(this);
+ },
+
+ set_resume: function() {
+ this.pause_button.textContent = "Resume";
+ this.pause_button.onclick = function() {
+ this.runner.unpause();
+ this.set_pause();
+ }.bind(this);
+
+ },
+
+ set_counts: function() {
+ if (this.runner.manifest_loading) {
+ setTimeout(function() {
+ this.set_counts();
+ }.bind(this), 1000);
+ return;
+ }
+ var path = this.get_path();
+ var test_types = this.get_test_types();
+ var use_regex = this.get_use_regex();
+ var iterator = new ManifestIterator(this.runner.manifest, path, test_types, use_regex);
+ var count = iterator.count();
+ this.testcount_area.textContent = count;
+ },
+
+ get_path: function() {
+ return this.path_input.value;
+ },
+
+ get_test_types: function() {
+ return this.type_checkboxes.filter(function(elem) {
+ return elem.checked;
+ }).map(function(elem) {
+ return elem.value;
+ });
+ },
+
+ get_testharness_settings: function() {
+ return {timeout_multiplier: parseFloat(this.timeout_input.value),
+ output: this.render_checkbox.checked};
+ },
+
+ get_use_regex: function() {
+ return this.use_regex_input.checked;
+ },
+
+ on_done: function() {
+ this.set_pause();
+ this.set_start();
+ }
+};
+
+function Results(runner) {
+ this.test_results = null;
+ this.runner = runner;
+
+ this.runner.start_callbacks.push(this.on_start.bind(this));
+}
+
+Results.prototype = {
+ on_start: function() {
+ this.test_results = [];
+ },
+
+ set: function(test, status, message, subtests) {
+ this.test_results.push({"test":test,
+ "subtests":subtests,
+ "status":status,
+ "message":message});
+ },
+
+ count: function() {
+ return this.test_results.length;
+ },
+
+ to_json: function() {
+ var data = {
+ "results": this.test_results.map(function(result) {
+ var rv = {"test":(result.test.hasOwnProperty("ref_url") ?
+ [result.test.url, result.test.ref_type, result.test.ref_url] :
+ result.test.url),
+ "subtests":result.subtests,
+ "status":result.status,
+ "message":result.message};
+ return rv;
+ })
+ };
+ return JSON.stringify(data, null, 2);
+ }
+};
+
+function Runner(manifest_path) {
+ this.server = location.protocol + "//" + location.host;
+ this.manifest = new Manifest(manifest_path);
+ this.path = null;
+ this.test_types = null;
+ this.manifest_iterator = null;
+
+ this.test_window = null;
+ this.test_div = document.getElementById('test_url');
+ this.current_test = null;
+ this.timeout = null;
+ this.num_tests = null;
+ this.pause_flag = false;
+ this.stop_flag = false;
+ this.done_flag = false;
+
+ this.manifest_wait_callbacks = [];
+ this.start_callbacks = [];
+ this.test_start_callbacks = [];
+ this.test_pause_callbacks = [];
+ this.result_callbacks = [];
+ this.done_callbacks = [];
+
+ this.results = new Results(this);
+
+ this.start_after_manifest_load = false;
+ this.manifest_loading = true;
+ this.manifest.load(this.manifest_loaded.bind(this));
+}
+
+Runner.prototype = {
+ test_timeout: 20000, //ms
+
+ currentTest: function() {
+ return this.manifest[this.mTestCount];
+ },
+
+ open_test_window: function() {
+ this.test_window = window.open("about:blank", 800, 600);
+ },
+
+ manifest_loaded: function() {
+ this.manifest_loading = false;
+ if (this.start_after_manifest_load) {
+ this.do_start();
+ }
+ },
+
+ start: function(path, test_types, testharness_settings, use_regex) {
+ this.pause_flag = false;
+ this.stop_flag = false;
+ this.done_flag = false;
+ this.path = path;
+ this.use_regex = use_regex;
+ this.test_types = test_types;
+ window.testharness_properties = testharness_settings;
+ this.manifest_iterator = new ManifestIterator(this.manifest, this.path, this.test_types, this.use_regex);
+ this.num_tests = null;
+
+ if (this.manifest.data === null) {
+ this.wait_for_manifest();
+ } else {
+ this.do_start();
+ }
+ },
+
+ wait_for_manifest: function() {
+ this.start_after_manifest_load = true;
+ this.manifest_wait_callbacks.forEach(function(callback) {
+ callback();
+ });
+ },
+
+ do_start: function() {
+ if (this.manifest_iterator.count() > 0) {
+ this.open_test_window();
+ this.start_callbacks.forEach(function(callback) {
+ callback();
+ });
+ this.run_next_test();
+ } else {
+ var tests = "tests";
+ if (this.test_types.length < 3) {
+ tests = this.test_types.join(" tests or ") + " tests";
+ }
+ var message = "No " + tests + " found in this path."
+ document.querySelector(".path").setCustomValidity(message);
+ this.done();
+ }
+ },
+
+ pause: function() {
+ this.pause_flag = true;
+ this.test_pause_callbacks.forEach(function(callback) {
+ callback(this.current_test);
+ }.bind(this));
+ },
+
+ unpause: function() {
+ this.pause_flag = false;
+ this.run_next_test();
+ },
+
+ on_result: function(status, message, subtests) {
+ clearTimeout(this.timeout);
+ this.results.set(this.current_test, status, message, subtests);
+ this.result_callbacks.forEach(function(callback) {
+ callback(this.current_test, status, message, subtests);
+ }.bind(this));
+ this.run_next_test();
+ },
+
+ on_timeout: function() {
+ this.on_result("TIMEOUT", "", []);
+ },
+
+ done: function() {
+ this.done_flag = true;
+ if (this.test_window) {
+ this.test_window.close();
+ }
+ this.done_callbacks.forEach(function(callback) {
+ callback();
+ });
+ },
+
+ run_next_test: function() {
+ if (this.pause_flag) {
+ return;
+ }
+ var next_test = this.manifest_iterator.next();
+ if (next_test === null||this.done_flag) {
+ this.done();
+ return;
+ }
+
+ this.current_test = next_test;
+
+ if (next_test.type === "testharness") {
+ this.timeout = setTimeout(this.on_timeout.bind(this),
+ this.test_timeout * window.testharness_properties.timeout_multiplier);
+ }
+ this.test_div.textContent = this.current_test.url;
+ this.load(this.current_test.url);
+
+ this.test_start_callbacks.forEach(function(callback) {
+ callback(this.current_test);
+ }.bind(this));
+ },
+
+ load: function(path) {
+ if (this.test_window.location === null) {
+ this.open_test_window();
+ }
+ this.test_window.location.href = this.server + path;
+ },
+
+ progress: function() {
+ return this.results.count() / this.test_count();
+ },
+
+ test_count: function() {
+ if (this.num_tests === null) {
+ this.num_tests = this.manifest_iterator.count();
+ }
+ return this.num_tests;
+ }
+
+};
+
+
+function parseOptions() {
+ var options = {
+ test_types: ["testharness", "reftest", "manual"]
+ };
+
+ var optionstrings = location.search.substring(1).split("&");
+ for (var i = 0, il = optionstrings.length; i < il; ++i) {
+ var opt = optionstrings[i];
+ //TODO: fix this for complex-valued options
+ options[opt.substring(0, opt.indexOf("="))] =
+ opt.substring(opt.indexOf("=") + 1);
+ }
+ return options;
+}
+
+function setup() {
+ var options = parseOptions();
+
+ if (options.path) {
+ document.getElementById('path').value = options.path;
+ }
+
+ runner = new Runner("/MANIFEST.json", options);
+ var test_control = new TestControl(document.getElementById("testControl"), runner);
+ new ManualUI(document.getElementById("manualUI"), runner);
+ new VisualOutput(document.getElementById("output"), runner);
+
+ if (options.autorun === "1") {
+ runner.start(test_control.get_path(),
+ test_control.get_test_types(),
+ test_control.get_testharness_settings(),
+ test_control.get_use_regex());
+ return;
+ }
+}
+
+window.completion_callback = function(tests, status) {
+ var harness_status_map = {0:"OK", 1:"ERROR", 2:"TIMEOUT", 3:"NOTRUN"};
+ var subtest_status_map = {0:"PASS", 1:"FAIL", 2:"TIMEOUT", 3:"NOTRUN"};
+
+ // this ugly hack is because IE really insists on holding on to the objects it creates in
+ // other windows, and on losing track of them when the window gets closed
+ var subtest_results = JSON.parse(JSON.stringify(
+ tests.map(function (test) {
+ return {name: test.name,
+ status: subtest_status_map[test.status],
+ message: test.message};
+ })
+ ));
+
+ runner.on_result(harness_status_map[status.status],
+ status.message,
+ subtest_results);
+};
+
+window.addEventListener("DOMContentLoaded", setup, false);
+})();