function idl_test(srcs, deps, idl_setup_func) {
    return promise_test(function (t) {
        var idl_array = new IdlArray();
        var setup_error = null;
        const validationIgnored = [
            "constructor-member",
            "dict-arg-default",
            "require-exposed"
        ];
        return Promise.all(
            srcs.concat(deps).map(globalThis.fetch_spec))
            .then(function(results) {
                const astArray = results.map(result =>
                    WebIDL2.parse(result.idl, { sourceName: result.spec })
                );
                test(() => {
                    const validations = WebIDL2.validate(astArray)
                        .filter(v => !validationIgnored.includes(v.ruleName));
                    if (validations.length) {
                        const message = validations.map(v => v.message).join("\n\n");
                        throw new Error(message);
                    }
                }, "idl_test validation");
                for (var i = 0; i < srcs.length; i++) {
                    idl_array.internal_add_idls(astArray[i]);
                }
                for (var i = srcs.length; i < srcs.length + deps.length; i++) {
                    idl_array.internal_add_dependency_idls(astArray[i]);
                }
            })
            .then(function() {
                if (idl_setup_func) {
                    return idl_setup_func(idl_array, t);
                }
            })
            .catch(function(e) { setup_error = e || 'IDL setup failed.'; })
            .then(function () {
                var error = setup_error;
                try {
                    idl_array.test(); // Test what we can.
                } catch (e) {
                    // If testing fails hard here, the original setup error
                    // is more likely to be the real cause.
                    error = error || e;
                }
                if (error) {
                    throw error;
                }
            });
    }, 'idl_test setup');
}
idl_test is a promise_test wrapper that handles the fetching of the IDL,
avoiding repetitive boilerplate.

@param {String[]} srcs Spec name(s) for source idl files (fetched from
     /interfaces/{name}.idl).
@param {String[]} deps Spec name(s) for dependency idl files (fetched
     from /interfaces/{name}.idl). Order is important - dependencies from
     each source will only be included if they're already know to be a
     dependency (i.e. have already been seen).
@param {Function} setup_func Function for extra setup of the idl_array, such
     as adding objects. Do not call idl_array.test() in the setup; it is
     called by this function (idl_test).