<commit_before>/*
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].

[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license
[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license
[3] http://www.w3.org/2004/10/27-testcases
*/

/* For user documentation see docs/idlharness.md */

/**
 * Notes for people who want to edit this file (not just use it as a library):
 *
 * Most of the interesting stuff happens in the derived classes of IdlObject,
 * especially IdlInterface.  The entry point for all IdlObjects is .test(),
 * which is called by IdlArray.test().  An IdlObject is conceptually just
 * "thing we want to run tests on", and an IdlArray is an array of IdlObjects
 * with some additional data thrown in.
 *
 * The object model is based on what WebIDLParser.js produces, which is in turn
 * based on its pegjs grammar.  If you want to figure out what properties an
 * object will have from WebIDLParser.js, the best way is to look at the
 * grammar:
 *
 *   https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg
 *
 * So for instance:
 *
 *   // interface definition
 *   interface
 *       =   extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w
 *           { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; }
 *
 * This means that an "interface" object will have a .type property equal to
 * the string "interface", a .name property equal to the identifier that the
 * parser found, an .inheritance property equal to either null or the result of
 * the "ifInheritance" production found elsewhere in the grammar, and so on.
 * After each grammatical production is a JavaScript function in curly braces
 * that gets called with suitable arguments and returns some JavaScript value.
 *
 * (Note that the version of WebIDLParser.js we use might sometimes be
 * out-of-date or forked.)
 *
 * The members and methods of the classes defined by this file are all at least
 * briefly documented, hopefully.
 */
(function(){
"use strict";
/// Helpers ///
function constValue (cnt) {
    if (cnt.type === "null") return null;
    if (cnt.type === "NaN") return NaN;
    if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity;
    return cnt.value;
}

/// IdlArray ///
// Entry point
window.IdlArray = function()
//@{
{
    /**
     * A map from strings to the corresponding named IdlObject, such as
     * IdlInterface or IdlException.  These are the things that test() will run
     * tests on.
     */
    this.members = {};

    /**
     * A map from strings to arrays of strings.  The keys are interface or
     * exception names, and are expected to also exist as keys in this.members
     * (otherwise they'll be ignored).  This is populated by add_objects() --
     * see documentation at the start of the file.  The actual tests will be
     * run by calling this.members[name].test_object(obj) for each obj in
     * this.objects[name].  obj is a string that will be eval'd to produce a
     * JavaScript value, which is supposed to be an object implementing the
     * given IdlObject (interface, exception, etc.).
     */
    this.objects = {};

    /**
     * When adding multiple collections of IDLs one at a time, an earlier one
     * might contain a partial interface or implements statement that depends
     * on a later one.  Save these up and handle them right before we run
     * tests.
     *
     * .partials is simply an array of objects from WebIDLParser.js'
     * "partialinterface" production.  .implements maps strings to arrays of
     * strings, such that
     *
     *   A implements B;
     *   A implements C;
     *   D implements E;
     *
     * results in { A: ["B", "C"], D: ["E"] }.
     */
    this.partials = [];
    this["implements"] = {};
};

//@}
IdlArray.prototype.add_idls = function(raw_idls)
//@{
{
    /** Entry point.  See documentation at beginning of file. */
    this.internal_add_idls(WebIDL2.parse(raw_idls));
};

//@}
IdlArray.prototype.add_untested_idls = function(raw_idls)
//@{
{
    /** Entry point.  See documentation at beginning of file. */
    var parsed_idls = WebIDL2.parse(raw_idls);
    for (var i = 0; i < parsed_idls.length; i++)
    {
        parsed_idls[i].untested = true;
        if ("members" in parsed_idls[i])
        {
            for (var j = 0; j < parsed_idls[i].members.length; j++)
            {
                parsed_idls[i].members[j].untested = true;
            }
        }
    }
    this.internal_add_idls(parsed_idls);
};

//@}
IdlArray.prototype.internal_add_idls = function(parsed_idls)
//@{
{
    /**
     * Internal helper called by add_idls() and add_untested_idls().
     * parsed_idls is an array of objects that come from WebIDLParser.js's
     * "definitions" production.  The add_untested_idls() entry point
     * additionally sets an .untested property on each object (and its
     * .members) so that they'll be skipped by test() -- they'll only be
     * used for base interfaces of tested interfaces, return types, etc.
     */
    parsed_idls.forEach(function(parsed_idl)
    {
        if (parsed_idl.type == "interface" && parsed_idl.partial)
        {
            this.partials.push(parsed_idl);
            return;
        }

        if (parsed_idl.type == "implements")
        {
            if (!(parsed_idl.target in this["implements"]))
            {
                this["implements"][parsed_idl.target] = [];
            }
            this["implements"][parsed_idl.target].push(parsed_idl["implements"]);
            return;
        }

        parsed_idl.array = this;
        if (parsed_idl.name in this.members)
        {
            throw "Duplicate identifier " + parsed_idl.name;
        }
        switch(parsed_idl.type)
        {
        case "interface":
            this.members[parsed_idl.name] = new IdlInterface(parsed_idl);
            break;

        case "exception":
            this.members[parsed_idl.name] = new IdlException(parsed_idl);
            break;

        case "dictionary":
            // Nothing to test, but we need the dictionary info around for type
            // checks
            this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);
            break;

        case "typedef":
            this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);
            break;

        case "callback":
            // TODO
            console.log("callback not yet supported");
            break;

        case "enum":
            this.members[parsed_idl.name] = new IdlEnum(parsed_idl);
            break;

        case "callback interface":
            // TODO
            console.log("callback interface not yet supported");
            break;

        default:
            throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";
        }
    }.bind(this));
};

