/*global self*/ /*jshint latedef: nofunc*/ /* Distributed under both the W3C Test Suite License [1] and the W3C 3-clause BSD License [2]. To contribute to a W3C Test Suite, see the policies and contribution forms [3].
/* * TestEnvironment is an abstraction for the environment in which the test * harness is used. Each implementation of a test environment has to provide * the following interface: * * interface TestEnvironment { * // Invoked after the global 'tests' object has been created and it's * // safe to call add_*_callback() to register event handlers. * void on_tests_ready(); * * // Invoked after setup() has been called to notify the test environment * // of changes to the test harness properties. * void on_new_harness_properties(object properties); * * // Should return a new unique default test name. * DOMString next_default_test_name(); * * // Should return the test harness timeout duration in milliseconds. * float test_timeout(); * * // Should return the global scope object. * object global_scope(); * };
*/
/* * A test environment with a DOM. The global object is 'window'. By default * test results are displayed in a table. Any parent windows receive * callbacks or messages via postMessage() when test events occur. See * apisample11.html and apisample12.html.
*/ function WindowTestEnvironment() { this.name_counter = 0; this.window_cache = null; this.output_handler = null; this.all_loaded = false; var this_obj = this; this.message_events = [];
WindowTestEnvironment.prototype._dispatch = function(selector, callback_args, message_arg) { this._forEach_windows( function(w, same_origin) { if (same_origin) { try { var has_selector = selector in w;
} catch(e) { // If document.domain was set at some point same_origin can be // wrong and the above will fail.
has_selector = false;
} if (has_selector) { try {
w[selector].apply(undefined, callback_args);
} catch (e) { if (debug) { throw e;
}
}
}
} if (supports_post_message(w) && w !== self) {
w.postMessage(message_arg, "*");
}
});
};
WindowTestEnvironment.prototype._forEach_windows = function(callback) { // Iterate of the the windows [self ... top, opener]. The callback is passed // two objects, the first one is the windows object itself, the second one // is a boolean indicating whether or not its on the same origin as the // current window. var cache = this.window_cache; if (!cache) {
cache = [[self, true]]; var w = self; var i = 0; var so; var origins = location.ancestorOrigins; while (w != w.parent) {
w = w.parent; // In WebKit, calls to parent windows' properties that aren't on the same // origin cause an error message to be displayed in the error console but // don't throw an exception. This is a deviation from the current HTML5 // spec. See: https://bugs.webkit.org/show_bug.cgi?id=43504 // The problem with WebKit's behavior is that it pollutes the error console // with error messages that can't be caught. // // This issue can be mitigated by relying on the (for now) proprietary // `location.ancestorOrigins` property which returns an ordered list of // the origins of enclosing windows. See: // http://trac.webkit.org/changeset/113945. if (origins) {
so = (location.origin == origins[i]);
} else {
so = is_same_origin(w);
}
cache.push([w, so]);
i++;
}
w = window.opener; if (w) { // window.opener isn't included in the `location.ancestorOrigins` prop. // We'll just have to deal with a simple check and an error msg on WebKit // browsers in this case.
cache.push([w, is_same_origin(w)]);
} this.window_cache = cache;
}
WindowTestEnvironment.prototype.setup_messages = function(new_events) { var this_obj = this;
forEach(settings.message_events, function(x) { var current_dispatch = this_obj.message_events.includes(x); var new_dispatch = new_events.includes(x); if (!current_dispatch && new_dispatch) {
this_obj.message_functions[x][0](this_obj.message_functions[x][2]);
} elseif (current_dispatch && !new_dispatch) {
this_obj.message_functions[x][1](this_obj.message_functions[x][2]);
}
}); this.message_events = new_events;
}
WindowTestEnvironment.prototype.next_default_test_name = function() { //Don't use document.title to work around an Opera bug in XHTML documents var title = document.getElementsByTagName("title")[0]; var prefix = (title && title.firstChild && title.firstChild.data) || "Untitled"; var suffix = this.name_counter > 0 ? " " + this.name_counter : ""; this.name_counter++; return prefix + suffix;
};
/* * Base TestEnvironment implementation for a generic web worker. * * Workers accumulate test results. One or more clients can connect and * retrieve results from a worker at any time. * * WorkerTestEnvironment supports communicating with a client via a * MessagePort. The mechanism for determining the appropriate MessagePort * for communicating with a client depends on the type of worker and is * implemented by the various specializations of WorkerTestEnvironment * below. * * A client document using testharness can use fetch_tests_from_worker() to * retrieve results from a worker. See apisample16.html.
*/ function WorkerTestEnvironment() { this.name_counter = 0; this.all_loaded = true; this.message_list = []; this.message_ports = [];
}
WorkerTestEnvironment.prototype._dispatch = function(message) { this.message_list.push(message); for (var i = 0; i < this.message_ports.length; ++i)
{ this.message_ports[i].postMessage(message);
}
};
// The only requirement is that port has a postMessage() method. It doesn't // have to be an instance of a MessagePort, and often isn't.
WorkerTestEnvironment.prototype._add_message_port = function(port) { this.message_ports.push(port); for (var i = 0; i < this.message_list.length; ++i)
{
port.postMessage(this.message_list[i]);
}
};
WorkerTestEnvironment.prototype.test_timeout = function() { // Tests running in a worker don't have a default timeout. I.e. all // worker tests behave as if settings.explicit_timeout is true. returnnull;
};
/* * Dedicated web workers. * https://html.spec.whatwg.org/multipage/workers.html#dedicatedworkerglobalscope * * This class is used as the test_environment when testharness is running * inside a dedicated worker.
*/ function DedicatedWorkerTestEnvironment() {
WorkerTestEnvironment.call(this); // self is an instance of DedicatedWorkerGlobalScope which exposes // a postMessage() method for communicating via the message channel // established when the worker is created. this._add_message_port(self);
}
DedicatedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);
DedicatedWorkerTestEnvironment.prototype.on_tests_ready = function() {
WorkerTestEnvironment.prototype.on_tests_ready.call(this); // In the absence of an onload notification, we a require dedicated // workers to explicitly signal when the tests are done.
tests.wait_for_finish = true;
};
/* * Shared web workers. * https://html.spec.whatwg.org/multipage/workers.html#sharedworkerglobalscope * * This class is used as the test_environment when testharness is running * inside a shared web worker.
*/ function SharedWorkerTestEnvironment() {
WorkerTestEnvironment.call(this); var this_obj = this; // Shared workers receive message ports via the 'onconnect' event for // each connection.
self.addEventListener("connect", function(message_event) {
this_obj._add_message_port(message_event.source);
});
}
SharedWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);
SharedWorkerTestEnvironment.prototype.on_tests_ready = function() {
WorkerTestEnvironment.prototype.on_tests_ready.call(this); // In the absence of an onload notification, we a require shared // workers to explicitly signal when the tests are done.
tests.wait_for_finish = true;
};
/* * Service workers. * http://www.w3.org/TR/service-workers/ * * This class is used as the test_environment when testharness is running * inside a service worker.
*/ function ServiceWorkerTestEnvironment() {
WorkerTestEnvironment.call(this); this.all_loaded = false; this.on_loaded_callback = null; var this_obj = this;
self.addEventListener("message", function(event) { if (event.data.type && event.data.type === "connect") { if (event.ports && event.ports[0]) { // If a MessageChannel was passed, then use it to // send results back to the main window. This // allows the tests to work even if the browser // does not fully support MessageEvent.source in // ServiceWorkers yet.
this_obj._add_message_port(event.ports[0]);
event.ports[0].start();
} else { // If there is no MessageChannel, then attempt to // use the MessageEvent.source to send results // back to the main window.
this_obj._add_message_port(event.source);
}
}
});
// The oninstall event is received after the service worker script and // all imported scripts have been fetched and executed. It's the // equivalent of an onload event for a document. All tests should have // been added by the time this event is received, thus it's not // necessary to wait until the onactivate event.
on_event(self, "install", function(event) {
this_obj.all_loaded = true; if (this_obj.on_loaded_callback) {
this_obj.on_loaded_callback();
}
});
}
ServiceWorkerTestEnvironment.prototype = Object.create(WorkerTestEnvironment.prototype);
function create_test_environment() { if ('document' in self) { returnnew WindowTestEnvironment();
} if ('DedicatedWorkerGlobalScope' in self &&
self instanceof DedicatedWorkerGlobalScope) { returnnew DedicatedWorkerTestEnvironment();
} if ('SharedWorkerGlobalScope' in self &&
self instanceof SharedWorkerGlobalScope) { returnnew SharedWorkerTestEnvironment();
} if ('ServiceWorkerGlobalScope' in self &&
self instanceof ServiceWorkerGlobalScope) { returnnew ServiceWorkerTestEnvironment();
} thrownew Error("Unsupported test environment");
}
var test_environment = create_test_environment();
function is_shared_worker(worker) { return'SharedWorker' in self && worker instanceof SharedWorker;
}
function is_service_worker(worker) { return'ServiceWorker' in self && worker instanceof ServiceWorker;
}
/* * API functions
*/
function test(func, name, properties)
{ var test_name = name ? name : test_environment.next_default_test_name();
properties = properties ? properties : {}; var test_obj = new Test(test_name, properties);
test_obj.step(func, test_obj, test_obj); if (test_obj.phase === test_obj.phases.STARTED) {
test_obj.done();
}
}
function async_test(func, name, properties)
{ if (typeof func !== "function") {
properties = name;
name = func;
func = null;
} var test_name = name ? name : test_environment.next_default_test_name();
properties = properties ? properties : {}; var test_obj = new Test(test_name, properties); if (func) {
test_obj.step(func, test_obj, test_obj);
} return test_obj;
}
function promise_test(func, name, properties) { var test = async_test(name, properties); // If there is no promise tests queue make one.
test.step(function() { if (!tests.promise_tests) {
tests.promise_tests = Promise.resolve();
}
});
tests.promise_tests = tests.promise_tests.then(function() { return Promise.resolve(test.step(func, test, test))
.then( function() {
test.done();
})
.catch(test.step_func( function(value) { if (value instanceof AssertionError) { throw value;
} assert(false, "promise_test", null, "Unhandled rejection with value: ${value}", {value:value});
}));
});
}
function promise_rejects(test, expected, promise) { return promise.then(test.unreached_func("Should have rejected.")).catch(function(e) {
assert_throws(expected, function() { throw e });
});
}
/** * This constructor helper allows DOM events to be handled using Promises, * which can make it a lot easier to test a very specific series of events, * including ensuring that unexpected events are not fired at any point.
*/ function EventWatcher(test, watchedNode, eventTypes)
{ if (typeof eventTypes == 'string') {
eventTypes = [eventTypes];
}
var waitingFor = null;
var eventHandler = test.step_func(function(evt) {
assert_true(!!waitingFor, 'Not expecting event, but got ' + evt.type + ' event');
assert_equals(evt.type, waitingFor.types[0], 'Expected ' + waitingFor.types[0] + ' event, but got ' +
evt.type + ' event instead'); if (waitingFor.types.length > 1) { // Pop first event from array
waitingFor.types.shift(); return;
} // We need to null out waitingFor before calling the resolve function // since the Promise's resolve handlers may call wait_for() which will // need to set waitingFor. var resolveFunc = waitingFor.resolve;
waitingFor = null;
resolveFunc(evt);
});
for (var i = 0; i < eventTypes.length; i++) {
watchedNode.addEventListener(eventTypes[i], eventHandler);
}
/** * Returns a Promise that will resolve after the specified event or * series of events has occured.
*/ this.wait_for = function(types) { if (waitingFor) { return Promise.reject('Already waiting for an event or events');
} if (typeof types == 'string') {
types = [types];
} returnnew Promise(function(resolve, reject) {
waitingFor = {
types: types,
resolve: resolve,
reject: reject
};
});
};
function stop_watching() { for (var i = 0; i < eventTypes.length; i++) {
watchedNode.removeEventListener(eventTypes[i], eventHandler);
}
};
/* * Return a string truncated to the given length, with ... added at the end * if it was longer.
*/ function truncate(s, len)
{ if (s.length > len) { return s.substring(0, len - 3) + "...";
} return s;
}
/* * Return true if object is probably a Node object.
*/ function is_node(object)
{ // I use duck-typing instead of instanceof, because // instanceof doesn't work if the node is from another window (like an // iframe's contentWindow): // http://www.w3.org/Bugs/Public/show_bug.cgi?id=12295 if ("nodeType" in object && "nodeName" in object && "nodeValue" in object && "childNodes" in object) { try {
object.nodeType;
} catch (e) { // The object is probably Node.prototype or another prototype // object that inherits from it, and not a Node instance. returnfalse;
} returntrue;
} returnfalse;
}
/* * Convert a value to a nice, human-readable string
*/ function format_value(val, seen)
{ if (!seen) {
seen = [];
} if (typeof val === "object" && val !== null) { if (seen.includes(val)) { return"[...]";
}
seen.push(val);
} if (Array.isArray(val)) { return"[" + val.map(function(x) {return format_value(x, seen);}).join(", ") + "]";
}
switch (typeof val) { case"string":
val = val.replace("\\", "\\\\"); for (var i = 0; i < 32; i++) { var replace = "\\"; switch (i) { case 0: replace += "0"; break; case 1: replace += "x01"; break; case 2: replace += "x02"; break; case 3: replace += "x03"; break; case 4: replace += "x04"; break; case 5: replace += "x05"; break; case 6: replace += "x06"; break; case 7: replace += "x07"; break; case 8: replace += "b"; break; case 9: replace += "t"; break; case 10: replace += "n"; break; case 11: replace += "v"; break; case 12: replace += "f"; break; case 13: replace += "r"; break; case 14: replace += "x0e"; break; case 15: replace += "x0f"; break; case 16: replace += "x10"; break; case 17: replace += "x11"; break; case 18: replace += "x12"; break; case 19: replace += "x13"; break; case 20: replace += "x14"; break; case 21: replace += "x15"; break; case 22: replace += "x16"; break; case 23: replace += "x17"; break; case 24: replace += "x18"; break; case 25: replace += "x19"; break; case 26: replace += "x1a"; break; case 27: replace += "x1b"; break; case 28: replace += "x1c"; break; case 29: replace += "x1d"; break; case 30: replace += "x1e"; break; case 31: replace += "x1f"; break;
}
val = val.replace(RegExp(String.fromCharCode(i), "g"), replace);
} return'"' + val.replace(/"/g, '\\"') + '"'; case"boolean": case"undefined": return String(val); case"number": // In JavaScript, -0 === 0 and String(-0) == "0", so we have to // special-case. if (val === -0 && 1/val === -Infinity) { return"-0";
} return String(val); case"object": if (val === null) { return"null";
}
// Special-case Node objects, since those come up a lot in my tests. I // ignore namespaces. if (is_node(val)) { switch (val.nodeType) { case Node.ELEMENT_NODE: var ret = "<" + val.localName; for (var i = 0; i < val.attributes.length; i++) {
ret += " " + val.attributes[i].name + '="' + val.attributes[i].value + '"';
}
ret += ">" + val.innerHTML + "" + val.localName + ">"; return"Element node " + truncate(ret, 60); case Node.TEXT_NODE: return'Text node "' + truncate(val.data, 60) + '"'; case Node.PROCESSING_INSTRUCTION_NODE: return"ProcessingInstruction node with target " + format_value(truncate(val.target, 60)) + " and data " + format_value(truncate(val.data, 60)); case Node.COMMENT_NODE: return"Comment node "; case Node.DOCUMENT_NODE: return"Document node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children"); case Node.DOCUMENT_TYPE_NODE: return"DocumentType node"; case Node.DOCUMENT_FRAGMENT_NODE: return"DocumentFragment node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children"); default: return"Node object of unknown type";
}
}
/* falls through */ default: returntypeof val + ' "' + truncate(String(val), 60) + '"';
}
}
expose(format_value, "format_value");
function same_value(x, y) { if (y !== y) { //NaN case return x !== x;
} if (x === 0 && y === 0) { //Distinguish +0 and -0 return 1/x === 1/y;
} return x === y;
}
function assert_equals(actual, expected, description)
{ /* * Test if two primitives are equal or two objects * are the same object
*/ if (typeof actual != typeof expected) { assert(false, "assert_equals", description, "expected (" + typeof expected + ") ${expected} but got (" + typeof actual + ") ${actual}",
{expected:expected, actual:actual}); return;
} assert(same_value(actual, expected), "assert_equals", description, "expected ${expected} but got ${actual}",
{expected:expected, actual:actual});
}
expose(assert_equals, "assert_equals");
function assert_not_equals(actual, expected, description)
{ /* * Test if two primitives are unequal or two objects * are different objects
*/ assert(!same_value(actual, expected), "assert_not_equals", description, "got disallowed value ${actual}",
{actual:actual});
}
expose(assert_not_equals, "assert_not_equals");
function assert_in_array(actual, expected, description)
{ assert(expected.includes(actual), "assert_in_array", description, "value ${actual} not in array ${expected}",
{actual:actual, expected:expected});
}
expose(assert_in_array, "assert_in_array");
function assert_object_equals(actual, expected, description)
{ //This needs to be improved a great deal function check_equal(actual, expected, stack)
{
stack.push(actual);
var p; for (p in actual) { assert(expected.hasOwnProperty(p), "assert_object_equals", description, "unexpected property ${p}", {p:p});
for (var i = 0; i < actual.length; i++) { assert(actual.hasOwnProperty(i) === expected.hasOwnProperty(i), "assert_array_equals", description, "property ${i}, property expected to be ${expected} but was ${actual}",
{i:i, expected:expected.hasOwnProperty(i) ? "present" : "missing",
actual:actual.hasOwnProperty(i) ? "present" : "missing"}); assert(same_value(expected[i], actual[i]), "assert_array_equals", description, "property ${i}, expected ${expected} but got ${actual}",
{i:i, expected:expected[i], actual:actual[i]});
}
}
expose(assert_array_equals, "assert_array_equals");
function assert_approx_equals(actual, expected, epsilon, description)
{ /* * Test if two primitive numbers are equal withing +/- epsilon
*/ assert(typeof actual === "number", "assert_approx_equals", description, "expected a number but got a ${type_actual}",
{type_actual:typeof actual});
function assert_less_than(actual, expected, description)
{ /* * Test if a primitive number is less than another
*/ assert(typeof actual === "number", "assert_less_than", description, "expected a number but got a ${type_actual}",
{type_actual:typeof actual});
assert(actual < expected, "assert_less_than", description, "expected a number less than ${expected} but got ${actual}",
{expected:expected, actual:actual});
}
expose(assert_less_than, "assert_less_than");
function assert_greater_than(actual, expected, description)
{ /* * Test if a primitive number is greater than another
*/ assert(typeof actual === "number", "assert_greater_than", description, "expected a number but got a ${type_actual}",
{type_actual:typeof actual});
assert(actual > expected, "assert_greater_than", description, "expected a number greater than ${expected} but got ${actual}",
{expected:expected, actual:actual});
}
expose(assert_greater_than, "assert_greater_than");
function assert_between_exclusive(actual, lower, upper, description)
{ /* * Test if a primitive number is between two others
*/ assert(typeof actual === "number", "assert_between_exclusive", description, "expected a number but got a ${type_actual}",
{type_actual:typeof actual});
assert(actual > lower && actual < upper, "assert_between_exclusive", description, "expected a number greater than ${lower} " + "and less than ${upper} but got ${actual}",
{lower:lower, upper:upper, actual:actual});
}
expose(assert_between_exclusive, "assert_between_exclusive");
function assert_less_than_equal(actual, expected, description)
{ /* * Test if a primitive number is less than or equal to another
*/ assert(typeof actual === "number", "assert_less_than_equal", description, "expected a number but got a ${type_actual}",
{type_actual:typeof actual});
assert(actual <= expected, "assert_less_than_equal", description, "expected a number less than or equal to ${expected} but got ${actual}",
{expected:expected, actual:actual});
}
expose(assert_less_than_equal, "assert_less_than_equal");
function assert_greater_than_equal(actual, expected, description)
{ /* * Test if a primitive number is greater than or equal to another
*/ assert(typeof actual === "number", "assert_greater_than_equal", description, "expected a number but got a ${type_actual}",
{type_actual:typeof actual});
assert(actual >= expected, "assert_greater_than_equal", description, "expected a number greater than or equal to ${expected} but got ${actual}",
{expected:expected, actual:actual});
}
expose(assert_greater_than_equal, "assert_greater_than_equal");
function assert_between_inclusive(actual, lower, upper, description)
{ /* * Test if a primitive number is between to two others or equal to either of them
*/ assert(typeof actual === "number", "assert_between_inclusive", description, "expected a number but got a ${type_actual}",
{type_actual:typeof actual});
assert(actual >= lower && actual <= upper, "assert_between_inclusive", description, "expected a number greater than or equal to ${lower} " + "and less than or equal to ${upper} but got ${actual}",
{lower:lower, upper:upper, actual:actual});
}
expose(assert_between_inclusive, "assert_between_inclusive");
function assert_regexp_match(actual, expected, description) { /* * Test if a string (actual) matches a regexp (expected)
*/ assert(expected.test(actual), "assert_regexp_match", description, "expected ${expected} but got ${actual}",
{expected:expected, actual:actual});
}
expose(assert_regexp_match, "assert_regexp_match");
function _assert_inherits(name) { returnfunction (object, property_name, description)
{ assert(typeof object === "object",
name, description, "provided value is not an object");
assert("hasOwnProperty" in object,
name, description, "provided value is an object but has no hasOwnProperty method");
assert(!object.hasOwnProperty(property_name),
name, description, "property ${p} found on object expected in prototype chain",
{p:property_name});
assert(property_name in object,
name, description, "property ${p} not found in prototype chain",
{p:property_name});
};
}
expose(_assert_inherits("assert_inherits"), "assert_inherits");
expose(_assert_inherits("assert_idl_attribute"), "assert_idl_attribute");
function assert_readonly(object, property_name, description)
{ var initial_value = object[property_name]; try { //Note that this can have side effects in the case where //the property has PutForwards
object[property_name] = initial_value + "a"; //XXX use some other value here? assert(same_value(object[property_name], initial_value), "assert_readonly", description, "changing property ${p} succeeded",
{p:property_name});
} finally {
object[property_name] = initial_value;
}
}
expose(assert_readonly, "assert_readonly");
function assert_throws(code, func, description)
{ try {
func.call(this); assert(false, "assert_throws", description, "${func} did not throw", {func:func});
} catch (e) { if (e instanceof AssertionError) { throw e;
} if (code === null) { return;
} if (typeof code === "object") { assert(typeof e == "object" && "name" in e && e.name == code.name, "assert_throws", description, "${func} threw ${actual} (${actual_name}) expected ${expected} (${expected_name})",
{func:func, actual:e, actual_name:e.name,
expected:code,
expected_name:code.name}); return;
}
if (!(name in name_code_map)) { thrownew AssertionError('Test bug: unrecognized DOMException code "' + code + '" passed to assert_throws()');
}
var required_props = { code: name_code_map[name] };
if (required_props.code === 0 ||
(typeof e == "object" && "name" in e &&
e.name !== e.name.toUpperCase() &&
e.name !== "DOMException")) { // New style exception: also test the name property.
required_props.name = name;
}
//We'd like to test that e instanceof the appropriate interface, //but we can't, because we don't know what window it was created //in. It might be an instanceof the appropriate interface on some //unknown other window. TODO: Work around this somehow?
assert(typeof e == "object", "assert_throws", description, "${func} threw ${e} with type ${type}, not an object",
{func:func, e:e, type:typeof e});
for (var prop in required_props) { assert(typeof e == "object" && prop in e && e[prop] == required_props[prop], "assert_throws", description, "${func} threw ${e} that is not a DOMException " + code + ": property ${prop} is equal to ${actual}, expected ${expected}",
{func:func, e:e, prop:prop, actual:e[prop], expected:required_props[prop]});
}
}
}
expose(assert_throws, "assert_throws");
function assert_any(assert_func, actual, expected_array)
{ var args = [].slice.call(arguments, 3); var errors = []; var passed = false;
forEach(expected_array, function(expected)
{ try {
assert_func.apply(this, [actual, expected].concat(args));
passed = true;
} catch (e) {
errors.push(e.message);
}
}); if (!passed) { thrownew AssertionError(errors.join("\n\n"));
}
}
expose(assert_any, "assert_any");
function Test(name, properties)
{ if (tests.file_is_test && tests.tests.length) { thrownew Error("Tried to create a test with file_is_test");
} this.name = name;
Test.prototype.step = function(func, this_obj)
{ if (this.phase > this.phases.STARTED) { return;
} this.phase = this.phases.STARTED; //If we don't get a result before the harness times out that will be a test timout this.set_status(this.TIMEOUT, "Test timed out");
/* * A RemoteTest object mirrors a Test object on a remote worker. The * associated RemoteWorker updates the RemoteTest object in response to * received events. In turn, the RemoteTest object replicates these events * on the local document. This allows listeners (test result reporting * etc..) to transparently handle local and remote events.
*/ function RemoteTest(clone) { var this_obj = this;
Object.keys(clone).forEach( function(key) {
this_obj[key] = clone[key];
}); this.index = null; this.phase = this.phases.INITIAL; this.update_state_from(clone);
tests.push(this);
}
/* * A RemoteWorker listens for test events from a worker. These events are * then used to construct and maintain RemoteTest objects that mirror the * tests running on the remote worker.
*/ function RemoteWorker(worker) { this.running = true; this.tests = new Array();
var this_obj = this;
worker.onerror = function(error) { this_obj.worker_error(error); };
var message_port;
if (is_service_worker(worker)) { if (window.MessageChannel) { // The ServiceWorker's implicit MessagePort is currently not // reliably accessible from the ServiceWorkerGlobalScope due to // Blink setting MessageEvent.source to null for messages sent // via ServiceWorker.postMessage(). Until that's resolved, // create an explicit MessageChannel and pass one end to the // worker. var message_channel = new MessageChannel();
message_port = message_channel.port1;
message_port.start();
worker.postMessage({type: "connect"}, [message_channel.port2]);
} else { // If MessageChannel is not available, then try the // ServiceWorker.postMessage() approach using MessageEvent.source // on the other end.
message_port = navigator.serviceWorker;
worker.postMessage({type: "connect"});
}
} elseif (is_shared_worker(worker)) {
message_port = worker.port;
} else {
message_port = worker;
}
// Keeping a reference to the worker until worker_done() is seen // prevents the Worker object and its MessageChannel from going away // before all the messages are dispatched. this.worker = worker;
message_port.onmessage = function(message) { if (this_obj.running && (message.data.type in this_obj.message_handlers)) {
this_obj.message_handlers[message.data.type].call(this_obj, message.data);
}
};
}
RemoteWorker.prototype.worker_error = function(error) { var message = error.message || String(error); var filename = (error.filename ? " " + error.filename: ""); // FIXME: Display worker error states separately from main document // error state. this.worker_done({
status: {
status: tests.status.ERROR,
message: "Error in worker" + filename + ": " + message,
stack: error.stack
}
});
error.preventDefault();
};
RemoteWorker.prototype.test_state = function(data) { var remote_test = this.tests[data.test.index]; if (!remote_test) {
remote_test = new RemoteTest(data.test); this.tests[data.test.index] = remote_test;
}
remote_test.update_state_from(data.test);
tests.notify_test_state(remote_test);
};
Tests.prototype.set_file_is_test = function() { if (this.tests.length > 0) { thrownew Error("Tried to set file as test after creating a test");
} this.wait_for_finish = true; this.file_is_test = true; // Create the test, which will add it to the list of tests
async_test();
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.