<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
*/

/*
 * == Introduction ==
 *
 * This file provides a framework for writing testcases. It is intended to
 * provide a convenient API for making common assertions, and to work both
 * for testing synchronous and asynchronous DOM features in a way that
 * promotes clear, robust, tests.
 *
 * == Basic Usage ==
 *
 * To use this file, import the script and the testharnessreport script into
 * the test document:
 * <script src="/resources/testharness.js"></script>
 * <script src="/resources/testharnessreport.js"></script>
 *
 * Within each file one may define one or more tests. Each test is atomic
 * in the sense that a single test has a single result (pass/fail/timeout).
 * Within each test one may have a number of asserts. The test fails at the
 * first failing assert, and the remainder of the test is (typically) not run.
 *
 * If the file containing the tests is a HTML file with an element of id "log"
 * this will be populated with a table containing the test results after all
 * the tests have run.
 *
 * NOTE: By default tests must be created before the load event fires. For ways
 *       to create tests after the load event, see "Determining when all tests
 *       are complete", below
 *
 * == Synchronous Tests ==
 *
 * To create a synchronous test use the test() function:
 *
 * test(test_function, name, properties)
 *
 * test_function is a function that contains the code to test. For example a
 * trivial passing test would be:
 *
 * test(function() {assert_true(true)}, "assert_true with true")
 *
 * The function passed in is run in the test() call.
 *
 * properties is an object that overrides default test properties. The
 * recognised properties are:
 *    timeout - the test timeout in ms
 *
 * e.g.
 * test(test_function, "Sample test", {timeout:1000})
 *
 * would run test_function with a timeout of 1s.
 *
 * Additionally, test-specific metadata can be passed in the properties. These
 * are used when the individual test has different metadata from that stored 
 * in the <head>.
 * The recognized metadata properties are:
 *
 *    help - The url of the part of the specification being tested
 *
 *    assert - A human readable description of what the test is attempting 
 *             to prove
 *
 *    author - Name and contact information for the author of the test in the
 *             format: "Name <email_addr>" or "Name http://contact/url"
 *
 * == Asynchronous Tests ==
 *
 * Testing asynchronous features is somewhat more complex since the result of
 * a test may depend on one or more events or other callbacks. The API provided
 * for testing these features is indended to be rather low-level but hopefully
 * applicable to many situations.
 *
 * To create a test, one starts by getting a Test object using async_test:
 *
 * async_test(name, properties)
 *
 * e.g.
 * var t = async_test("Simple async test")
 *
 * Assertions can be added to the test by calling the step method of the test
 * object with a function containing the test assertions:
 *
 * t.step(function() {assert_true(true)});
 *
 * When all the steps are complete, the done() method must be called:
 *
 * t.done();
 *
 * The properties argument is identical to that for test().
 *
 * In many cases it is convenient to run a step in response to an event or a
 * callback. A convenient method of doing this is through the step_func method
 * which returns a function that, when called runs a test step. For example
 *
 * object.some_event = t.step_func(function(e) {assert_true(e.a)});
 *
 * == Making assertions ==
 *
 * Functions for making assertions start assert_
 * The best way to get a list is to look in this file for functions names
 * matching that pattern. The general signature is
 *
 * assert_something(actual, expected, description)
 *
 * although not all assertions precisely match this pattern e.g. assert_true
 * only takes actual and description as arguments.
 *
 * The description parameter is used to present more useful error messages when
 * a test fails
 *
 * NOTE: All asserts must be located in a test() or a step of an async_test().
 *       asserts outside these places won't be detected correctly by the harness
 *       and may cause a file to stop testing.
 *
 * == Setup ==
 *
 * Sometimes tests require non-trivial setup that may fail. For this purpose
 * there is a setup() function, that may be called with one or two arguments.
 * The two argument version is:
 *
 * setup(func, properties)
 *
 * The one argument versions may omit either argument.
 * func is a function to be run synchronously. setup() becomes a no-op once
 * 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)
 *
 * output_document - The document to which results should be logged. By default
 *                   this is the current document but could be an ancestor
 *                   document in some cases e.g. a SVG test loaded in an HTML
 *                   wrapper
 *
 * explicit_timeout - disable file timeout; only stop waiting for results
 *                    when the timeout() function is called (typically for
 *                    use when integrating with some existing test framework
 *                    that has its own timeout mechanism).
 *
 * == Determining when all tests are complete ==
 *
 * By default the test harness will assume there are no more results to come
 * when:
 * 1) There are no Test objects that have been created but not completed
 * 2) The load event on the document has fired
 *
 * This behaviour can be overridden by setting the explicit_done property to
 * true in a call to setup(). If explicit_done is true, the test harness will
 * not assume it is done until the global done() function is called. Once done()
 * is called, the two conditions above apply like normal.
 *
 * == Generating tests ==
 *
 * NOTE: this functionality may be removed
 *
 * There are scenarios in which is is desirable to create a large number of
 * (synchronous) tests that are internally similar but vary in the parameters
 * used. To make this easier, the generate_tests function allows a single
 * function to be called with each set of parameters in a list:
 *
 * generate_tests(test_function, parameter_lists, properties)
 *
 * For example:
 *
 * generate_tests(assert_equals, [
 *     ["Sum one and one", 1+1, 2],
 *     ["Sum one and zero", 1+0, 1]
 *     ])
 *
 * Is equivalent to:
 *
 * test(function() {assert_equals(1+1, 2)}, "Sum one and one")
 * test(function() {assert_equals(1+0, 1)}, "Sum one and zero")
 *
 * Note that the first item in each parameter list corresponds to the name of
 * the test.
 *
 * The properties argument is identical to that for test(). This may be a 
 * single object (used for all generated tests) or an array.
 *
 * == Callback API ==
 *
 * The framework provides callbacks corresponding to 3 events:
 *
 * start - happens when the first Test is created
 * result - happens when a test result is recieved
 * complete - happens when all results are recieved
 *
 * The page defining the tests may add callbacks for these events by calling
 * the following methods:
 *
 *   add_start_callback(callback) - callback called with no arguments
 *   add_result_callback(callback) - callback called with a test argument
 *   add_completion_callback(callback) - callback called with an array of tests
 *                                       and an status object
 *
 * tests have the following properties:
 *   status: A status code. This can be compared to the PASS, FAIL, TIMEOUT and
 *           NOTRUN properties on the test object
 *   message: A message indicating the reason for failure. In the future this
 *            will always be a string
 *
 *  The status object gives the overall status of the harness. It has the
 *  following properties:
 *    status: Can be compared to the OK, ERROR and TIMEOUT properties
 *    message: An error message set when the status is ERROR
 *
 * == External API ==
 *
 * In order to collect the results of multiple pages containing tests, the test
 * harness will, when loaded in a nested browsing context, attempt to call
 * certain functions in each ancestor browsing context:
 *
 * start - start_callback
 * result - result_callback
 * complete - completion_callback
 *
 * These are given the same arguments as the corresponding internal callbacks
 * described above.
 *
 * == List of assertions ==
 *
 * assert_true(actual, description)
 *   asserts that /actual/ is strictly true
 *
 * assert_false(actual, description)
 *   asserts that /actual/ is strictly false
 *
 * assert_equals(actual, expected, description)
 *   asserts that /actual/ is the same value as /expected/
 *
 * assert_not_equals(actual, expected, description)
 *   asserts that /actual/ is a different value to /expected/. Yes, this means
 *   that "expected" is a misnomer
 *
 * assert_in_array(actual, expected, description)
 *   asserts that /expected/ is an Array, and /actual/ is equal to one of the
 *   members -- expected.indexOf(actual) != -1
 *
 * assert_array_equals(actual, expected, description)
 *   asserts that /actual/ and /expected/ have the same length and the value of
 *   each indexed property in /actual/ is the strictly equal to the corresponding
 *   property value in /expected/
 *
 * assert_approx_equals(actual, expected, epsilon, description)
 *   asserts that /actual/ is a number within +/- /epsilon/ of /expected/
 *
 * assert_regexp_match(actual, expected, description)
 *   asserts that /actual/ matches the regexp /expected/
 *
 * assert_own_property(object, property_name, description)
 *   assert that object has own property property_name
 *
 * assert_inherits(object, property_name, description)
 *   assert that object does not have an own property named property_name
 *   but that property_name is present in the prototype chain for object
 *
 * assert_idl_attribute(object, attribute_name, description)
 *   assert that an object that is an instance of some interface has the
 *   attribute attribute_name following the conditions specified by WebIDL
 *
 * assert_readonly(object, property_name, description)
 *   assert that property property_name on object is readonly
 *
 * assert_throws(code, func, description)
 *   code - the expected exception:
 *     o string: the thrown exception must be a DOMException with the given
 *               name, e.g., "TimeoutError" (for compatibility with existing
 *               tests, a constant is also supported, e.g., "TIMEOUT_ERR")
 *     o object: the thrown exception must have a property called "name" that
 *               matches code.name
 *     o null:   allow any exception (in general, one of the options above
 *               should be used)
 *   func - a function that should throw
 *
 * assert_unreached(description)
 *   asserts if called. Used to ensure that some codepath is *not* taken e.g.
 *   an event does not fire.
 *
 * assert_exists(object, property_name, description)
 *   *** deprecated ***
 *   asserts that object has an own property property_name
 *
 * assert_not_exists(object, property_name, description)
 *   *** deprecated ***
 *   assert that object does not have own property property_name
 */