//@}
IdlArray.prototype.add_objects = function(dict)
//@{
{
    /** Entry point.  See documentation at beginning of file. */
    for (var k in dict)
    {
        if (k in this.objects)
        {
            this.objects[k] = this.objects[k].concat(dict[k]);
        }
        else
        {
            this.objects[k] = dict[k];
        }
    }
};

//@}
IdlArray.prototype.prevent_multiple_testing = function(name)
//@{
{
    /** Entry point.  See documentation at beginning of file. */
    this.members[name].prevent_multiple_testing = true;
};

//@}
IdlArray.prototype.recursively_get_implements = function(interface_name)
//@{
{
    /**
     * Helper function for test().  Returns an array of things that implement
     * interface_name, so if the IDL contains
     *
     *   A implements B;
     *   B implements C;
     *   B implements D;
     *
     * then recursively_get_implements("A") should return ["B", "C", "D"].
     */
    var ret = this["implements"][interface_name];
    if (ret === undefined)
    {
        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];
        }
    }
    return ret;
};

//@}
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";
        }
        if (parsed_idl.extAttrs)
        {
            parsed_idl.extAttrs.forEach(function(extAttr)
            {
                this.members[parsed_idl.name].extAttrs.push(extAttr);
            }.bind(this));
        }
        parsed_idl.members.forEach(function(member)
        {
            this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
        }.bind(this));
    }.bind(this));
    this.partials = [];

    for (var lhs in this["implements"])
    {
        this.recursively_get_implements(lhs).forEach(function(rhs)
        {
            var errStr = lhs + " implements " + rhs + ", but ";
            if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";
            if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
            if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
            if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
            this.members[rhs].members.forEach(function(member)
            {
                this.members[lhs].members.push(new IdlInterfaceMember(member));
            }.bind(this));
        }.bind(this));
    }
    this["implements"] = {};

    // Now run test() on every member, and test_object() for every object.
    for (var name in this.members)
    {
        this.members[name].test();
        if (name in this.objects)
        {
            this.objects[name].forEach(function(str)
            {
                this.members[name].test_object(str);
            }.bind(this));
        }
    }
};

