Bug 885878 - Update testharness.js; r=jgraham

This commit is contained in:
Ms2ger 2013-11-28 15:07:56 +01:00
Родитель 328c960528
Коммит fe5d5ddd33
3 изменённых файлов: 264 добавлений и 132 удалений

Просмотреть файл

@ -1,6 +1,7 @@
{
"Selection interface: existence and properties of interface object":true,
"Selection interface: existence and properties of interface prototype object":true,
"Selection interface object length":true,
"Selection interface: existence and properties of interface prototype object's \"constructor\" property":true,
"Selection interface: attribute anchorNode":true,
"Selection interface: attribute anchorOffset":true,

Просмотреть файл

@ -14,7 +14,7 @@ policies and contribution forms [3].
*
* <script src=/resources/testharness.js></script>
* <script src=/resources/testharnessreport.js></script>
* <script src=/resources/webidl2.js></script>
* <script src=/resources/WebIDLParser.js></script>
* <script src=/resources/idlharness.js></script>
*
* Then you'll need some type of IDLs. Here's some script that can be run on a
@ -304,8 +304,7 @@ IdlArray.prototype.internal_add_idls = function(parsed_idls)
break;
case "typedef":
// TODO
console.log("typedef not yet supported");
this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);
break;
case "callback":
@ -317,8 +316,9 @@ IdlArray.prototype.internal_add_idls = function(parsed_idls)
this.members[parsed_idl.name] = new IdlEnum(parsed_idl);
break;
case "callback":
case "callback interface":
// TODO
console.log("callback interface not yet supported");
break;
default:
@ -543,6 +543,8 @@ IdlArray.prototype.assert_type_is = function(value, type)
case "float":
case "double":
case "unrestricted float":
case "unrestricted double":
// TODO: distinguish these cases
assert_equals(typeof value, "number");
return;
@ -584,6 +586,10 @@ IdlArray.prototype.assert_type_is = function(value, type)
{
// TODO: Test when we actually have something to test this on
}
else if (this.members[type] instanceof IdlTypedef)
{
// TODO: Test when we actually have something to test this on
}
else
{
throw "Type " + type + " isn't an interface or dictionary";
@ -1142,41 +1148,46 @@ IdlInterface.prototype.test_self = function()
}
}.bind(this), this.name + " interface: existence and properties of interface object");
if (this.has_extended_attribute("Constructor"))
{
test(function()
{
if (!this.is_callback()) {
test(function() {
// This function tests WebIDL as of 2013-08-25.
// http://dev.w3.org/2006/webapi/WebIDL/#es-interface-call
assert_own_property(window, this.name,
"window does not have own property " + format_value(this.name));
// "Interface objects for interfaces declared with a [Constructor]
// extended attribute must have a property named “length” with
// attributes { [[Writable]]: false, [[Enumerable]]: false,
// [[Configurable]]: false } whose value is a Number determined as
// follows: . . .
// "Return the length of the shortest argument list of the entries
// in S."
// TODO: Variadic constructors. Should generalize this so that it
// works for testing operation length too (currently we just don't
// support multiple operations with the same identifier).
var expected_length = this.extAttrs
.filter(function(attr) { return attr.name == "Constructor"; })
.map(function(attr) {
return attr.arguments ? attr.arguments.filter(
function(arg) {
return !arg.optional;
}).length : 0;
})
.reduce(function(m, n) { return Math.min(m, n); });
// "Interface objects for non-callback interfaces MUST have a
// property named “length” with attributes { [[Writable]]: false,
// [[Enumerable]]: false, [[Configurable]]: false } whose value is
// a Number."
assert_own_property(window[this.name], "length");
assert_equals(window[this.name].length, expected_length, "wrong value for " + this.name + ".length");
var desc = Object.getOwnPropertyDescriptor(window[this.name], "length");
assert_false("get" in desc, this.name + ".length has getter");
assert_false("set" in desc, this.name + ".length has setter");
assert_false(desc.writable, this.name + ".length is writable");
assert_false(desc.enumerable, this.name + ".length is enumerable");
assert_false(desc.configurable, this.name + ".length is configurable");
}.bind(this), this.name + " interface constructor");
var constructors = this.extAttrs
.filter(function(attr) { return attr.name == "Constructor"; });
var expected_length;
if (!constructors.length) {
// "If the [Constructor] extended attribute, does not appear on
// the interface definition, then the value is 0."
expected_length = 0;
} else {
// "Otherwise, the value is determined as follows: . . .
// "Return the length of the shortest argument list of the
// entries in S."
expected_length = constructors.map(function(attr) {
return attr.arguments ? attr.arguments.filter(function(arg) {
return !arg.optional;
}).length : 0;
})
.reduce(function(m, n) { return Math.min(m, n); });
}
assert_equals(window[this.name].length, expected_length, "wrong value for " + this.name + ".length");
}.bind(this), this.name + " interface object length");
}
// TODO: Test named constructors if I find any interfaces that have them.
@ -1377,16 +1388,24 @@ IdlInterface.prototype.test_members = function()
"window does not have own property " + format_value(this.name));
assert_own_property(window[this.name], "prototype",
'interface "' + this.name + '" does not have own property "prototype"');
assert_true(member.name in window[this.name].prototype,
"The prototype object must have a property " +
format_value(member.name));
// TODO: Needs to test for LenientThis.
assert_throws(new TypeError(), function() {
window[this.name].prototype[member.name];
}.bind(this), "getting property on prototype object must throw TypeError");
if (member["static"]) {
assert_own_property(window[this.name], member.name,
"The interface object must have a property " +
format_value(member.name));
}
else
{
assert_true(member.name in window[this.name].prototype,
"The prototype object must have a property " +
format_value(member.name));
do_interface_attribute_asserts(window[this.name].prototype, member);
// TODO: Needs to test for LenientThis.
assert_throws(new TypeError(), function() {
window[this.name].prototype[member.name];
}.bind(this), "getting property on prototype object must throw TypeError");
do_interface_attribute_asserts(window[this.name].prototype, member);
}
}.bind(this), this.name + " interface: attribute " + member.name);
}
else if (member.type == "operation")
@ -1420,11 +1439,20 @@ IdlInterface.prototype.test_members = function()
// and with an argument count of 0 (for the ECMAScript language
// binding) has no entries."
//
// TODO: The library doesn't seem to support static operations.
assert_own_property(window[this.name].prototype, member.name,
"interface prototype object missing non-static operation");
var prototypeOrInterfaceObject;
if (member["static"]) {
assert_own_property(window[this.name], member.name,
"interface prototype object missing static operation");
prototypeOrInterfaceObject = window[this.name];
}
else
{
assert_own_property(window[this.name].prototype, member.name,
"interface prototype object missing non-static operation");
prototypeOrInterfaceObject = window[this.name].prototype;
}
var desc = Object.getOwnPropertyDescriptor(window[this.name].prototype, member.name);
var desc = Object.getOwnPropertyDescriptor(prototypeOrInterfaceObject, member.name);
// "The property has attributes { [[Writable]]: true,
// [[Enumerable]]: true, [[Configurable]]: true }."
assert_false("get" in desc, "property has getter");
@ -1434,7 +1462,7 @@ IdlInterface.prototype.test_members = function()
assert_true(desc.configurable, "property is not configurable");
// "The value of the property is a Function object whose
// behavior is as follows . . ."
assert_equals(typeof window[this.name].prototype[member.name], "function",
assert_equals(typeof prototypeOrInterfaceObject[member.name], "function",
"property must be a function");
// "The value of the Function objects “length” property is
// a Number determined as follows:
@ -1443,7 +1471,7 @@ IdlInterface.prototype.test_members = function()
// entries in S."
//
// TODO: Doesn't handle overloading or variadic arguments.
assert_equals(window[this.name].prototype[member.name].length,
assert_equals(prototypeOrInterfaceObject[member.name].length,
member.arguments.filter(function(arg) {
return !arg.optional;
}).length,
@ -1460,11 +1488,12 @@ IdlInterface.prototype.test_members = function()
// This should be hit if the operation is not static, there is
// no [ImplicitThis] attribute, and the this value is null.
//
// TODO: We currently ignore the static and [ImplicitThis]
// cases.
assert_throws(new TypeError(), function() {
window[this.name].prototype[member.name].apply(null, args);
}, "calling operation with this = null didn't throw TypeError");
// TODO: We currently ignore the [ImplicitThis] case.
if (!member["static"]) {
assert_throws(new TypeError(), function() {
window[this.name].prototype[member.name].apply(null, args);
}, "calling operation with this = null didn't throw TypeError");
}
// ". . . If O is not null and is also not a platform object
// that implements interface I, throw a TypeError."
@ -1596,34 +1625,36 @@ IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expect
{
assert_equals(exception, null, "Unexpected exception when evaluating object");
assert_equals(typeof obj, expected_typeof, "wrong typeof object");
assert_inherits(obj, member.name);
if (member.type == "const")
{
assert_equals(obj[member.name], constValue(member.value));
}
if (member.type == "attribute")
{
// Attributes are accessor properties, so they might
// legitimately throw an exception rather than returning
// anything.
var property, thrown = false;
try
if (!member["static"]) {
assert_inherits(obj, member.name);
if (member.type == "const")
{
property = obj[member.name];
assert_equals(obj[member.name], constValue(member.value));
}
catch (e)
if (member.type == "attribute")
{
thrown = true;
// Attributes are accessor properties, so they might
// legitimately throw an exception rather than returning
// anything.
var property, thrown = false;
try
{
property = obj[member.name];
}
catch (e)
{
thrown = true;
}
if (!thrown)
{
this.array.assert_type_is(property, member.idlType);
}
}
if (!thrown)
if (member.type == "operation")
{
this.array.assert_type_is(property, member.idlType);
assert_equals(typeof obj[member.name], "function");
}
}
if (member.type == "operation")
{
assert_equals(typeof obj[member.name], "function");
}
}.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member.name + '" with the proper type (' + i + ')');
}
// TODO: This is wrong if there are multiple operations with the same
@ -1635,7 +1666,13 @@ IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expect
{
assert_equals(exception, null, "Unexpected exception when evaluating object");
assert_equals(typeof obj, expected_typeof, "wrong typeof object");
assert_inherits(obj, member.name);
if (!member["static"]) {
assert_inherits(obj, member.name);
}
else
{
assert_false(member.name in obj);
}
var args = [];
for (var i = 0; i < member.arguments.length; i++)
{
@ -1785,6 +1822,7 @@ function create_suitable_object(type)
case "byte": case "octet": case "short": case "unsigned short":
case "long": case "unsigned long": case "long long":
case "unsigned long long": case "float": case "double":
case "unrestricted float": case "unrestricted double":
return 7;
case "DOMString":
@ -1813,8 +1851,6 @@ function IdlEnum(obj)
/** Self-explanatory. */
this.name = obj.name;
console.log("Name is " + this.name);
/** An array of values produced by the "enum" production. */
this.values = obj.values;
@ -1823,15 +1859,26 @@ function IdlEnum(obj)
IdlEnum.prototype = Object.create(IdlObject.prototype);
IdlEnum.prototype.test = function()
/// IdlTypedef ///
// Used for IdlArray.prototype.assert_type_is
function IdlTypedef(obj)
//@{
{
test(function()
{
// NOTHING to test
return;
});
/**
* obj is an object produced by the WebIDLParser.js "typedef"
* production.
*/
/** Self-explanatory. */
this.name = obj.name;
/** An array of values produced by the "typedef" production. */
this.values = obj.values;
}
//@}
IdlTypedef.prototype = Object.create(IdlObject.prototype);
}());
// vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker:

Просмотреть файл

@ -133,6 +133,18 @@ policies and contribution forms [3].
* asserts outside these places won't be detected correctly by the harness
* and may cause a file to stop testing.
*
* == Harness Timeout ==
*
* The overall harness admits two timeout values "normal" (the
* default) and "long", used for tests which have an unusually long
* runtime. After the timeout is reached, the harness will stop
* waiting for further async tests to complete. By default the
* timeouts are set to 10s and 60s, respectively, but may be changed
* when the test is run on hardware with different performance
* characteristics to a common desktop computer. In order to opt-in
* to the longer test timeout, the test must specify a meta element:
* <meta name="timeout" content="long">
*
* == Setup ==
*
* Sometimes tests require non-trivial setup that may fail. For this purpose
@ -146,9 +158,6 @@ policies and contribution forms [3].
* any tests have returned results. Properties are global properties of the test
* harness. Currently recognised properties are:
*
* timeout - The time in ms after which the harness should stop waiting for
* tests to complete (this is different to the per-test timeout
* because async tests do not start their timer until .step is called)
*
* explicit_done - Wait for an explicit call to done() before declaring all
* tests complete (see below)
@ -167,6 +176,8 @@ policies and contribution forms [3].
* needed when e.g. testing the window.onerror
* handler.
*
* timeout_multiplier - Multiplier to apply to per-test timeouts.
*
* == Determining when all tests are complete ==
*
* By default the test harness will assume there are no more results to come
@ -359,11 +370,12 @@ policies and contribution forms [3].
(function ()
{
var debug = false;
// default timeout is 5 seconds, test can override if needed
// default timeout is 10 seconds, test can override if needed
var settings = {
output:true,
timeout:5000,
test_timeout:2000
harness_timeout:{"normal":10000,
"long":60000},
test_timeout:null
};
var xhtml_ns = "http://www.w3.org/1999/xhtml";
@ -415,7 +427,7 @@ policies and contribution forms [3].
properties = properties ? properties : {};
var test_obj = new Test(test_name, properties);
test_obj.step(func);
if (test_obj.status === test_obj.NOTRUN) {
if (test_obj.phase === test_obj.phases.STARTED) {
test_obj.done();
}
}
@ -493,13 +505,42 @@ policies and contribution forms [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.
return false;
}
return true;
}
return false;
}
/*
* Convert a value to a nice, human-readable string
*/
function format_value(val, seen)
{
if (!seen) {
seen = [];
if (!seen) {
seen = [];
}
if (typeof val === "object" && val !== null)
{
@ -507,7 +548,7 @@ policies and contribution forms [3].
{
return "[...]";
}
seen.push(val);
seen.push(val);
}
if (Array.isArray(val))
{
@ -576,14 +617,8 @@ policies and contribution forms [3].
}
// Special-case Node objects, since those come up a lot in my tests. I
// ignore namespaces. 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 val
&& "nodeName" in val
&& "nodeValue" in val
&& "childNodes" in val)
// ignore namespaces.
if (is_node(val))
{
switch (val.nodeType)
{
@ -1070,12 +1105,25 @@ policies and contribution forms [3].
function Test(name, properties)
{
this.name = name;
this.phases = {
INITIAL:0,
STARTED:1,
HAS_RESULT:2,
COMPLETE:3
};
this.phase = this.phases.INITIAL;
this.status = this.NOTRUN;
this.timeout_id = null;
this.is_done = false;
this.properties = properties;
this.timeout_length = properties.timeout ? properties.timeout : settings.test_timeout;
var timeout = properties.timeout ? properties.timeout : settings.test_timeout
if (timeout != null) {
this.timeout_length = timeout * tests.timeout_multiplier;
} else {
this.timeout_length = null;
}
this.message = null;
@ -1111,15 +1159,18 @@ policies and contribution forms [3].
Test.prototype.step = function(func, this_obj)
{
//In case the test has already failed
if (this.status !== this.NOTRUN)
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");
tests.started = true;
if (this.timeout_id === null) {
if (this.timeout_id === null)
{
this.set_timeout();
}
@ -1136,25 +1187,21 @@ policies and contribution forms [3].
}
catch(e)
{
//This can happen if something called synchronously invoked another
//step
if (this.status !== this.NOTRUN)
if (this.phase >= this.phases.HAS_RESULT)
{
return;
}
this.status = this.FAIL;
this.message = (typeof e === "object" && e !== null) ? e.message : e;
var message = (typeof e === "object" && e !== null) ? e.message : e;
if (typeof e.stack != "undefined" && typeof e.message == "string") {
//Try to make it more informative for some exceptions, at least
//in Gecko and WebKit. This results in a stack dump instead of
//just errors like "Cannot read property 'parentNode' of null"
//or "root is null". Makes it a lot longer, of course.
this.message += "(stack: " + e.stack + ")";
message += "(stack: " + e.stack + ")";
}
this.set_status(this.FAIL, message);
this.phase = this.phases.HAS_RESULT;
this.done();
if (debug && e.constructor !== AssertionError) {
throw e;
}
}
};
@ -1189,36 +1236,51 @@ policies and contribution forms [3].
Array.prototype.slice.call(arguments)));
test_this.done();
};
};
}
Test.prototype.set_timeout = function()
{
var this_obj = this;
this.timeout_id = setTimeout(function()
{
this_obj.timeout();
}, this.timeout_length);
};
if (this.timeout_length !== null)
{
var this_obj = this;
this.timeout_id = setTimeout(function()
{
this_obj.timeout();
}, this.timeout_length);
}
}
Test.prototype.set_status = function(status, message)
{
this.status = status;
this.message = message;
}
Test.prototype.timeout = function()
{
this.status = this.TIMEOUT;
this.timeout_id = null;
this.message = "Test timed out";
this.set_status(this.TIMEOUT, "Test timed out")
this.phase = this.phases.HAS_RESULT;
this.done();
};
Test.prototype.done = function()
{
if (this.is_done) {
if (this.phase == this.phases.COMPLETE) {
return;
}
clearTimeout(this.timeout_id);
if (this.status === this.NOTRUN)
} else if (this.phase <= this.phases.STARTED)
{
this.status = this.PASS;
this.set_status(this.PASS, null);
}
this.is_done = true;
if (this.status == this.NOTRUN)
{
alert(this.phase);
}
this.phase = this.phases.COMPLETE;
clearTimeout(this.timeout_id);
tests.result(this);
};
@ -1278,7 +1340,8 @@ policies and contribution forms [3].
this.allow_uncaught_exception = false;
this.timeout_length = settings.timeout;
this.timeout_multiplier = 1;
this.timeout_length = this.get_timeout();
this.timeout_id = null;
this.start_callbacks = [];
@ -1320,11 +1383,7 @@ policies and contribution forms [3].
if (properties.hasOwnProperty(p))
{
var value = properties[p]
if (p == "timeout")
{
this.timeout_length = value;
}
else if (p == "allow_uncaught_exception") {
if (p == "allow_uncaught_exception") {
this.allow_uncaught_exception = value;
}
else if (p == "explicit_done" && value)
@ -1333,6 +1392,14 @@ policies and contribution forms [3].
}
else if (p == "explicit_timeout" && value) {
this.timeout_length = null;
if (this.timeout_id)
{
clearTimeout(this.timeout_id);
}
}
else if (p == "timeout_multiplier")
{
this.timeout_multiplier = value;
}
}
}
@ -1351,6 +1418,23 @@ policies and contribution forms [3].
this.set_timeout();
};
Tests.prototype.get_timeout = function()
{
var metas = document.getElementsByTagName("meta");
for (var i=0; i<metas.length; i++)
{
if (metas[i].name == "timeout")
{
if (metas[i].content == "long")
{
return settings.harness_timeout.long;
}
break;
}
}
return settings.harness_timeout.normal;
}
Tests.prototype.set_timeout = function()
{
var this_obj = this;
@ -1630,7 +1714,7 @@ policies and contribution forms [3].
if (typeof this.output_document === "function")
{
output_document = this.output_document.apply(undefined);
} else
} else
{
output_document = this.output_document;
}