Bug 1448492 [wpt PR 10164] - Special-case idlharness.js errors as an assert_throw option, a=testonly
authorLuke Bjerring <lukebjerring@users.noreply.github.com>
Mon, 09 Apr 2018 18:24:37 +0000
changeset 467210 6f3e57f64d7dcf943666ad18ce99ea0e2a2495bd
parent 467209 aa307699d85b9ef29cc23845f1ee968c1f73a15f
child 467211 11e80888afc46ec2aa2939fc7502b71da3bed517
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1448492, 10164
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1448492 [wpt PR 10164] - Special-case idlharness.js errors as an assert_throw option, a=testonly Automatic update from web-platform-testsHandle idlharness.js expected errors as an IdlArray.assert_throw option (#10164) * Handle idlharness.js expected errors as an IdlArray.assert_throw option wpt-commits: 355c2d77c7ec01ee18fd9c5917cc845a558551e8 wpt-pr: 10164 wpt-commits: 355c2d77c7ec01ee18fd9c5917cc845a558551e8 wpt-pr: 10164
testing/web-platform/tests/resources/idlharness.js
testing/web-platform/tests/resources/test/tests/idlharness/basic.html
testing/web-platform/tests/resources/testharness.js
--- a/testing/web-platform/tests/resources/idlharness.js
+++ b/testing/web-platform/tests/resources/idlharness.js
@@ -118,16 +118,38 @@ var fround =
     var arr = new Float32Array(1);
     return function fround(n) {
         arr[0] = n;
         return arr[0];
     };
 })();
 //@}
 
+/// IdlHarnessError ///
+// Entry point
+self.IdlHarnessError = function(message)
+//@{
+{
+    /**
+     * Message to be printed as the error's toString invocation.
+     */
+    this.message = message;
+};
+
+IdlHarnessError.prototype = Object.create(Error.prototype);
+
+//@}
+IdlHarnessError.prototype.toString = function()
+//@{
+{
+    return this.message;
+};
+
+//@}
+
 /// IdlArray ///
 // Entry point
 self.IdlArray = function()
 //@{
 {
     /**
      * A map from strings to the corresponding named IdlObject, such as
      * IdlInterface or IdlException.  These are the things that test() will run
@@ -212,17 +234,17 @@ IdlArray.prototype.internal_add_idls = f
      * options is a dictionary that can have an only or except member which are
      * arrays. If only is given then only members, partials and interface
      * targets listed will be added, and if except is given only those that
      * aren't listed will be added. Only one of only and except can be used.
      */
 
     if (options && options.only && options.except)
     {
-        throw "The only and except options can't be used together."
+        throw new IdlHarnessError("The only and except options can't be used together.");
     }
 
     function should_skip(name)
     {
         if (options && options.only && options.only.indexOf(name) == -1)
         {
             return true;
         }
@@ -275,17 +297,17 @@ IdlArray.prototype.internal_add_idls = f
 
         parsed_idl.array = this;
         if (should_skip(parsed_idl.name))
         {
             return;
         }
         if (parsed_idl.name in this.members)
         {
-            throw "Duplicate identifier " + parsed_idl.name;
+            throw new IdlHarnessError("Duplicate identifier " + parsed_idl.name);
         }
         switch(parsed_idl.type)
         {
         case "interface":
             this.members[parsed_idl.name] =
                 new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ false);
             break;
 
@@ -369,17 +391,17 @@ IdlArray.prototype.recursively_get_imple
     {
         return [];
     }
     for (var i = 0; i < this["implements"][interface_name].length; i++)
     {
         ret = ret.concat(this.recursively_get_implements(ret[i]));
         if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))
         {
-            throw "Circular implements statements involving " + ret[i];
+            throw new IdlHarnessError("Circular implements statements involving " + ret[i]);
         }
     }
     return ret;
 };
 
 //@}
 IdlArray.prototype.recursively_get_includes = function(interface_name)
 //@{
@@ -399,17 +421,17 @@ IdlArray.prototype.recursively_get_inclu
     {
         return [];
     }
     for (var i = 0; i < this["includes"][interface_name].length; i++)
     {
         ret = ret.concat(this.recursively_get_includes(ret[i]));
         if (ret.indexOf(ret[i]) != ret.lastIndexOf(ret[i]))
         {
-            throw "Circular includes statements involving " + ret[i];
+            throw new IdlHarnessError("Circular includes statements involving " + ret[i]);
         }
     }
     return ret;
 };
 
 //@}
 IdlArray.prototype.is_json_type = function(type)
 //@{