//@}
IdlArray.prototype.assert_type_is = function(value, type)
//@{
{
    /**
     * Helper function that tests that value is an instance of type according
     * to the rules of WebIDL.  value is any JavaScript value, and type is an
     * object produced by WebIDLParser.js' "type" production.  That production
     * is fairly elaborate due to the complexity of WebIDL's types, so it's
     * best to look at the grammar to figure out what properties it might have.
     */
    if (type.idlType == "any")
    {
        // No assertions to make
        return;
    }

    if (type.nullable && value === null)
    {
        // This is fine
        return;
    }

    if (type.array)
    {
        // TODO: not supported yet
        return;
    }

    if (type.sequence)
    {
        assert_true(Array.isArray(value), "is not array");
        if (!value.length)
        {
            // Nothing we can do.
            return;
        }
        this.assert_type_is(value[0], type.idlType.idlType);
        return;
    }

    type = type.idlType;

    switch(type)
    {
        case "void":
            assert_equals(value, undefined);
            return;

        case "boolean":
            assert_equals(typeof value, "boolean");
            return;

        case "byte":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(-128 <= value && value <= 127, "byte " + value + " not in range [-128, 127]");
            return;

        case "octet":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(0 <= value && value <= 255, "octet " + value + " not in range [0, 255]");
            return;

        case "short":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(-32768 <= value && value <= 32767, "short " + value + " not in range [-32768, 32767]");
            return;

        case "unsigned short":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(0 <= value && value <= 65535, "unsigned short " + value + " not in range [0, 65535]");
            return;

        case "long":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " not in range [-2147483648, 2147483647]");
            return;

        case "unsigned long":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " not in range [0, 4294967295]");
            return;

        case "long long":
            assert_equals(typeof value, "number");
            return;

        case "unsigned long long":
            assert_equals(typeof value, "number");
            assert_true(0 <= value, "unsigned long long is negative");
            return;

        case "float":
        case "double":
        case "unrestricted float":
        case "unrestricted double":
            // TODO: distinguish these cases
            assert_equals(typeof value, "number");
            return;

        case "DOMString":
        case "ByteString":
        case "USVString":
            assert_equals(typeof value, "string");
            return;

        case "object":
            assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
            return;
    }
<commit_msg>Add a pointer to an issue.<commit_after>/*
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].

[1] http://www.w3.org/Consortium/Legal/2008/04-testsuite-license
[2] http://www.w3.org/Consortium/Legal/2008/03-bsd-license
[3] http://www.w3.org/2004/10/27-testcases
*/

/* For user documentation see docs/idlharness.md */

/**
 * Notes for people who want to edit this file (not just use it as a library):
 *
 * Most of the interesting stuff happens in the derived classes of IdlObject,
 * especially IdlInterface.  The entry point for all IdlObjects is .test(),
 * which is called by IdlArray.test().  An IdlObject is conceptually just
 * "thing we want to run tests on", and an IdlArray is an array of IdlObjects
 * with some additional data thrown in.
 *
 * The object model is based on what WebIDLParser.js produces, which is in turn
 * based on its pegjs grammar.  If you want to figure out what properties an
 * object will have from WebIDLParser.js, the best way is to look at the
 * grammar:
 *
 *   https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg
 *
 * So for instance:
 *
 *   // interface definition
 *   interface
 *       =   extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w
 *           { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; }
 *
 * This means that an "interface" object will have a .type property equal to
 * the string "interface", a .name property equal to the identifier that the
 * parser found, an .inheritance property equal to either null or the result of
 * the "ifInheritance" production found elsewhere in the grammar, and so on.
 * After each grammatical production is a JavaScript function in curly braces
 * that gets called with suitable arguments and returns some JavaScript value.
 *
 * (Note that the version of WebIDLParser.js we use might sometimes be
 * out-of-date or forked.)
 *
 * The members and methods of the classes defined by this file are all at least
 * briefly documented, hopefully.
 */