(function ()
{
    var debug = false;
    // default timeout is 5 seconds, test can override if needed
    var settings = {
      output:true,
      timeout:5000,
      test_timeout:2000
    };

    var xhtml_ns = "http://www.w3.org/1999/xhtml";

    // script_prefix is used by Output.prototype.show_results() to figure out
    // where to get testharness.css from.  It's enclosed in an extra closure to
    // not pollute the library's namespace with variables like "src".
    var script_prefix = null;
    (function ()
    {
        var scripts = document.getElementsByTagName("script");
        for (var i = 0; i < scripts.length; i++)
        {
            if (scripts[i].src)
            {
                var src = scripts[i].src;
            }
            else if (scripts[i].href)
            {
                //SVG case
                var src = scripts[i].href.baseVal;
            }
            if (src && src.slice(src.length - "testharness.js".length) === "testharness.js")
            {
                script_prefix = src.slice(0, src.length - "testharness.js".length);
                break;
            }
        }
    })();

    /*
     * API functions
     */

    var name_counter = 0;
    function next_default_name()
    {
        //Don't use document.title to work around an Opera bug in XHTML documents
        var prefix = document.getElementsByTagName("title").length > 0 ?
                         document.getElementsByTagName("title")[0].firstChild.data :
                         "Untitled";
        var suffix = name_counter > 0 ? " " + name_counter : "";
        name_counter++;
        return prefix + suffix;
    }

    function test(func, name, properties)
    {
        var test_name = name ? name : next_default_name();
        properties = properties ? properties : {};
        var test_obj = new Test(test_name, properties);
        test_obj.step(func);
        if (test_obj.status === test_obj.NOTRUN) {
            test_obj.done();
        }
    }

    function async_test(name, properties)
    {
        var test_name = name ? name : next_default_name();
        properties = properties ? properties : {};
        var test_obj = new Test(test_name, properties);
        return test_obj;
    }

    function setup(func_or_properties, maybe_properties)
    {
        var func = null;
        var properties = {};
        if (arguments.length === 2) {
            func = func_or_properties;
            properties = maybe_properties;
        } else if (func_or_properties instanceof Function){
            func = func_or_properties;
        } else {
            properties = func_or_properties;
        }
        tests.setup(func, properties);
        output.setup(properties);
    }

    function done() {
        tests.end_wait();
    }

    function generate_tests(func, args, properties) {
        forEach(args, function(x, i)
                {
                    var name = x[0];
                    test(function()
                         {
                             func.apply(this, x.slice(1));
                         }, 
                         name, 
                         Array.isArray(properties) ? properties[i] : properties);
                });
    }

    function on_event(object, event, callback)
    {
      object.addEventListener(event, callback, false);
    }

    expose(test, 'test');
    expose(async_test, 'async_test');
    expose(generate_tests, 'generate_tests');
    expose(setup, 'setup');
    expose(done, 'done');
    expose(on_event, 'on_event');

    /*
     * 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;
    }

    /*
     * Convert a value to a nice, human-readable string
     */
    function format_value(val)
    {
        if (Array.isArray(val))
        {
            return "[" + val.map(format_value).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.  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)
            {
                switch (val.nodeType)
                {
                case Node.ELEMENT_NODE:
                    var ret = "<" + val.tagName.toLowerCase();
                    for (var i = 0; i < val.attributes.length; i++)
                    {
                        ret += " " + val.attributes[i].name + '="' + val.attributes[i].value + '"';
                    }
                    ret += ">" + val.innerHTML + "</" + val.tagName.toLowerCase() + ">";
                    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 <!--" + truncate(val.data, 60) + "-->";
                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";
                }
            }

            // Fall through to default
        default:
            return typeof val + ' "' + truncate(String(val), 60) + '"';
        }
    }
    expose(format_value, "format_value");

    /*
     * Assertions
     */

    function assert_true(actual, description)
    {
        assert(actual === true, "assert_true", description,
                                "expected true got ${actual}", {actual:actual});
    };
    expose(assert_true, "assert_true");

    function assert_false(actual, description)
    {
        assert(actual === false, "assert_false", description,
                                 "expected false got ${actual}", {actual:actual});
    };
    expose(assert_false, "assert_false");

    function same_value(x, y) {
        if (y !== y)
        {
            //NaN case
            return x !== x;
        }
        else if (x === 0 && y === 0) {
            //Distinguish +0 and -0
            return 1/x === 1/y;
        }
        else
        {
            //typical case
            return x === y;
        }
    }

    function assert_equals(actual, expected, description)
    {
         /*
          * Test if two primitives are equal or two objects
          * are the same object
          */
        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.indexOf(actual) != -1, "assert_in_array", description,
                                               "value ${actual} not in array ${expected}",
                                               {actual:actual, expected:expected});
    }
    expose(assert_in_array, "assert_in_array");<commit_msg>Display variable types in assert_equals if they're different<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