@@ -528,17 +550,17 @@ IdlArray.prototype.is_json_type = functi
            }
            return false;
     }
 };
 
 function exposure_set(object, default_set) {
     var exposed = object.extAttrs.filter(function(a) { return a.name == "Exposed" });
     if (exposed.length > 1 || exposed.length < 0) {
-        throw "Unexpected Exposed extended attributes on " + memberName + ": " + exposed;
+        throw new IdlHarnessError("Unexpected Exposed extended attributes on " + memberName + ": " + exposed);
     }
 
     if (exposed.length === 0) {
         return default_set;
     }
 
     var set = exposed[0].rhs.value;
     // Could be a list or a string.
@@ -562,33 +584,59 @@ function exposed_in(globals) {
         return globals.indexOf("Worker") >= 0 ||
                globals.indexOf("SharedWorker") >= 0;
     }
     if ('ServiceWorkerGlobalScope' in self &&
         self instanceof ServiceWorkerGlobalScope) {
         return globals.indexOf("Worker") >= 0 ||
                globals.indexOf("ServiceWorker") >= 0;
     }
-    throw "Unexpected global object";
+    throw new IdlHarnessError("Unexpected global object");
+}
+
+//@}
+/**
+ * Asserts that the given error message is thrown for the given function.
+ * @param {string|IdlHarnessError} error Expected Error message.
+ * @param {Function} idlArrayFunc Function operating on an IdlArray that should throw.
+ */
+IdlArray.prototype.assert_throws = function(error, idlArrayFunc)
+//@{
+{
+    try {
+        idlArrayFunc.call(this, this);
+        throw new IdlHarnessError(`${idlArrayFunc} did not throw the expected IdlHarnessError`);
+    } catch (e) {
+        if (e instanceof AssertionError) {
+            throw e;
+        }
+        // Assertions for behaviour of the idlharness.js engine.
+        if (error instanceof IdlHarnessError) {
+            error = error.message;
+        }
+        if (e.message !== error) {
+            throw new IdlHarnessError(`${idlArrayFunc} threw ${e}, not the expected IdlHarnessError`);
+        }
+    }
 }
 
 //@}
 IdlArray.prototype.test = function()
 //@{
 {
     /** Entry point.  See documentation at beginning of file. */
 
     // First merge in all the partial interfaces and implements statements we
     // encountered.
     this.partials.forEach(function(parsed_idl)
     {
         if (!(parsed_idl.name in this.members)
         || !(this.members[parsed_idl.name] instanceof IdlInterface))
         {
-            throw "Partial interface " + parsed_idl.name + " with no original interface";
+            throw new IdlHarnessError("Partial interface " + parsed_idl.name + " with no original interface");
         }
         if (parsed_idl.extAttrs)
         {
             parsed_idl.extAttrs.forEach(function(extAttr)
             {
                 this.members[parsed_idl.name].extAttrs.push(extAttr);
             }.bind(this));
         }
@@ -843,17 +891,17 @@ IdlArray.prototype.assert_type_is = func
 
         case "object":
             assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function");
             return;
     }
 
     if (!(type in this.members))
     {
-        throw "Unrecognized type " + type;
+        throw new IdlHarnessError("Unrecognized type " + type);
     }
 
     if (this.members[type] instanceof IdlInterface)
     {
         // We don't want to run the full
         // IdlInterface.prototype.test_instance_of, because that could result
         // in an infinite loop.  TODO: This means we don't have tests for
         // NoInterfaceObject interfaces, and we also can't test objects that
@@ -871,17 +919,17 @@ IdlArray.prototype.assert_type_is = func
         assert_equals(typeof value, "string");
     }
     else if (this.members[type] instanceof IdlDictionary)
     {
         // TODO: Test when we actually have something to test this on
     }
     else
     {
-        throw "Type " + type + " isn't an interface or dictionary";
+        throw new IdlHarnessError("Type " + type + " isn't an interface or dictionary");
     }
 };
 //@}
 
 /// IdlObject ///
 function IdlObject() {}
 IdlObject.prototype.test = function()
 //@{
@@ -1340,32 +1388,32 @@ IdlInterface.prototype.test_self = funct
     }
 
 
     if (this.has_extended_attribute("LegacyWindowAlias")) {
         test(function()
         {
             var aliasAttrs = this.extAttrs.filter(function(o) { return o.name === "LegacyWindowAlias"; });
             if (aliasAttrs.length > 1) {
-                throw "Invalid IDL: multiple LegacyWindowAlias extended attributes on " + this.name;
+                throw new IdlHarnessError("Invalid IDL: multiple LegacyWindowAlias extended attributes on " + this.name);
             }
             if (this.is_callback()) {
-                throw "Invalid IDL: LegacyWindowAlias extended attribute on non-interface " + this.name;
+                throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on non-interface " + this.name);
             }
             if (this.exposureSet.indexOf("Window") === -1) {
-                throw "Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " which is not exposed in Window";
+                throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " which is not exposed in Window");
             }
             // TODO: when testing of [NoInterfaceObject] interfaces is supported,
             // check that it's not specified together with LegacyWindowAlias.
 
             // TODO: maybe check that [LegacyWindowAlias] is not specified on a partial interface.
 
             var rhs = aliasAttrs[0].rhs;
             if (!rhs) {
-                throw "Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " without identifier";
+                throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " without identifier");
             }
             var aliases;
             if (rhs.type === "identifier-list") {
                 aliases = rhs.value;
             } else { // rhs.type === identifier
                 aliases = [ rhs.value ];
             }
 
@@ -1725,17 +1773,17 @@ IdlInterface.prototype.test_immutable_pr
         "should return true");
 };
 
 //@}
 IdlInterface.prototype.test_member_const = function(member)
 //@{
 {
     if (!this.has_constants()) {
-        throw "Internal error: test_member_const called without any constants";
+        throw new IdlHarnessError("Internal error: test_member_const called without any constants");
     }
 
     test(function()
     {
         assert_own_property(self, this.name,
                             "self does not have own property " + format_value(this.name));
 
         // "For each constant defined on an interface A, there must be
@@ -2283,17 +2331,17 @@ IdlInterface.prototype.test_object = fun
 
     this.test_primary_interface_of(desc, obj, exception, expected_typeof);
 
     var current_interface = this;
     while (current_interface)
     {
         if (!(current_interface.name in this.array.members))
         {
-            throw "Interface " + current_interface.name + " not found (inherited by " + this.name + ")";
+            throw new IdlHarnessError("Interface " + current_interface.name + " not found (inherited by " + this.name + ")");
         }
         if (current_interface.prevent_multiple_testing && current_interface.already_tested)
         {
             return;
         }
         current_interface.test_interface_of(desc, obj, exception, expected_typeof);
         current_interface = this.array.members[current_interface.base];
     }
--- a/testing/web-platform/tests/resources/test/tests/idlharness/basic.html
+++ b/testing/web-platform/tests/resources/test/tests/idlharness/basic.html
@@ -24,12 +24,31 @@
         assert_equals(typeof WebIDL2.parse, "function");
     }, 'WebIDL2 namespace should have a parse method');
     test(function() {
         assert_throws(new TypeError(), function() {  WebIDL2.parse("I'm a syntax error"); });
     }, 'WebIDL2 parse method should bail on incorrect WebIDL');
     test(function() {
         assert_equals(typeof WebIDL2.parse("interface Foo {};"), "object");
     }, 'WebIDL2 parse method should produce an AST for correct WebIDL');
+    test(function() {
+        let i = new IdlArray();
+        i.add_untested_idls('partial interface A {};');
+        i.assert_throws( new IdlHarnessError("Partial interface A with no original interface"), i.test);
+    }, 'assert_throws should handle IdlHarnessError');
+    test(function() {
+        let i = new IdlArray();
+        i.add_untested_idls('partial interface A {};');
+        i.assert_throws( "Partial interface A with no original interface", i.test);
+    }, 'assert_throws should handle IdlHarnessError from message');
+    test(function () {
+        try {
+            let i = new IdlArray();
+            i.add_untested_idls('interface A {};');
+            i.assert_throws( "Partial interface A with no original interface", i.test);
+        } catch (e) {
+            assert_true(e instanceof IdlHarnessError);
+        }
+    }, 'assert_throws should throw if no IdlHarnessError thrown');
 </script>
 </body>
 </html>
 
--- a/testing/web-platform/tests/resources/testharness.js
+++ b/testing/web-platform/tests/resources/testharness.js
@@ -1242,16 +1242,23 @@ policies and contribution forms [3].
                     "changing property ${p} succeeded",
                     {p:property_name});
          } finally {
              object[property_name] = initial_value;
          }
     }
     expose(assert_readonly, "assert_readonly");
 
+    /**
+     * Assert an Exception with the expected code is thrown.
+     *
+     * @param {object|number|string} code The expected exception code.
+     * @param {Function} func Function which should throw.
+     * @param {string} description Error description for the case that the error is not thrown.
+     */
     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) {