(function(){
"use strict";
/// Helpers ///
function constValue (cnt) {
    if (cnt.type === "null") return null;
    if (cnt.type === "NaN") return NaN;
    if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity;
    return cnt.value;
}

/// IdlArray ///
// Entry point
window.IdlArray = function()
//@{
{
    /**
     * A map from strings to the corresponding named IdlObject, such as
     * IdlInterface or IdlException.  These are the things that test() will run
     * tests on.
     */
    this.members = {};

    /**
     * A map from strings to arrays of strings.  The keys are interface or
     * exception names, and are expected to also exist as keys in this.members
     * (otherwise they'll be ignored).  This is populated by add_objects() --
     * see documentation at the start of the file.  The actual tests will be
     * run by calling this.members[name].test_object(obj) for each obj in
     * this.objects[name].  obj is a string that will be eval'd to produce a
     * JavaScript value, which is supposed to be an object implementing the
     * given IdlObject (interface, exception, etc.).
     */
    this.objects = {};

    /**
     * When adding multiple collections of IDLs one at a time, an earlier one
     * might contain a partial interface or implements statement that depends
     * on a later one.  Save these up and handle them right before we run
     * tests.
     *
     * .partials is simply an array of objects from WebIDLParser.js'
     * "partialinterface" production.  .implements maps strings to arrays of
     * strings, such that
     *
     *   A implements B;
     *   A implements C;
     *   D implements E;
     *
     * results in { A: ["B", "C"], D: ["E"] }.
     */
    this.partials = [];
    this["implements"] = {};
};

//@}
IdlArray.prototype.add_idls = function(raw_idls)
//@{
{
    /** Entry point.  See documentation at beginning of file. */
    this.internal_add_idls(WebIDL2.parse(raw_idls));
};

//@}
IdlArray.prototype.add_untested_idls = function(raw_idls)
//@{
{
    /** Entry point.  See documentation at beginning of file. */
    var parsed_idls = WebIDL2.parse(raw_idls);
    for (var i = 0; i < parsed_idls.length; i++)
    {
        parsed_idls[i].untested = true;
        if ("members" in parsed_idls[i])
        {
            for (var j = 0; j < parsed_idls[i].members.length; j++)
            {
                parsed_idls[i].members[j].untested = true;
            }
        }
    }
    this.internal_add_idls(parsed_idls);
};

//@}
IdlArray.prototype.internal_add_idls = function(parsed_idls)
//@{
{
    /**
     * Internal helper called by add_idls() and add_untested_idls().
     * parsed_idls is an array of objects that come from WebIDLParser.js's
     * "definitions" production.  The add_untested_idls() entry point
     * additionally sets an .untested property on each object (and its
     * .members) so that they'll be skipped by test() -- they'll only be
     * used for base interfaces of tested interfaces, return types, etc.
     */
    parsed_idls.forEach(function(parsed_idl)
    {
        if (parsed_idl.type == "interface" && parsed_idl.partial)
        {
            this.partials.push(parsed_idl);
            return;
        }

        if (parsed_idl.type == "implements")
        {
            if (!(parsed_idl.target in this["implements"]))
            {
                this["implements"][parsed_idl.target] = [];
            }
            this["implements"][parsed_idl.target].push(parsed_idl["implements"]);
            return;
        }

        parsed_idl.array = this;
        if (parsed_idl.name in this.members)
        {
            throw "Duplicate identifier " + parsed_idl.name;
        }
        switch(parsed_idl.type)
        {
        case "interface":
            this.members[parsed_idl.name] = new IdlInterface(parsed_idl);
            break;

        case "exception":
            this.members[parsed_idl.name] = new IdlException(parsed_idl);
            break;

        case "dictionary":
            // Nothing to test, but we need the dictionary info around for type
            // checks
            this.members[parsed_idl.name] = new IdlDictionary(parsed_idl);
            break;

        case "typedef":
            this.members[parsed_idl.name] = new IdlTypedef(parsed_idl);
            break;

        case "callback":
            // TODO
            console.log("callback not yet supported");
            break;

        case "enum":
            this.members[parsed_idl.name] = new IdlEnum(parsed_idl);
            break;

        case "callback interface":
            // TODO
            console.log("callback interface not yet supported");
            break;

        default:
            throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported";
        }
    }.bind(this));
};

//@}
IdlArray.prototype.add_objects = function(dict)
//@{
{
    /** Entry point.  See documentation at beginning of file. */
    for (var k in dict)
    {
        if (k in this.objects)
        {
            this.objects[k] = this.objects[k].concat(dict[k]);
        }
        else
        {
            this.objects[k] = dict[k];
        }
    }
};

//@}
IdlArray.prototype.prevent_multiple_testing = function(name)
//@{
{
    /** Entry point.  See documentation at beginning of file. */
    this.members[name].prevent_multiple_testing = true;
};

//@}
IdlArray.prototype.recursively_get_implements = function(interface_name)
//@{
{
    /**
     * Helper function for test().  Returns an array of things that implement
     * interface_name, so if the IDL contains
     *
     *   A implements B;
     *   B implements C;
     *   B implements D;
     *
     * then recursively_get_implements("A") should return ["B", "C", "D"].
     */
    var ret = this["implements"][interface_name];
    if (ret === undefined)
    {
        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];
        }
    }
    return ret;
};