*/

/*
 * == Introduction ==
 *
 * This file provides a framework for writing testcases. It is intended to
 * provide a convenient API for making common assertions, and to work both
 * for testing synchronous and asynchronous DOM features in a way that
 * promotes clear, robust, tests.
 *
 * == Basic Usage ==
 *
 * To use this file, import the script and the testharnessreport script into
 * the test document:
 * <script src="/resources/testharness.js"></script>
 * <script src="/resources/testharnessreport.js"></script>
 *
 * Within each file one may define one or more tests. Each test is atomic
 * in the sense that a single test has a single result (pass/fail/timeout).
 * Within each test one may have a number of asserts. The test fails at the
 * first failing assert, and the remainder of the test is (typically) not run.
 *
 * If the file containing the tests is a HTML file with an element of id "log"
 * this will be populated with a table containing the test results after all
 * the tests have run.
 *
 * NOTE: By default tests must be created before the load event fires. For ways
 *       to create tests after the load event, see "Determining when all tests
 *       are complete", below
 *
 * == Synchronous Tests ==
 *
 * To create a synchronous test use the test() function:
 *
 * test(test_function, name, properties)
 *
 * test_function is a function that contains the code to test. For example a
 * trivial passing test would be:
 *
 * test(function() {assert_true(true)}, "assert_true with true")
 *
 * The function passed in is run in the test() call.
 *
 * properties is an object that overrides default test properties. The
 * recognised properties are:
 *    timeout - the test timeout in ms
 *
 * e.g.
 * test(test_function, "Sample test", {timeout:1000})
 *
 * would run test_function with a timeout of 1s.
 *
 * Additionally, test-specific metadata can be passed in the properties. These
 * are used when the individual test has different metadata from that stored 
 * in the <head>.
 * The recognized metadata properties are:
 *
 *    help - The url of the part of the specification being tested
 *
 *    assert - A human readable description of what the test is attempting 
 *             to prove
 *
 *    author - Name and contact information for the author of the test in the
 *             format: "Name <email_addr>" or "Name http://contact/url"
 *
 * == Asynchronous Tests ==
 *
 * Testing asynchronous features is somewhat more complex since the result of
 * a test may depend on one or more events or other callbacks. The API provided
 * for testing these features is indended to be rather low-level but hopefully
 * applicable to many situations.
 *
 * To create a test, one starts by getting a Test object using async_test:
 *
 * async_test(name, properties)
 *
 * e.g.
 * var t = async_test("Simple async test")
 *
 * Assertions can be added to the test by calling the step method of the test
 * object with a function containing the test assertions:
 *
 * t.step(function() {assert_true(true)});
 *
 * When all the steps are complete, the done() method must be called:
 *
 * t.done();
 *
 * The properties argument is identical to that for test().
 *
 * In many cases it is convenient to run a step in response to an event or a
 * callback. A convenient method of doing this is through the step_func method
 * which returns a function that, when called runs a test step. For example
 *
 * object.some_event = t.step_func(function(e) {assert_true(e.a)});
 *
 * == Making assertions ==
 *
 * Functions for making assertions start assert_
 * The best way to get a list is to look in this file for functions names
 * matching that pattern. The general signature is
 *
 * assert_something(actual, expected, description)
 *
 * although not all assertions precisely match this pattern e.g. assert_true
 * only takes actual and description as arguments.
 *
 * The description parameter is used to present more useful error messages when
 * a test fails
 *
 * NOTE: All asserts must be located in a test() or a step of an async_test().
 *       asserts outside these places won't be detected correctly by the harness
 *       and may cause a file to stop testing.
 *
 * == Setup ==
 *
 * Sometimes tests require non-trivial setup that may fail. For this purpose
 * there is a setup() function, that may be called with one or two arguments.
 * The two argument version is:
 *
 * setup(func, properties)
 *
 * The one argument versions may omit either argument.
 * func is a function to be run synchronously. setup() becomes a no-op once
 * 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)
 *
 * output_document - The document to which results should be logged. By default
 *                   this is the current document but could be an ancestor
 *                   document in some cases e.g. a SVG test loaded in an HTML
 *                   wrapper
 *
 * explicit_timeout - disable file timeout; only stop waiting for results
 *                    when the timeout() function is called (typically for
 *                    use when integrating with some existing test framework
 *                    that has its own timeout mechanism).
 *
 * == Determining when all tests are complete ==
 *
 * By default the test harness will assume there are no more results to come
 * when:
 * 1) There are no Test objects that have been created but not completed
 * 2) The load event on the document has fired
 *
 * This behaviour can be overridden by setting the explicit_done property to
 * true in a call to setup(). If explicit_done is true, the test harness will
 * not assume it is done until the global done() function is called. Once done()
 * is called, the two conditions above apply like normal.
 *
 * == Generating tests ==
 *
 * NOTE: this functionality may be removed
 *
 * There are scenarios in which is is desirable to create a large number of
 * (synchronous) tests that are internally similar but vary in the parameters
 * used. To make this easier, the generate_tests function allows a single
 * function to be called with each set of parameters in a list:
 *
 * generate_tests(test_function, parameter_lists, properties)
 *
 * For example:
 *
 * generate_tests(assert_equals, [
 *     ["Sum one and one", 1+1, 2],
 *     ["Sum one and zero", 1+0, 1]
 *     ])
 *
 * Is equivalent to:
 *
 * test(function() {assert_equals(1+1, 2)}, "Sum one and one")
 * test(function() {assert_equals(1+0, 1)}, "Sum one and zero")
 *
 * Note that the first item in each parameter list corresponds to the name of
 * the test.
 *
 * The properties argument is identical to that for test(). This may be a 
 * single object (used for all generated tests) or an array.
 *
 * == Callback API ==
 *
 * The framework provides callbacks corresponding to 3 events:
 *
 * start - happens when the first Test is created
 * result - happens when a test result is recieved
 * complete - happens when all results are recieved
 *
 * The page defining the tests may add callbacks for these events by calling
 * the following methods:
 *
 *   add_start_callback(callback) - callback called with no arguments
 *   add_result_callback(callback) - callback called with a test argument
 *   add_completion_callback(callback) - callback called with an array of tests
 *                                       and an status object
 *
 * tests have the following properties:
 *   status: A status code. This can be compared to the PASS, FAIL, TIMEOUT and
 *           NOTRUN properties on the test object
 *   message: A message indicating the reason for failure. In the future this
 *            will always be a string
 *
 *  The status object gives the overall status of the harness. It has the
 *  following properties:
 *    status: Can be compared to the OK, ERROR and TIMEOUT properties
 *    message: An error message set when the status is ERROR
 *
 * == External API ==
 *
 * In order to collect the results of multiple pages containing tests, the test
 * harness will, when loaded in a nested browsing context, attempt to call
 * certain functions in each ancestor browsing context:
 *
 * start - start_callback
 * result - result_callback
 * complete - completion_callback
 *
 * These are given the same arguments as the corresponding internal callbacks
 * described above.
 *
 * == List of assertions ==
 *
 * assert_true(actual, description)
 *   asserts that /actual/ is strictly true
 *
 * assert_false(actual, description)
 *   asserts that /actual/ is strictly false
 *
 * assert_equals(actual, expected, description)
 *   asserts that /actual/ is the same value as /expected/
 *
 * assert_not_equals(actual, expected, description)
 *   asserts that /actual/ is a different value to /expected/. Yes, this means
 *   that "expected" is a misnomer
 *
 * assert_in_array(actual, expected, description)
 *   asserts that /expected/ is an Array, and /actual/ is equal to one of the
 *   members -- expected.indexOf(actual) != -1
 *
 * assert_array_equals(actual, expected, description)
 *   asserts that /actual/ and /expected/ have the same length and the value of
 *   each indexed property in /actual/ is the strictly equal to the corresponding
 *   property value in /expected/
 *
 * assert_approx_equals(actual, expected, epsilon, description)
 *   asserts that /actual/ is a number within +/- /epsilon/ of /expected/
 *
 * assert_regexp_match(actual, expected, description)
 *   asserts that /actual/ matches the regexp /expected/
 *
 * assert_own_property(object, property_name, description)
 *   assert that object has own property property_name
 *
 * assert_inherits(object, property_name, description)
 *   assert that object does not have an own property named property_name
 *   but that property_name is present in the prototype chain for object
 *
 * assert_idl_attribute(object, attribute_name, description)
 *   assert that an object that is an instance of some interface has the
 *   attribute attribute_name following the conditions specified by WebIDL
 *
 * assert_readonly(object, property_name, description)
 *   assert that property property_name on object is readonly
 *
 * assert_throws(code, func, description)
 *   code - the expected exception:
 *     o string: the thrown exception must be a DOMException with the given
 *               name, e.g., "TimeoutError" (for compatibility with existing
 *               tests, a constant is also supported, e.g., "TIMEOUT_ERR")
 *     o object: the thrown exception must have a property called "name" that
 *               matches code.name
 *     o null:   allow any exception (in general, one of the options above
 *               should be used)
 *   func - a function that should throw
 *
 * assert_unreached(description)
 *   asserts if called. Used to ensure that some codepath is *not* taken e.g.
 *   an event does not fire.
 *
 * assert_exists(object, property_name, description)
 *   *** deprecated ***
 *   asserts that object has an own property property_name
 *
 * assert_not_exists(object, property_name, description)
 *   *** deprecated ***
 *   assert that object does not have own property property_name
 */

