michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0:
michael@0: var gPageCompleted;
michael@0: var GLOBAL = this + '';
michael@0:
michael@0: // Variables local to jstests harness.
michael@0: var jstestsTestPassesUnlessItThrows = false;
michael@0: var jstestsRestoreFunction;
michael@0: var jstestsOptions;
michael@0:
michael@0: /*
michael@0: * Signals to this script that the current test case should be considered to
michael@0: * have passed if it doesn't throw an exception.
michael@0: *
michael@0: * Overrides the same-named function in shell.js.
michael@0: */
michael@0: function testPassesUnlessItThrows() {
michael@0: jstestsTestPassesUnlessItThrows = true;
michael@0: }
michael@0:
michael@0: /*
michael@0: * Requests to load the given JavaScript file before the file containing the
michael@0: * test case.
michael@0: */
michael@0: function include(file) {
michael@0: outputscripttag(file, {language: "type", mimetype: "text/javascript"});
michael@0: }
michael@0:
michael@0: /*
michael@0: * Sets a restore function which restores the standard built-in ECMAScript
michael@0: * properties after a destructive test case, and which will be called after
michael@0: * the test case terminates.
michael@0: */
michael@0: function setRestoreFunction(restore) {
michael@0: jstestsRestoreFunction = restore;
michael@0: }
michael@0:
michael@0: function htmlesc(str) {
michael@0: if (str == '<')
michael@0: return '<';
michael@0: if (str == '>')
michael@0: return '>';
michael@0: if (str == '&')
michael@0: return '&';
michael@0: return str;
michael@0: }
michael@0:
michael@0: function DocumentWrite(s)
michael@0: {
michael@0: try
michael@0: {
michael@0: var msgDiv = document.createElement('div');
michael@0: msgDiv.innerHTML = s;
michael@0: document.body.appendChild(msgDiv);
michael@0: msgDiv = null;
michael@0: }
michael@0: catch(excp)
michael@0: {
michael@0: document.write(s + '
\n');
michael@0: }
michael@0: }
michael@0:
michael@0: function print() {
michael@0: var s = '';
michael@0: var a;
michael@0: for (var i = 0; i < arguments.length; i++)
michael@0: {
michael@0: a = arguments[i];
michael@0: s += String(a) + ' ';
michael@0: }
michael@0:
michael@0: if (typeof dump == 'function')
michael@0: {
michael@0: dump( s + '\n');
michael@0: }
michael@0:
michael@0: s = s.replace(/[<>&]/g, htmlesc);
michael@0:
michael@0: DocumentWrite(s);
michael@0: }
michael@0:
michael@0: function writeHeaderToLog( string ) {
michael@0: string = String(string);
michael@0:
michael@0: if (typeof dump == 'function')
michael@0: {
michael@0: dump( string + '\n');
michael@0: }
michael@0:
michael@0: string = string.replace(/[<>&]/g, htmlesc);
michael@0:
michael@0: DocumentWrite( "
" + string + "
" );
michael@0: }
michael@0:
michael@0: function writeFormattedResult( expect, actual, string, passed ) {
michael@0: string = String(string);
michael@0:
michael@0: if (typeof dump == 'function')
michael@0: {
michael@0: dump( string + '\n');
michael@0: }
michael@0:
michael@0: string = string.replace(/[<>&]/g, htmlesc);
michael@0:
michael@0: var s = ""+ string ;
michael@0: s += "" ;
michael@0: s += ( passed ) ? " " + PASSED
michael@0: : " " + FAILED + expect;
michael@0:
michael@0: DocumentWrite( s + "
" );
michael@0: return passed;
michael@0: }
michael@0:
michael@0: window.onerror = function (msg, page, line)
michael@0: {
michael@0: jstestsTestPassesUnlessItThrows = false;
michael@0:
michael@0: // Restore options in case a test case used this common variable name.
michael@0: options = jstestsOptions;
michael@0:
michael@0: // Restore the ECMAScript environment after potentially destructive tests.
michael@0: if (typeof jstestsRestoreFunction === "function") {
michael@0: jstestsRestoreFunction();
michael@0: }
michael@0:
michael@0: optionsPush();
michael@0:
michael@0: if (typeof DESCRIPTION == 'undefined')
michael@0: {
michael@0: DESCRIPTION = 'Unknown';
michael@0: }
michael@0: if (typeof EXPECTED == 'undefined')
michael@0: {
michael@0: EXPECTED = 'Unknown';
michael@0: }
michael@0:
michael@0: var testcase = new TestCase("unknown-test-name", DESCRIPTION, EXPECTED, "error");
michael@0:
michael@0: if (document.location.href.indexOf('-n.js') != -1)
michael@0: {
michael@0: // negative test
michael@0: testcase.passed = true;
michael@0: }
michael@0:
michael@0: testcase.reason = page + ':' + line + ': ' + msg;
michael@0:
michael@0: reportFailure(msg);
michael@0:
michael@0: optionsReset();
michael@0: };
michael@0:
michael@0: function gc()
michael@0: {
michael@0: try
michael@0: {
michael@0: SpecialPowers.forceGC();
michael@0: }
michael@0: catch(ex)
michael@0: {
michael@0: print('gc: ' + ex);
michael@0: }
michael@0: }
michael@0:
michael@0: function jsdgc()
michael@0: {
michael@0: try
michael@0: {
michael@0: var jsdIDebuggerService = SpecialPowers.Ci.jsdIDebuggerService;
michael@0: var service = SpecialPowers.Cc['@mozilla.org/js/jsd/debugger-service;1'].
michael@0: getService(jsdIDebuggerService);
michael@0: service.GC();
michael@0: }
michael@0: catch(ex)
michael@0: {
michael@0: print('jsdgc: ' + ex);
michael@0: }
michael@0: }
michael@0:
michael@0: function quit()
michael@0: {
michael@0: }
michael@0:
michael@0: function options(aOptionName)
michael@0: {
michael@0: // return value of options() is a comma delimited list
michael@0: // of the previously set values
michael@0:
michael@0: var value = '';
michael@0: for (var optionName in options.currvalues)
michael@0: {
michael@0: value += optionName + ',';
michael@0: }
michael@0: if (value)
michael@0: {
michael@0: value = value.substring(0, value.length-1);
michael@0: }
michael@0:
michael@0: if (aOptionName) {
michael@0: if (!(aOptionName in SpecialPowers.Cu)) {
michael@0: // This test is trying to flip an unsupported option, so it's
michael@0: // likely no longer testing what it was supposed to. Fail it
michael@0: // hard.
michael@0: throw "Unsupported JSContext option '"+ aOptionName +"'";
michael@0: }
michael@0:
michael@0: if (options.currvalues.hasOwnProperty(aOptionName))
michael@0: // option is set, toggle it to unset
michael@0: delete options.currvalues[aOptionName];
michael@0: else
michael@0: // option is not set, toggle it to set
michael@0: options.currvalues[aOptionName] = true;
michael@0:
michael@0: SpecialPowers.Cu[aOptionName] =
michael@0: options.currvalues.hasOwnProperty(aOptionName);
michael@0: }
michael@0:
michael@0: return value;
michael@0: }
michael@0:
michael@0: // Keep a reference to options around so that we can restore it after running
michael@0: // a test case, which may have used this common name for one of its own
michael@0: // variables.
michael@0: jstestsOptions = options;
michael@0:
michael@0: function optionsInit() {
michael@0:
michael@0: // hash containing the set options.
michael@0: options.currvalues = {
michael@0: strict: true,
michael@0: werror: true,
michael@0: strict_mode: true
michael@0: };
michael@0:
michael@0: // record initial values to support resetting
michael@0: // options to their initial values
michael@0: options.initvalues = {};
michael@0:
michael@0: // record values in a stack to support pushing
michael@0: // and popping options
michael@0: options.stackvalues = [];
michael@0:
michael@0: for (var optionName in options.currvalues)
michael@0: {
michael@0: var propName = optionName;
michael@0:
michael@0: if (!(propName in SpecialPowers.Cu))
michael@0: {
michael@0: throw "options.currvalues is out of sync with Components.utils";
michael@0: }
michael@0: if (!SpecialPowers.Cu[propName])
michael@0: {
michael@0: delete options.currvalues[optionName];
michael@0: }
michael@0: else
michael@0: {
michael@0: options.initvalues[optionName] = true;
michael@0: }
michael@0: }
michael@0: }
michael@0:
michael@0: function gczeal(z)
michael@0: {
michael@0: SpecialPowers.setGCZeal(z);
michael@0: }
michael@0:
michael@0: function jit(on)
michael@0: {
michael@0: }
michael@0:
michael@0: function jsTestDriverBrowserInit()
michael@0: {
michael@0:
michael@0: if (typeof dump != 'function')
michael@0: {
michael@0: dump = print;
michael@0: }
michael@0:
michael@0: optionsInit();
michael@0: optionsClear();
michael@0:
michael@0: if (document.location.search.indexOf('?') != 0)
michael@0: {
michael@0: // not called with a query string
michael@0: return;
michael@0: }
michael@0:
michael@0: var properties = {};
michael@0: var fields = document.location.search.slice(1).split(';');
michael@0: for (var ifield = 0; ifield < fields.length; ifield++)
michael@0: {
michael@0: var propertycaptures = /^([^=]+)=(.*)$/.exec(fields[ifield]);
michael@0: if (!propertycaptures)
michael@0: {
michael@0: properties[fields[ifield]] = true;
michael@0: }
michael@0: else
michael@0: {
michael@0: properties[propertycaptures[1]] = decodeURIComponent(propertycaptures[2]);
michael@0: if (propertycaptures[1] == 'language')
michael@0: {
michael@0: // language=(type|language);mimetype
michael@0: properties.mimetype = fields[ifield+1];
michael@0: }
michael@0: }
michael@0: }
michael@0:
michael@0: if (properties.language != 'type')
michael@0: {
michael@0: try
michael@0: {
michael@0: properties.version = /javascript([.0-9]+)/.exec(properties.mimetype)[1];
michael@0: }
michael@0: catch(ex)
michael@0: {
michael@0: }
michael@0: }
michael@0:
michael@0: if (!properties.version && navigator.userAgent.indexOf('Gecko/') != -1)
michael@0: {
michael@0: // If the version is not specified, and the browser is Gecko,
michael@0: // use the default version corresponding to the shell's version(0).
michael@0: // See https://bugzilla.mozilla.org/show_bug.cgi?id=522760#c11
michael@0: // Otherwise adjust the version to match the suite version for 1.6,
michael@0: // and later due to the use of for-each, let, yield, etc.
michael@0: //
michael@0: // Note that js1_8, js1_8_1, and js1_8_5 are treated identically in
michael@0: // the browser.
michael@0: if (properties.test.match(/^js1_6/))
michael@0: {
michael@0: properties.version = '1.6';
michael@0: }
michael@0: else if (properties.test.match(/^js1_7/))
michael@0: {
michael@0: properties.version = '1.7';
michael@0: }
michael@0: else if (properties.test.match(/^js1_8/))
michael@0: {
michael@0: properties.version = '1.8';
michael@0: }
michael@0: }
michael@0:
michael@0: // default to language=type;text/javascript. required for
michael@0: // reftest style manifests.
michael@0: if (!properties.language)
michael@0: {
michael@0: properties.language = 'type';
michael@0: properties.mimetype = 'text/javascript';
michael@0: }
michael@0:
michael@0: gTestPath = properties.test;
michael@0:
michael@0: if (properties.gczeal)
michael@0: {
michael@0: gczeal(Number(properties.gczeal));
michael@0: }
michael@0:
michael@0: /*
michael@0: * since the default setting of jit changed from false to true
michael@0: * in http://hg.mozilla.org/tracemonkey/rev/685e00e68be9
michael@0: * bisections which depend upon jit settings can be thrown off.
michael@0: * default jit(false) when not running jsreftests to make bisections
michael@0: * depending upon jit settings consistent over time. This is not needed
michael@0: * in shell tests as the default jit setting has not changed there.
michael@0: */
michael@0:
michael@0: if (properties.jit || !document.location.href.match(/jsreftest.html/))
michael@0: jit(properties.jit);
michael@0:
michael@0: var testpathparts = properties.test.split(/\//);
michael@0:
michael@0: if (testpathparts.length < 3)
michael@0: {
michael@0: // must have at least suitepath/subsuite/testcase.js
michael@0: return;
michael@0: }
michael@0:
michael@0: document.write('' + properties.test + '<\/title>');
michael@0:
michael@0: // XXX bc - the first document.written script is ignored if the protocol
michael@0: // is file:. insert an empty script tag, to work around it.
michael@0: document.write('');
michael@0:
michael@0: // Output script tags for shell.js, then browser.js, at each level of the
michael@0: // test path hierarchy.
michael@0: var prepath = "";
michael@0: var i = 0;
michael@0: for (end = testpathparts.length - 1; i < end; i++) {
michael@0: prepath += testpathparts[i] + "/";
michael@0: outputscripttag(prepath + "shell.js", properties);
michael@0: outputscripttag(prepath + "browser.js", properties);
michael@0: }
michael@0:
michael@0: // Output the test script itself.
michael@0: outputscripttag(prepath + testpathparts[i], properties);
michael@0:
michael@0: // Finally output the driver-end script to advance to the next test.
michael@0: outputscripttag('js-test-driver-end.js', properties);
michael@0: return;
michael@0: }
michael@0:
michael@0: function outputscripttag(src, properties)
michael@0: {
michael@0: if (!src)
michael@0: {
michael@0: return;
michael@0: }
michael@0:
michael@0: var s = '