//@}
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";
        }
        if (parsed_idl.extAttrs)
        {
            parsed_idl.extAttrs.forEach(function(extAttr)
            {
                this.members[parsed_idl.name].extAttrs.push(extAttr);
            }.bind(this));
        }
        parsed_idl.members.forEach(function(member)
        {
            this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
        }.bind(this));
    }.bind(this));
    this.partials = [];

    for (var lhs in this["implements"])
    {
        this.recursively_get_implements(lhs).forEach(function(rhs)
        {
            var errStr = lhs + " implements " + rhs + ", but ";
            if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";
            if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface.";
            if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
            if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
            this.members[rhs].members.forEach(function(member)
            {
                this.members[lhs].members.push(new IdlInterfaceMember(member));
            }.bind(this));
        }.bind(this));
    }
    this["implements"] = {};

    // Now run test() on every member, and test_object() for every object.
    for (var name in this.members)
    {
        this.members[name].test();
        if (name in this.objects)
        {
            this.objects[name].forEach(function(str)
            {
                this.members[name].test_object(str);
            }.bind(this));
        }
    }
};

//@}
IdlArray.prototype.assert_type_is = function(value, type)
//@{
{
    /**
     * Helper function that tests that value is an instance of type according
     * to the rules of WebIDL.  value is any JavaScript value, and type is an
     * object produced by WebIDLParser.js' "type" production.  That production
     * is fairly elaborate due to the complexity of WebIDL's types, so it's
     * best to look at the grammar to figure out what properties it might have.
     */
    if (type.idlType == "any")
    {
        // No assertions to make
        return;
    }

    if (type.nullable && value === null)
    {
        // This is fine
        return;
    }

    if (type.array)
    {
        // TODO: not supported yet
        return;
    }

    if (type.sequence)
    {
        assert_true(Array.isArray(value), "is not array");
        if (!value.length)
        {
            // Nothing we can do.
            return;
        }
        this.assert_type_is(value[0], type.idlType.idlType);
        return;
    }

    type = type.idlType;

    switch(type)
    {
        case "void":
            assert_equals(value, undefined);
            return;

        case "boolean":
            assert_equals(typeof value, "boolean");
            return;

        case "byte":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(-128 <= value && value <= 127, "byte " + value + " not in range [-128, 127]");
            return;

        case "octet":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(0 <= value && value <= 255, "octet " + value + " not in range [0, 255]");
            return;

        case "short":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(-32768 <= value && value <= 32767, "short " + value + " not in range [-32768, 32767]");
            return;

        case "unsigned short":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(0 <= value && value <= 65535, "unsigned short " + value + " not in range [0, 65535]");
            return;

        case "long":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " not in range [-2147483648, 2147483647]");
            return;

        case "unsigned long":
            assert_equals(typeof value, "number");
            assert_equals(value, Math.floor(value), "not an integer");
            assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " not in range [0, 4294967295]");
            return;

        case "long long":
            assert_equals(typeof value, "number");
            return;

        case "unsigned long long":
            assert_equals(typeof value, "number");
            assert_true(0 <= value, "unsigned long long is negative");
            return;

        case "float":
        case "double":
        case "unrestricted float":
        case "unrestricted double":
            // TODO: distinguish these cases
            assert_equals(typeof value, "number");
            return;

        case "DOMString":
        case "ByteString":
        case "USVString":
            // TODO: https://github.com/w3c/testharness.js/issues/92
            assert_equals(typeof value, "string");
            return;

        case "object":
            assert_true(typeof value == "object" || typeof value == "function", "wrong type: not object or function");
            return;
    }

{'fraction_non_alphanumeric': 0.11869799314733236, 'fraction_numerical': 0.01087336549891616, 'mean_word_length': 2.1830625417315823, 'pattern_counts': {'":': 52, '<': 35, '<?xml version=': 0, '>': 3, 'https://': 3, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7168479', 'n_tokens_mistral': 8589, 'n_tokens_neox': 8313, 'n_words': 2503}