(function ()
{
    var debug = false;
    // default timeout is 5 seconds, test can override if needed
    var settings = {
      output:true,
      timeout:5000,
      test_timeout:2000
    };

    var xhtml_ns = "http://www.w3.org/1999/xhtml";

    // script_prefix is used by Output.prototype.show_results() to figure out
    // where to get testharness.css from.  It's enclosed in an extra closure to
    // not pollute the library's namespace with variables like "src".
    var script_prefix = null;
    (function ()
    {
        var scripts = document.getElementsByTagName("script");
        for (var i = 0; i < scripts.length; i++)
        {
            if (scripts[i].src)
            {
                var src = scripts[i].src;
            }
            else if (scripts[i].href)
            {
                //SVG case
                var src = scripts[i].href.baseVal;
            }
            if (src && src.slice(src.length - "testharness.js".length) === "testharness.js")
            {
                script_prefix = src.slice(0, src.length - "testharness.js".length);
                break;
            }
        }
    })();

    /*
     * API functions
     */

    var name_counter = 0;
    function next_default_name()
    {
        //Don't use document.title to work around an Opera bug in XHTML documents
        var prefix = document.getElementsByTagName("title").length > 0 ?
                         document.getElementsByTagName("title")[0].firstChild.data :
                         "Untitled";
        var suffix = name_counter > 0 ? " " + name_counter : "";
        name_counter++;
        return prefix + suffix;
    }

    function test(func, name, properties)
    {
        var test_name = name ? name : next_default_name();
        properties = properties ? properties : {};
        var test_obj = new Test(test_name, properties);
        test_obj.step(func);
        if (test_obj.status === test_obj.NOTRUN) {
            test_obj.done();
        }
    }

    function async_test(name, properties)
    {
        var test_name = name ? name : next_default_name();
        properties = properties ? properties : {};
        var test_obj = new Test(test_name, properties);
        return test_obj;
    }

    function setup(func_or_properties, maybe_properties)
    {
        var func = null;
        var properties = {};
        if (arguments.length === 2) {
            func = func_or_properties;
            properties = maybe_properties;
        } else if (func_or_properties instanceof Function){
            func = func_or_properties;
        } else {
            properties = func_or_properties;
        }
        tests.setup(func, properties);
        output.setup(properties);
    }

    function done() {
        tests.end_wait();
    }

    function generate_tests(func, args, properties) {
        forEach(args, function(x, i)
                {
                    var name = x[0];
                    test(function()
                         {
                             func.apply(this, x.slice(1));
                         }, 
                         name, 
                         Array.isArray(properties) ? properties[i] : properties);
                });
    }

    function on_event(object, event, callback)
    {
      object.addEventListener(event, callback, false);
    }

    expose(test, 'test');
    expose(async_test, 'async_test');
    expose(generate_tests, 'generate_tests');
    expose(setup, 'setup');
    expose(done, 'done');
    expose(on_event, 'on_event');

    /*
     * 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;
    }

    /*
     * Convert a value to a nice, human-readable string
     */
    function format_value(val)
    {
        if (Array.isArray(val))
        {
            return "[" + val.map(format_value).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.  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)
            {
                switch (val.nodeType)
                {
                case Node.ELEMENT_NODE:
                    var ret = "<" + val.tagName.toLowerCase();
                    for (var i = 0; i < val.attributes.length; i++)
                    {
                        ret += " " + val.attributes[i].name + '="' + val.attributes[i].value + '"';
                    }
                    ret += ">" + val.innerHTML + "</" + val.tagName.toLowerCase() + ">";
                    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 <!--" + truncate(val.data, 60) + "-->";
                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";
                }
            }

            // Fall through to default
        default:
            return typeof val + ' "' + truncate(String(val), 60) + '"';
        }
    }
    expose(format_value, "format_value");

    /*
     * Assertions
     */

    function assert_true(actual, description)
    {
        assert(actual === true, "assert_true", description,
                                "expected true got ${actual}", {actual:actual});
    };
    expose(assert_true, "assert_true");

    function assert_false(actual, description)
    {
        assert(actual === false, "assert_false", description,
                                 "expected false got ${actual}", {actual:actual});
    };
    expose(assert_false, "assert_false");

    function same_value(x, y) {
        if (y !== y)
        {
            //NaN case
            return x !== x;
        }
        else if (x === 0 && y === 0) {
            //Distinguish +0 and -0
            return 1/x === 1/y;
        }
        else
        {
            //typical case
            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.indexOf(actual) != -1, "assert_in_array", description,
                                               "value ${actual} not in array ${expected}",
                                               {actual:actual, expected:expected});
    }
    expose(assert_in_array, "assert_in_array");
{'fraction_non_alphanumeric': 0.09704347826086956, 'fraction_numerical': 0.009304347826086957, 'mean_word_length': 2.1231583950030553, 'pattern_counts': {'":': 10, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 10, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5345750', 'n_tokens_mistral': 12436, 'n_tokens_neox': 11802, 'n_words': 4664}