aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorDavid Fugate <dfugate@microsoft.com>2011-09-12 11:25:23 -0700
committerDavid Fugate <dfugate@microsoft.com>2011-09-12 11:25:23 -0700
commit807a3ba1b741b6e4b763855b00bfeaec2a88c450 (patch)
treeaa479fc421eba4d3228150653572cab1f153f56b /tools
parent2c16b93983cce6aa72458f82d801a4702d61506a (diff)
parent68d44bb52a11ba7a9a2f08ad2ea45e23f0224a7a (diff)
Merge.
Diffstat (limited to 'tools')
-rw-r--r--tools/SputnikConverter/ES5TestCase.cs2
-rw-r--r--tools/converter/browserPlatform.js171
-rw-r--r--tools/converter/convert.js512
-rw-r--r--tools/converter/utils.js118
-rw-r--r--tools/converter/v8PosixPlatform.js368
-rw-r--r--tools/test262.py531
6 files changed, 1701 insertions, 1 deletions
diff --git a/tools/SputnikConverter/ES5TestCase.cs b/tools/SputnikConverter/ES5TestCase.cs
index db39133a7..bbe4ba046 100644
--- a/tools/SputnikConverter/ES5TestCase.cs
+++ b/tools/SputnikConverter/ES5TestCase.cs
@@ -139,7 +139,7 @@ namespace Microsoft.Sputnik.Interop.ParserEngine
}
FileStream fs = new FileStream(destination.Remove(destination.LastIndexOf("\\")) + globalScopeFileName, FileMode.Create, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
- sw.Write("this.GlobalScopeTests = this.GlobalScopeTests || new Array();\n");
+ sw.Write("this.GlobalScopeTests = this.GlobalScopeTests || {};\n");
sw.Flush();
sw.Close();
fs.Close();
diff --git a/tools/converter/browserPlatform.js b/tools/converter/browserPlatform.js
new file mode 100644
index 000000000..ff9e392bd
--- /dev/null
+++ b/tools/converter/browserPlatform.js
@@ -0,0 +1,171 @@
+// Copyright 2011 by Google, Inc. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+
+/**
+ * Each implementation of *Platform.js abstracts the underlying OS and JS
+ * engine peculiarities.
+ *
+ * <p>The implementation here is for running in many browsers, and so
+ * should assume the platform may be anything from ES3+Reality
+ * forward, including somewhat non-conformant implementations.
+ */
+(function (global) {
+ "use strict";
+
+ /////////////////// Development Switches /////////////////
+
+ var VERBOSE = true;
+
+ // Affects side effecting os operations,
+ // currently only platform.writeSpawn and platform.mkdir.
+ var DRY_RUN = false;
+
+ // When converting paths to path strings, should the pathstring be
+ // relative to the TEST262_ROOT, or should it be relative to the
+ // current working directory?
+ var ABSOLUTE_PATHSTR = false;
+
+ ////////////////////////////////////////////////////////
+
+ global.t262 = global.t262 || {};
+
+ var platform = global.t262.platform = {};
+
+ var utils = global.t262.utils;
+ var forEach = utils.forEach;
+ var map = utils.map;
+
+ // Someday this will be https:
+ var ABS_ROOT_STR = 'http://test262.ecmascript.org/';
+
+ var TEST262_ROOT_STR = ABSOLUTE_PATHSTR ? ABS_ROOT : '';
+
+ var HARNESS_DIR = ['resources', 'scripts', 'global'];
+ platform.HARNESS_DIR = HARNESS_DIR;
+
+ var CONVERTER_DIR = ['resources', 'scripts', 'global'];
+ platform.CONVERTER_DIR = CONVERTER_DIR;
+
+ var PLATFORM_PATHS = [
+ HARNESS_DIR.concat('jquery-1.4.2.min.js'),
+ CONVERTER_DIR.concat('utils.js'),
+ CONVERTER_DIR.concat('v8PosixPlatform.js')
+ ];
+ platform.PLATFORM_PATHS = PLATFORM_PATHS;
+
+ ////////////////// Needed for building and running tests //////////////
+
+ /**
+ *
+ */
+ function validatePath(path) {
+ var pathStr = path.join('/');
+ forEach(path, function(segment) {
+ if (segment === '') {
+ throw new Error('A path cannot have empty segments: ' + pathStr);
+ }
+ if (segment === '/') {
+ throw new Error('Path insufficiently parsed: ' + pathStr);
+ }
+ if (segment === '..') {
+ throw new Error('Cannot use "..": ' + pathStr);
+ }
+ });
+ return path;
+ }
+
+ /**
+ * Converts a relPath to a relPathStr.
+ *
+ * A relPath is an array of filenames relative to some base onto
+ * which it will be concatenated before use.
+ */
+ function toRelPathStr(relPath) {
+ validatePath(relPath);
+ return relPath.join('/');
+ }
+ platform.toRelPathStr = toRelPathStr;
+
+ /**
+ * Converts a path to a pathStr.
+ *
+ * A path is an array of filenames relative to TEST262_ROOT. A
+ * pathStr is a (possibly fully qualified string) for referring to
+ * that file on the current platform, according to the operations
+ * in this *Platform.js file.
+ */
+ function toPathStr(path) {
+ validatePath(path);
+ return TEST262_ROOT_STR + path.join('/');
+ }
+ platform.toPathStr = toPathStr;
+
+ /**
+ * Returns the text found at path, with newlines normalized and
+ * any initial BOM (Unicode Byte Order Mark) removed.
+ *
+ * <p>Note that sync remote reading is a terrible idea, but that
+ * the way test262 was designed and it's hard to change after the
+ * fact.
+ */
+ function getText(path) {
+ var text;
+ $.ajax({
+ async: false,
+ url: toPathStr(path),
+ success: function(s) { text = s; }
+ });
+ text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
+ if (text.charCodeAt(0) === 0xfeff) { return text.substring(1); }
+ return text;
+ }
+ platform.getText = getText;
+
+ /**
+ * How one JavaScript script possibly spawns another and possibly
+ * redirects its printed form to a chosen file (or resource).
+ *
+ * <p>For example, if !DRY_RUN, then<pre>
+ * writeSpawn([], 'print(+arguments[0] + +arguments[1]);', ['3', '5'])
+ * </pre>
+ * should return the string "8" if writeSpawn decides to spawn.
+ *
+ * @param scriptPaths An array of path arrays of JavaScript source
+ * files to be loaded into the spawned JS engine (in addition to
+ * the spawning platform file) if we are indeed spawning.
+ * @param opt_src A Program to be evaluated in an environment in
+ * which "arguments" is bound to the list of strings provided by
+ * opt_args. If spawned, the result is whatever the program writes
+ * to its stdout. On platforms (like SES) where this can be a
+ * safely confining evaluation, it should be. The implementation
+ * here is not safe.
+ * @param opt_args A list of strings to be bound to top-level
+ * 'arguments' both in opt_src and in the possibly spawed scripts.
+ * @param opt_targetPath A path array naming a file where the
+ * result of opt_src should be written. In the browser context, the
+ * result is PUT (or should that be POST), using XHR, to the target
+ * resource.
+ * @param opt_spawn_required If truthy, forces spawning.
+ * @returns If there is a target, then the null string. Otherwise,
+ * the string result of evaluating opt_src.
+ */
+ function writeSpawn(scriptPaths,
+ opt_src,
+ opt_args,
+ opt_targetPath,
+ opt_spawn_required,
+ opt_forceNonStrict) {
+ "TBD"();
+ if (VERBOSE || DRY_RUN) { "TBD"(); }
+ if (DRY_RUN) { return ''; }
+
+ return "TBD";
+ }
+ platform.writeSpawn = writeSpawn;
+
+
+ ////////////////// Only needed for running tests //////////////////////
+
+
+ })(this);
diff --git a/tools/converter/convert.js b/tools/converter/convert.js
new file mode 100644
index 000000000..902551e72
--- /dev/null
+++ b/tools/converter/convert.js
@@ -0,0 +1,512 @@
+// Copyright 2011 by Google, Inc. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+
+(function(global) {
+ "use strict";
+
+ var t262 = global.t262;
+
+ var platform = t262.platform;
+ var toRelPathStr = platform.toRelPathStr;
+ var toPathStr = platform.toPathStr;
+ var toRelPath = platform.toRelPath;
+ var toPath = platform.toPath;
+
+ var utils = t262.utils;
+ var forEach = utils.forEach;
+ var map = utils.map;
+ var filter = utils.filter;
+ var keys = utils.keys;
+ var trim = utils.trim;
+ var regExp = utils.regExp;
+
+ var CONTRIB_DIRS = [
+ ['test', 'suite', 'other'],
+ ['test', 'suite', 'sputnik', 'Conformance'],
+ ['test', 'suite', 'ietestcenter']
+ ];
+
+ var CONVERTED_DIR = ['test', 'suite', 'converted'];
+
+ var OUT_DIR = ['website', 'resources', 'scripts', 'testcases2'];
+
+ var CONVERT_PATH = platform.CONVERTER_DIR.concat('convert.js');
+
+/////////////////////////////////////////////////////////////////
+
+ var headerPattern = /(?:(?:\/\/.*)?\s*\n)*/;
+ var captureCommentPattern = /\/\*\*?((?:\s|\S)*?)\*\/\s*\n/;
+ var anyPattern = /(?:\s|\S)*/;
+ var blanksPattern = /(?:\s|\n)*/;
+
+ // Should match anything
+ var testEnvelopePattern =
+ regExp('^(', headerPattern,
+ ')(?:', captureCommentPattern,
+ ')?(', anyPattern,
+ ')$');
+
+ var registerPattern =
+ regExp('^(', anyPattern, '?)(',
+ /ES5Harness\.registerTest\s*\(\s*\{/, anyPattern,
+ /\}\s*\)/, ')',
+ /\s*;?(?:\s|\n)*$/);
+
+ // Matches a named function. Captures both the name and the body.
+ var captureFuncNameBodyPattern =
+ regExp(/^function\s+(\w*)\(\s*\)\s*\{/,
+ '(', anyPattern, ')',
+ /;?/, blanksPattern,
+ /\}$/);
+
+ var captureExprBodyPattern =
+ regExp(/^return\s+/,
+ '(', anyPattern, '?)',
+ /;$/);
+
+ var capturePredicatePattern =
+ regExp(/^if\s+\((.*?)\)\s*\{/, blanksPattern,
+ /return\s+true;?/, blanksPattern,
+ /\}$/);
+
+/////////////////////////////////////////////////////////////////
+
+ /**
+ * Strip the left margin "*"s that are found in the body of a
+ * multiline doc-comment like this one.
+ */
+ function stripStars(text) {
+ return trim(text.replace(/\s*\n\s*\*\s?/g, '\n'));
+ }
+
+ /**
+ * Parses the source of a test262 test case file into a JSON
+ * envelope record.
+ *
+ * <p>The input can be in old sputnik or ietestcenter style, or in
+ * the canonical test262 style. In all cases, we have an optional
+ * header, an optional "/*" comment possibly containing properties
+ * of the form<pre>
+ * @propName: propValue;
+ * </pre>which populate the test record. This is followed by the
+ * rest of the text, which is the test itself. In the case of an
+ * ietestcenter style test, this is followed by a call to
+ * <code>ES5Harness\.registerTest</code> to register a test record.
+ */
+ function parseTestEnvelope(src, name) {
+ var envelope = { testRecord: {} };
+ var envelopeMatch = testEnvelopePattern.exec(src);
+ if (!envelopeMatch) {
+ // Can't happen?
+ throw new Error('unrecognized: ' + name);
+ }
+ envelope.header = trim(envelopeMatch[1]);
+
+ if (envelopeMatch[2]) {
+ var propTexts = envelopeMatch[2].split(/\s*\n\s*\*\s*@/);
+ envelope.comment = stripStars(propTexts.shift()), // notice side effect
+ forEach(propTexts, function(propText) {
+ var propName = propText.match(/^\w+/)[0];
+ var propVal = propText.substring(propName.length);
+ // strip optional initial colon or final semicolon.
+ // The initial colon is only stripped if it comes immediately
+ // after the identifier with no intervening whitespace.
+ propVal = propVal.replace(/^:\s*/, '').replace(/;\s*$/, '');
+ propVal = stripStars(propVal);
+ if (propName in envelope.testRecord) {
+ throw new Error('duplicate: ' + propName);
+ }
+ envelope.testRecord[propName] = propVal;
+ });
+ }
+ envelope.rest = envelopeMatch[3]; // Do not trim
+
+ var registerMatch = registerPattern.exec(envelope.rest);
+ if (registerMatch) {
+ envelope.rest = trim(registerMatch[1]);
+ envelope.registerExpr = trim(registerMatch[2]);
+ } else if (envelope.rest.indexOf('ES5Harness.registerTest') >= 0) {
+ print(' \n--header---\n|' + envelope.header +
+ '|\n--rest-----\n|' + envelope.rest +
+ '|\n--harness--\n|' + envelope.registerExpr +
+ '|\n-----------\n');
+ throw new Error('Malformed harness? ' + name);
+ }
+ return envelope;
+ }
+
+ /**
+ * Given a function that indicates success by returning a truthy
+ * value, return the source for a Program that, when evaluated in
+ * the environment the function assumes, will behave the same as
+ * calling that function in that environment and asserting the
+ * truthiness of the result.
+ *
+ * <p>Programs do not conveniently return any value, even their
+ * completion value, so Programs in canonical test262 style instead
+ * indicate success simply by completing normally, i.e., without
+ * throwing anything. The conversion assumes a one argument
+ * <code>runTestCase</code> function which calls it function
+ * argument and throws an indication of test failure iff that
+ * function returns a falsy argument.
+ *
+ * <p>Unless it specifies otherwise, the Program source may be
+ * executed strict and/or non-strict, and it may be exeuted within
+ * the try block of a try/catch or try/catch finally, i.e., as a
+ * Block rather than as a Program.
+ */
+ function functionToProgramSrc(func) {
+ var funcSrc = '' + func;
+ var cfnbMatch = captureFuncNameBodyPattern.exec(funcSrc);
+ if (!cfnbMatch) {
+ throw new Error('Could not recognize: "' + funcSrc + '"');
+ }
+ var name = trim(cfnbMatch[1]);
+ var body = trim(cfnbMatch[2]);
+
+ // Look for special cases
+
+ var cebMatch = captureExprBodyPattern.exec(body);
+ if (cebMatch) {
+ return 'assertTruthy(' + trim(cebMatch[1]) + ');';
+ }
+
+ var cpMatch = capturePredicatePattern.exec(body);
+ if (cpMatch) {
+ return 'assertTruthy(' + trim(cpMatch[1]) + ');';
+ }
+
+ // General case
+
+ return funcSrc + '\n' +
+ 'runTestCase(' + name + ');';
+ }
+
+ /**
+ * Given an ietestcenter style test, this <b>evaluates</b> the
+ * registration expression in order to gather the test record.
+ */
+ function gatherOne(envelope, name) {
+ if (envelope.testRecord) {
+ var propNames = keys(envelope.testRecord);
+ if (propNames.length >= 1) {
+ // This need not be an error. It's just here so we notice the
+ // first time it happens. This would happen if an
+ // ietestcenter style test also had a comment with "@"
+ // property definitions.
+ throw new Error('unexpected in ' + name + ': ' + propNames);
+ }
+ }
+ var testRecords = [];
+
+ // Evaluating!!!!
+ platform.evalExprIn(envelope.registerExpr,
+ {
+ ES5Harness: {
+ registerTest: function(testRecord) {
+ testRecords.push(testRecord);
+ }
+ }
+ },
+ 'forceNonStrict');
+
+ if (testRecords.length !== 1) {
+ // We may lift this restriction in order to support test
+ // generators.
+ throw new Error('not singleton: ' + name);
+ }
+ var testRecord = testRecords[0];
+
+ if (typeof testRecord.test === 'function') {
+ testRecord.test = envelope.rest + '\n' +
+ functionToProgramSrc(testRecord.test);
+ }
+ if ('precondition' in testRecord) {
+ // Only ietestcenter tests currently have preconditions, and they
+ // plan to drop them. So canonical test262 style omits
+ // them.
+ delete testRecord.precondition;
+ }
+
+ return testRecord;
+ }
+
+ /**
+ * Normalizes the properties of testRecord to be the canonical
+ * test262 style properties, that will be assumed by the new test
+ * runners.
+ */
+ function normalizeProps(testRecord) {
+ if (!('strict_only' in testRecord) && testRecord.strict === 1) {
+ testRecord.strict_only = '';
+ delete testRecord.strict;
+ }
+
+ if ('strict_mode_negative' in testRecord) {
+ if (!('strict_only' in testRecord)) {
+ testRecord.strict_only = '';
+ }
+ if (!'negative' in testRecord) {
+ testRecord.negative = testRecord.strict_mode_negative;
+ delete testRecord.strict_mode_negative;
+ }
+ }
+
+ // Note that testRecord.negative is falsy whether negative is
+ // absent or empty.
+ if (!testRecord.negative && 'errortype' in testRecord) {
+ testRecord.negative = testRecord.errortype;
+ delete testRecord.errortype;
+ }
+
+ if (!testRecord.description && testRecord.assertion) {
+ testRecord.description = testRecord.assertion;
+ delete testRecord.assertion;
+ }
+ if (!testRecord.comment && testRecord.assertion) {
+ testRecord.comment = testRecord.assertion;
+ delete testRecord.assertion;
+ }
+ }
+ t262.normalizeProps = normalizeProps;
+
+ /**
+ * Parses the source of a test262 test case file into a normalized
+ * JSON test record.
+ */
+ function parseTestRecord(inBase, relPath, name) {
+ var nextRelPath = relPath.concat([name]);
+ var nextPath = inBase.concat(nextRelPath);
+
+ var src = platform.getText(nextPath);
+ var testRecord;
+ if (!src) {
+ throw new Error('no src: ' + toPathStr(nextPath));
+ }
+ var envelope = parseTestEnvelope(src, name);
+
+ if (envelope.registerExpr) {
+ testRecord = gatherOne(envelope, name);
+ } else {
+ testRecord = envelope.testRecord;
+ if (!testRecord.test) {
+ testRecord.test = envelope.rest;
+ }
+ }
+ delete testRecord.id;
+ delete testRecord.name;
+ testRecord.path = toRelPathStr(nextRelPath);
+ testRecord.header = envelope.header;
+ testRecord.comment = envelope.comment;
+
+ normalizeProps(testRecord);
+ return testRecord;
+ }
+ t262.parseTestRecord = parseTestRecord;
+
+ // The known ones will be rendered first, and in this order.
+ var KNOWN_PROPS = ['section', 'path', 'description',
+ 'strict_only', 'negative'];
+
+ /**
+ * Turns the (assumed) normalized test record into its string form
+ * in canonical test262 style.
+ *
+ * NOTE: This is currently destructive of testRecord. Easy to fix
+ * if it becomes a problem.
+ */
+ function formatTestRecord(testRecord) {
+ var test = testRecord.test;
+ delete testRecord.test;
+
+ function addProp(pname) {
+ if (pname in testRecord) {
+ result += ' * @' + pname;
+ if (testRecord[pname]) {
+ result += ' ' + testRecord[pname].replace(/\n/g, '\n * ');
+ }
+ result += '\n';
+ delete testRecord[pname];
+ }
+ }
+
+ var result = testRecord.header + '\n\n';
+ delete testRecord.header;
+ result += '/**\n';
+ if (testRecord.comment) {
+ result += ' * ' + testRecord.comment.replace(/\n/g, '\n * ') + '\n *\n';
+ }
+ delete testRecord.comment;
+ forEach(KNOWN_PROPS, addProp);
+ forEach(keys(testRecord), addProp);
+ result += ' */\n\n' + test;
+ return result;
+ }
+ t262.formatTestRecord = formatTestRecord;
+
+ /**
+ * Reads the test case at inBaseStr+relPathStr and returns the
+ * source of that test case converted to canonical test262 style.
+ */
+ function convertTest(inBaseStr, relPathStr) {
+ var inBase = toPath(inBaseStr);
+ var relPath = platform.toRelPath(relPathStr);
+ var name = relPath.pop();
+ var testRecord = parseTestRecord(inBase, relPath, name);
+ var result = formatTestRecord(testRecord);
+ return result;
+ }
+ t262.convertTest = convertTest;
+
+ var writeSpawnFailures = [];
+
+ /**
+ * Convert all the testcases found at inBase+relDir to test cases
+ * in canonical test262 style, to be stored at corresponding
+ * positions in outBase+relPath.
+ */
+ function convertAll(inBase, outBase, relPath) {
+ var inPath = inBase.concat(relPath);
+ var outPath = outBase.concat(relPath);
+ platform.mkdir(outPath);
+ forEach(platform.ls(inPath), function(name) {
+ var nextRelPath = relPath.concat([name]);
+ if (platform.isDirectory(inBase.concat(nextRelPath))) {
+ convertAll(inBase, outBase, nextRelPath);
+ } else if (/\.js$/.test(name)) {
+ var outFilePath = outPath.concat([name]);
+ try {
+ platform.writeSpawn(
+ [CONVERT_PATH],
+ 't262.show(t262.convertTest("' + toPathStr(inBase) +
+ '", "' + toRelPathStr(nextRelPath) + '"));',
+ void 0,
+ outFilePath);
+ } catch (err) {
+ writeSpawnFailures.push({
+ error: err,
+ relPath: relPath
+ });
+ }
+ }
+ });
+ }
+ t262.convertAll = convertAll;
+
+ /**
+ * Do all the conversions (from sputnik style, ietestcenter style,
+ * or other to canonical test262 style) matching relPath.
+ */
+ function convert(opt_relPathStr) {
+ var relPath = opt_relPathStr ? toRelPath(opt_relPathStr) : [];
+ writeSpawnFailures = [];
+ forEach(CONTRIB_DIRS, function(srcDir) {
+ convertAll(srcDir, CONVERTED_DIR, relPath);
+ });
+ if (writeSpawnFailures.length >= 1) {
+ print('********* failures **********');
+ forEach(writeSpawnFailures, function(failure) {
+ print(failure.error + ': ' + toRelPathStr(failure.relPath));
+ });
+ throw writeSpawnFailures[0].error;
+ }
+ }
+ t262.convert = convert;
+
+ /**
+ * Reads all the test case records for the section corresponding to
+ * the directory at pathStr, and return a JSON record for a test
+ * case section, as would be uploaded to a browser-based test
+ * runner.
+ */
+ function buildSection(inBaseStr, relPathStr) {
+ var inBase = toPath(inBaseStr);
+ var relPath = platform.toRelPath(relPathStr);
+ var path = inBase.concat(relPath);
+ if (!platform.isDirectory(path)) { throw new Error('not dir: ' + path); }
+
+ var jsFiles = filter(platform.ls(path), function(name) {
+ return /\.js$/.test(name);
+ });
+ var testRecords = map(jsFiles, function(name) {
+ var testRecord = parseTestRecord(inBase, relPath, name);
+
+ delete testRecord.header;
+ delete testRecord.comment;
+ return testRecord;
+ });
+ testRecords = filter(testRecords, function(testRecord) {
+ return testRecord !== null;
+ });
+ return {
+ testCollection: {
+ name: path[path.length -1],
+ numTests: testRecords.length,
+ tests: testRecords
+ }
+ };
+ }
+ t262.buildSection = buildSection;
+
+ /**
+ * Use the test cases at inBase+relPath to build the test
+ * collection portion of the website, at outBase.
+ */
+ function buildAll(inBase, outBase, relPath) {
+ var inPath = inBase.concat(relPath);
+ var hasJS = false;
+ forEach(platform.ls(inPath), function(name) {
+ var nextRelPath = relPath.concat([name]);
+ if (platform.isDirectory(inBase.concat(nextRelPath))) {
+ buildAll(inBase, outBase, nextRelPath);
+ } else if (/\.js$/.test(name)) {
+ hasJS = true;
+ }
+ });
+ if (hasJS) {
+ var name = relPath[relPath.length -1] + '.json';
+ var outFilePath = outBase.concat([name]);
+ try {
+ platform.writeSpawn(
+ [CONVERT_PATH],
+ 't262.showJSON(t262.buildSection("' + toPathStr(inBase) +
+ '", "' + toRelPathStr(nextRelPath) + '"));',
+ void 0,
+ outFilePath);
+ } catch (err) {
+ writeSpawnFailures.push({
+ error: err,
+ path: relPath
+ });
+ }
+ }
+ }
+ t262.buildAll = buildAll;
+
+ /**
+ * Build those test case files for the website corresponding to the
+ * test cases matching relPath.
+ *
+ * <p>Right now it's building from the pre-converted test
+ * files. Once we switch over to converted as the maintained
+ * sources, we should change this.
+ */
+ function buildWebSite(opt_relPathStr) {
+ var relPath = opt_relPathStr ? toRelPath(opt_relPathStr) : [];
+ writeSpawnFailures = [];
+ forEach(CONTRIB_DIRS, function(srcDir) {
+ buildAll(srcDir, OUT_DIR, relPath);
+ });
+// buildAll(CONVERTED_DIR, OUT_DIR, relPath);
+ if (writeSpawnFailures.length >= 1) {
+ print('********* failures **********');
+ forEach(writeSpawnFailures, function(failure) {
+ print(failure.error + ': ' + toRelPathStr(failure.relPath));
+ });
+ throw writeSpawnFailures[0].error;
+ }
+ }
+ t262.buildWebSite = buildWebSite;
+
+ })(this);
diff --git a/tools/converter/utils.js b/tools/converter/utils.js
new file mode 100644
index 000000000..ba96748ec
--- /dev/null
+++ b/tools/converter/utils.js
@@ -0,0 +1,118 @@
+// Copyright 2011 by Google, Inc. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+
+/**
+ * General conveniences, including some functionality available in ES5
+ * but not ES3.
+ *
+ * <p>This file must be able to run in many browsers, and so should
+ * assume the platform may be anything from ES3+Reality forward,
+ * including somewhat non-conformant implementations. It must also be
+ * able to run in a non-browser environment, such as from the command
+ * line.
+ *
+ * <p>The conveniences that are analogs of similar ES5 features are
+ * not full emulations, but only emulations of the portion of their
+ * semantics we need.
+ */
+(function(global) {
+ "use strict";
+
+ global.t262 = global.t262 || {};
+
+ var utils = global.t262.utils = global.t262.utils || {};
+
+ ////////////////////////////////////////////////////////
+
+ /**
+ * Like ES5 call.bind([].forEach)(list, func), but supporting fewer
+ * optional arguments.
+ */
+ function forEach(list, func) {
+ for (var i = 0, len = list.length; i < len; i++) {
+ func(list[i], i);
+ }
+ }
+ utils.forEach = forEach;
+
+ /**
+ * Like ES5 call.bind([].map)(list, func), but supporting fewer
+ * optional arguments.
+ */
+ function map(list, func) {
+ var result = [];
+ for (var i = 0, len = list.length; i < len; i++) {
+ result.push(func(list[i], i));
+ }
+ return result;
+ }
+ utils.map = map;
+
+ /**
+ * Like ES5 call.bind([].filter)(list, pred), but supporting fewer
+ * optional arguments.
+ */
+ function filter(list, pred) {
+ var result = [];
+ for (var i = 0, len = list.length; i < len; i++) {
+ if (pred(list[i], i)) { result.push(list[i]); }
+ }
+ return result;
+ }
+ utils.filter = filter;
+
+ /**
+ * Like ES5 Object.keys(obj).
+ */
+ function keys(obj) {
+ var result = [];
+ var hop = {}.hasOwnProperty;
+ for (var k in obj) {
+ if (hop.call(obj, k)) { result.push(k); }
+ }
+ return result;
+ }
+ utils.keys = keys;
+
+ /**
+ * Like ES5 call.bind(''.trim)(string).
+ */
+ function trim(str) {
+ return str.replace(/^\s*/, '').replace(/\s*$/, '');
+ }
+ utils.trim = trim;
+
+ /**
+ * Appends a bunch of RegExps together into a single RegExp,
+ * solving both the RegExp-one-liner problem and the doubled
+ * backslash problem when composing literal strings.
+ *
+ * <p>The arguments can be any mixture of RegExps and strings. By
+ * expressing the portions that should be well formed regexps as
+ * regexps, we catch well-formedness errors within such a portion
+ * separately. The strings are added as is without escaping --
+ * BEWARE. By not escaping the strings, we can use them to
+ * represent the individually unbalanced fragments, like capturing
+ * parens, around other regexps. If arguments[0] is a RegExp, we
+ * use its flags on the resuting RegExp.
+ *
+ * <p>Not platform dependent, so does not really belong in this
+ * file.
+ */
+ function regExp(var_args) {
+ var args = [].slice.call(arguments, 0);
+ var reSrc = map(args, function(arg) {
+ return (typeof arg === 'string') ? arg : arg.source;
+ }).join('');
+ var flags = '';
+ if (typeof args[0] === 'object') {
+ var parts = (''+args[0]).split('/');
+ flags = parts[parts.length -1];
+ }
+ return new RegExp(reSrc, flags);
+ }
+ utils.regExp = regExp;
+
+
+ })(this); \ No newline at end of file
diff --git a/tools/converter/v8PosixPlatform.js b/tools/converter/v8PosixPlatform.js
new file mode 100644
index 000000000..0864fd732
--- /dev/null
+++ b/tools/converter/v8PosixPlatform.js
@@ -0,0 +1,368 @@
+// Copyright 2011 by Google, Inc. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+
+/**
+ * Each implementation of *Platform.js abstracts the underlying OS and JS
+ * engine peculiarities.
+ *
+ * <p>The implementation here is specific to the v8 shell running on a
+ * Posix platform. Therefore, it may legitimately use ES5 features,
+ * although it generally avoids them for consistency with the rest of
+ * test262.
+ */
+(function (global) {
+ "use strict";
+
+ /////////////////// Development Switches /////////////////
+
+ var VERBOSE = true;
+
+ // Affects side effecting os operations,
+ // currently only platform.writeSpawn and platform.mkdir.
+ var DRY_RUN = false;
+
+ // When converting paths to path strings, should the pathstring be
+ // relative to the TEST262_ROOT, or should it be relative to the
+ // current working directory?
+ var ABSOLUTE_PATHSTR = false;
+
+ ////////////////////////////////////////////////////////
+
+ global.t262 = global.t262 || {};
+
+ var platform = global.t262.platform = {};
+
+ var utils = global.t262.utils;
+ var forEach = utils.forEach;
+ var map = utils.map;
+ var keys = utils.keys;
+ var trim = utils.trim;
+
+ try {
+ read('tools/converter/v8PosixPlatform.js');
+ } catch (err) {
+ throw new Error('Must run in a test262 source root');
+ }
+
+ var ABS_ROOT = trim(os.system('pwd', ['-P'])).split('/');
+
+ var TEST262_ROOT = ABSOLUTE_PATHSTR ? ABS_ROOT : [];
+
+ var TEST262_ROOT_STR = TEST262_ROOT.join('/');
+
+ var HARNESS_DIR = ['test', 'harness'];
+ platform.HARNESS_DIR = HARNESS_DIR;
+
+ var CONVERTER_DIR = ['tools', 'converter'];
+ platform.CONVERTER_DIR = CONVERTER_DIR;
+
+ var PLATFORM_PATHS = [
+ CONVERTER_DIR.concat('utils.js'),
+ CONVERTER_DIR.concat('v8PosixPlatform.js')
+ ];
+
+ ////////////////// Needed for building and running test //////////////
+
+ /**
+ *
+ */
+ function validatePath(path) {
+ var pathStr = path.join('/');
+ forEach(path, function(segment) {
+ if (segment === '') {
+ throw new Error('A path cannot have empty segments: ' + pathStr);
+ }
+ if (segment === '/') {
+ throw new Error('Path insufficiently parsed: ' + pathStr);
+ }
+ if (segment === '..') {
+ throw new Error('Cannot use "..": ' + pathStr);
+ }
+ });
+ return path;
+ }
+
+ /**
+ * Converts a relPath to a relPathStr.
+ *
+ * A relPath is an array of filenames relative to some base onto
+ * which it will be concatenated before use.
+ */
+ function toRelPathStr(relPath) {
+ validatePath(relPath);
+ return relPath.join('/');
+ }
+ platform.toRelPathStr = toRelPathStr;
+
+ /**
+ * Converts a path to a pathStr.
+ *
+ * A path is an array of filenames relative to TEST262_ROOT. A
+ * pathStr is a (possibly fully qualified string) for referring to
+ * that file on the current platform, according to the operations
+ * in this *Platform.js file.
+ */
+ function toPathStr(path) {
+ validatePath(path);
+ return TEST262_ROOT.concat(path).join('/');
+ }
+ platform.toPathStr = toPathStr;
+
+ /**
+ * Returns the text found at path, with newlines normalized and
+ * any initial BOM (Unicode Byte Order Mark) removed.
+ */
+ function getText(path) {
+ var text = read(toPathStr(path));
+ text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
+ if (text.charCodeAt(0) === 0xfeff) { return text.substring(1); }
+ return text;
+ }
+ platform.getText = getText;
+
+ /**
+ *
+ */
+ function bashQuote(str) {
+ var escaped = JSON.stringify(str).replace(/'/g, "\\'");
+ return "$'" + escaped.substring(1, escaped.length -1) + "'";
+ }
+
+ /**
+ * How one JavaScript script possibly spawns another and possibly
+ * redirects its printed form to a chosen file (or resource).
+ *
+ * <p>For example, if !DRY_RUN, then<pre>
+ * platform.writeSpawn([],
+ * 't262.show(+arguments[0] + +arguments[1]);',
+ * ['3', '5'])
+ * </pre>
+ * should emit string "8" to stdout.
+ *
+ * <p>To spawn a platform distinct from the present one -- for
+ * example, as outer v8-based driver can drive a rhino-based child
+ * -- create a distinct object representing that other platform and
+ * invoke its writeSpawn method.
+ *
+ * @param scriptPaths An array of path arrays of JavaScript source
+ * files to be loaded into the spawned JS engine, after
+ * PLATFORM_PATHS, if we are indeed spawning.
+ * @param opt_src A Program to be evaluated in an environment in
+ * which "arguments" is bound to the list of strings provided by
+ * opt_args. If spawned, the result is whatever the program writes
+ * to its stdout. On platforms (like SES) where this can be a
+ * safely confining evaluation, it should be. The implementation
+ * here is not safe.
+ * @param opt_args A list of strings to be bound to top-level
+ * 'arguments' both in opt_src and in the possibly spawed scripts.
+ * @param opt_targetPath A path array naming a file where the
+ * result of opt_src should be written. On v8 currently, if this is
+ * provided, then writeSpawn will spawn, since we have no other way
+ * to implement this functionality. In the browser context, the
+ * result is PUT (or should that be POST), using XHR, to the target
+ * resource.
+ * @param opt_spawn_required If truthy, forces spawning.
+ * @returns If there is a target, then the null string. Otherwise,
+ * the string result of evaluating opt_src.
+ */
+ function writeSpawn(scriptPaths,
+ opt_src,
+ opt_args,
+ opt_targetPath,
+ opt_spawn_required,
+ opt_forceNonStrict) {
+ if (typeof opt_src === 'string' &&
+ !opt_targetPath &&
+ !opt_spawn_required) {
+ var str = '(function(/*var_args*/) { ';
+ if (opt_forceNonStrict !== 'forceNonStrict') {
+ str += '"use strict"; ';
+ }
+ str += opt_src + '\n})';
+ return ''+(1,eval)(str).apply(void 0, opt_args || []);
+ }
+
+ var sys = os.system;
+ if (DRY_RUN) {
+ sys = function(command, args) {
+ print(command + ' ' + args.join(' '));
+ };
+ }
+
+ var allScriptPaths = PLATFORM_PATHS.concat(scriptPaths);
+ var cmd = 'v8 ' + map(allScriptPaths, toPathStr).join(' ');
+
+ if (typeof opt_src === 'string') {
+ cmd += ' -e ' + bashQuote(opt_src);
+ }
+ if (opt_args) {
+ cmd += ' -- ' + map(opt_args, bashQuote).join(' ');
+ }
+
+ if (VERBOSE && !DRY_RUN) { print(cmd); }
+
+ // We write the output to a temporary file for two reasons:
+ // * If the spawned script dies with a useful diagnostic,
+ // os.system will throw an error omitting that diagnostic
+ // text. However, bash ">" will both redirect to the output file
+ // and preserve the error code of the command to the left. Bash
+ // does not preserve the error code with "|" redirection.
+ // * If we actually have a target, we only want to write into it
+ // if our command runs successfully.
+ var tempPathStr = os.system('mktemp', ['-t', 'temp.']).trim();
+ cmd += ' > ' + tempPathStr;
+
+ var result;
+ try {
+ try {
+ result = sys('bash', ['-c', cmd]);
+ } catch (err) {
+ // The error we catch is almost certainly less interesting
+ // than the one unfortunately written to the target file.
+ var message = 'failed: ' + cmd + '\n---\n' + read(tempPathStr);
+ throw new Error(message);
+ }
+ if (opt_targetPath) {
+ sys('cp', [tempPathStr, toPathStr(opt_targetPath)]);
+ }
+ return result;
+ } finally {
+ sys('rm', ['-f', tempPathStr]);
+ }
+ }
+ platform.writeSpawn = writeSpawn;
+
+
+ ////////////////// Only needed for building tests /////////////////////
+
+ /**
+ * Calls a non-strict indirect eval function on exprSrc.
+ *
+ * <p>On platforms (like SES) where this can be a safely confining
+ * evaluation, it should be. The implementation here is not safe.
+ */
+ function evalExprIn(exprSrc, env, opt_forceNonStrict) {
+ var varNames = keys(env);
+ var str = '(function(' + varNames.join(',') + ') {';
+ if (opt_forceNonStrict !== 'forceNonStrict') {
+ str += '"use strict";';
+ }
+ str += ' return (' + exprSrc + '); })';
+ var vals = map(varNames, function(varName) { return env[varName]; });
+ return (1,eval)(str).apply(void 0, vals);
+ }
+ platform.evalExprIn = evalExprIn;
+
+ /**
+ * Converts a relPathStr to a relPath.
+ *
+ * <p>See toRelPathStr.
+ */
+ function toRelPath(relPathStr) {
+ return validatePath(relPathStr.split('/'));
+ }
+ platform.toRelPath = toRelPath;
+
+ /**
+ * Converts a pathStr to a path.
+ *
+ * <p>See toPathStr.
+ */
+ function toPath(pathStr) {
+ if (pathStr[0] === '/') {
+ if (pathStr.indexOf(TEST262_ROOT_STR + '/') !== 0) {
+ throw new Error('"' + pathStr + '" must start with "' +
+ TEST262_ROOT_STR + '/"');
+ }
+ pathStr = pathStr.substring(TEST262_ROOT_STR.length + 1);
+ }
+ return validatePath(pathStr.split('/'));
+ }
+ platform.toPath = toPath;
+
+ /**
+ * Does path name a directory?
+ */
+ function isDirectory(path) {
+ try {
+ os.system('test', ['-d', toPathStr(path)]);
+ return true;
+ } catch (x) {
+ return false;
+ }
+ }
+ platform.isDirectory = isDirectory;
+
+ /**
+ * A list of the filenames found in path, which must name a
+ * directory.
+ */
+ function ls(path) {
+ var pathStr = toPathStr(path);
+ if (!isDirectory(path)) { return []; }
+ var lines;
+ try {
+ lines = trim(os.system('ls', [pathStr]));
+ } catch (err) {
+ throw err;
+ }
+ if (lines === '') { return []; }
+ return lines.split('\n');
+ }
+ platform.ls = ls;
+
+ /**
+ * If the directory does not yet exist, create it.
+ */
+ function mkdir(path) {
+ var pathStr = toPathStr(path);
+ if (DRY_RUN) {
+ print('mkdir ' + pathStr);
+ return;
+ }
+ try {
+ os.mkdirp(pathStr);
+ } catch (err) {
+ print('***could not mkdir: ' + pathStr);
+ throw err;
+ }
+ }
+ platform.mkdir = mkdir;
+
+ /**
+ * Emits the text itself followed by a newline.
+ *
+ * <p>On the v8 shell, this is identical to "print".
+ */
+ var show = global.t262.show = print;
+
+ /**
+ * Emits the jsonRecord serialized as JSON, either compactly or
+ * readably according to VERBOSE.
+ */
+ function showJSON(jsonRecord) {
+ if (VERBOSE) {
+ print(JSON.stringify(jsonRecord, void 0, ' '));
+ } else {
+ print(JSON.stringify(jsonRecord));
+ }
+ }
+ global.t262.showJSON = platform.showJSON = showJSON;
+
+
+ ////////////////// Only needed for running tests //////////////////////
+
+ if (!global.$PRINT) {
+ global.$PRINT = t262.show;
+ }
+
+ if (!global.$INCLUDE) {
+ global.$INCLUDE = function(name) {
+ // does nothing even locally, since the platform independent
+ // include processing picks these up anyway.
+ // load(toPathStr(HARNESS_DIR.concat([name])));
+ };
+ }
+
+ })(this);
diff --git a/tools/test262.py b/tools/test262.py
new file mode 100644
index 000000000..a247e86b6
--- /dev/null
+++ b/tools/test262.py
@@ -0,0 +1,531 @@
+#!/usr/bin/python
+# Copyright 2009 the Sputnik authors. All rights reserved.
+# This code is governed by the BSD license found in the LICENSE file.
+
+
+import logging
+import optparse
+import os
+from os import path
+import platform
+import re
+import subprocess
+import sys
+import tempfile
+import time
+
+
+class Test262Error(Exception):
+
+ def __init__(self, message):
+ self.message = message
+
+
+def ReportError(s):
+ raise Test262Error(s)
+
+
+def BuildOptions():
+ result = optparse.OptionParser()
+ result.add_option("--command", default=None, help="The command-line to run")
+ result.add_option("--tests", default=path.abspath('.'),
+ help="Path to the tests")
+ result.add_option("--cat", default=False, action="store_true",
+ help="Print test source code")
+ result.add_option("--summary", default=False, action="store_true",
+ help="Print summary after running tests")
+ result.add_option("--full-summary", default=False, action="store_true",
+ help="Print summary and test output after running tests")
+ result.add_option("--enable-strict-mode", default=False, action="store_true",
+ help="Run the mode also in ES5 strict mode")
+
+ return result
+
+
+def ValidateOptions(options):
+ if not options.command:
+ ReportError("A --command must be specified.")
+ if not path.exists(options.tests):
+ ReportError("Couldn't find test path '%s'" % options.tests)
+
+
+_PLACEHOLDER_PATTERN = re.compile(r"\{\{(\w+)\}\}")
+_INCLUDE_PATTERN = re.compile(r"\$INCLUDE\(\"(.*)\"\);")
+_SPECIAL_CALL_PATTERN = re.compile(r"\$([A-Z]+)(?=\()")
+
+
+_SPECIAL_CALLS = {
+ 'ERROR': 'testFailed',
+ 'FAIL': 'testFailed',
+ 'PRINT': 'testPrint'
+}
+
+
+def IsWindows():
+ p = platform.system()
+ return (p == 'Windows') or (p == 'Microsoft')
+
+
+def StripHeader(str):
+ while str.startswith('//') and "\n" in str:
+ str = str[str.index("\n")+1:]
+ return str.lstrip()
+
+
+class TempFile(object):
+
+ def __init__(self, suffix="", prefix="tmp", text=False):
+ self.suffix = suffix
+ self.prefix = prefix
+ self.text = text
+ self.fd = None
+ self.name = None
+ self.is_closed = False
+ self.Open()
+
+ def Open(self):
+ (self.fd, self.name) = tempfile.mkstemp(
+ suffix = self.suffix,
+ prefix = self.prefix,
+ text = self.text
+ )
+
+ def Write(self, str):
+ os.write(self.fd, str)
+
+ def Read(self):
+ f = file(self.name)
+ result = f.read()
+ f.close()
+ return result
+
+ def Close(self):
+ if not self.is_closed:
+ self.is_closed = True
+ os.close(self.fd)
+
+ def Dispose(self):
+ try:
+ self.Close()
+ os.unlink(self.name)
+ except OSError, e:
+ logging.error("Error disposing temp file: %s", str(e))
+
+
+class TestResult(object):
+
+ def __init__(self, exit_code, stdout, stderr, case):
+ self.exit_code = exit_code
+ self.stdout = stdout
+ self.stderr = stderr
+ self.case = case
+
+ def ReportOutcome(self, long_format):
+ name = self.case.GetName()
+ if self.HasUnexpectedOutcome():
+ if self.case.IsNegative():
+ print "%s was expected to fail but didn't" % name
+ elif (self.case.strict_mode and self.case.IsStrictModeNegative()):
+ print "%s was expected to fail in strict mode, but didn't" % name
+ else:
+ if long_format:
+ print "=== %s failed ===" % name
+ else:
+ print "%s: " % name
+ out = self.stdout.strip()
+ if len(out) > 0:
+ print "--- output ---"
+ print out
+ err = self.stderr.strip()
+ if len(err) > 0:
+ print "--- errors ---"
+ print err
+ if long_format:
+ print "==="
+ elif self.case.IsNegative():
+ print "%s failed as expected" % name
+ elif self.case.strict_mode:
+ if self.case.IsStrictModeNegative():
+ print "%s failed in strict mode as expected" % name
+ else:
+ print "%s passed in strict mode" % name
+ else:
+ print "%s passed" % name
+
+ def HasFailed(self):
+ return self.exit_code != 0
+
+ def HasUnexpectedOutcome(self):
+ if self.case.IsNegative():
+ return not self.HasFailed()
+ if self.case.IsStrictModeNegative():
+ return not self.HasFailed()
+ else:
+ return self.HasFailed()
+
+
+class TestCase(object):
+
+ def __init__(self, suite, name, full_path, strict_mode=False):
+ self.suite = suite
+ self.name = name
+ self.full_path = full_path
+ self.contents = None
+ self.is_negative = None
+ self.strict_mode = strict_mode
+ self.is_strict_mode_negative = None
+
+ def GetName(self):
+ return path.join(*self.name)
+
+ def GetPath(self):
+ return self.name
+
+ def GetRawContents(self):
+ if self.contents is None:
+ f = open(self.full_path)
+ self.contents = f.read()
+ f.close()
+ return self.contents
+
+ def IsNegative(self):
+ if self.is_negative is None:
+ self.is_negative = ("@negative" in self.GetRawContents())
+ return self.is_negative
+
+ def IsStrictModeNegative(self):
+ if self.strict_mode and self.is_strict_mode_negative is None:
+ self.is_strict_mode_negative = \
+ ("@strict_mode_negative" in self.GetRawContents())
+ return self.is_strict_mode_negative
+
+ def GetSource(self):
+ source = self.suite.GetInclude("framework.js", False) + \
+ self.suite.GetInclude("sta.js", False)
+ source += StripHeader(self.GetRawContents())
+ def IncludeFile(match):
+ return self.suite.GetInclude(match.group(1))
+ source = _INCLUDE_PATTERN.sub(IncludeFile, source)
+ def SpecialCall(match):
+ key = match.group(1)
+ return _SPECIAL_CALLS.get(key, match.group(0))
+ if self.strict_mode:
+ source = '"use strict";\nvar strict_mode = true;\n' + \
+ _SPECIAL_CALL_PATTERN.sub(SpecialCall, source)
+ else:
+ source = "var strict_mode = false; \n" + \
+ _SPECIAL_CALL_PATTERN.sub(SpecialCall, source)
+ return source
+
+ def InstantiateTemplate(self, template, params):
+ def GetParameter(match):
+ key = match.group(1)
+ return params.get(key, match.group(0))
+ return _PLACEHOLDER_PATTERN.sub(GetParameter, template)
+
+ def RunTestIn(self, command_template, tmp):
+ tmp.Write(self.GetSource())
+ tmp.Close()
+ command = self.InstantiateTemplate(command_template, {
+ 'path': tmp.name
+ })
+ (code, out, err) = self.Execute(command)
+ return TestResult(code, out, err, self)
+
+ def Execute(self, command):
+ if IsWindows():
+ args = '"%s"' % command
+ else:
+ args = command.split(" ")
+ stdout = TempFile(prefix="test262-out-")
+ stderr = TempFile(prefix="test262-err-")
+ try:
+ logging.info("exec: %s", str(args))
+ process = subprocess.Popen(
+ args,
+ shell = IsWindows(),
+ stdout = stdout.fd,
+ stderr = stderr.fd
+ )
+ code = process.wait()
+ out = stdout.Read()
+ err = stderr.Read()
+ finally:
+ stdout.Dispose()
+ stderr.Dispose()
+ return (code, out, err)
+
+ def Run(self, command_template):
+ tmp = TempFile(suffix=".js", prefix="test262-", text=True)
+ try:
+ result = self.RunTestIn(command_template, tmp)
+ finally:
+ tmp.Dispose()
+ return result
+
+ def Print(self):
+ print self.GetSource()
+
+
+class ProgressIndicator(object):
+
+ def __init__(self, count):
+ self.count = count
+ self.succeeded = 0
+ self.failed = 0
+ self.failed_tests = []
+
+ def HasRun(self, result):
+ result.ReportOutcome(True)
+ if result.HasUnexpectedOutcome():
+ self.failed += 1
+ self.failed_tests.append(result)
+ else:
+ self.succeeded += 1
+
+
+def MakePlural(n):
+ if (n == 1):
+ return (n, "")
+ else:
+ return (n, "s")
+
+
+class TestSuite(object):
+
+ def __init__(self, root, stric_mode):
+# self.test_root = path.join(root, 'test', 'suite', 'Sputnik', 'Conformance')
+# self.test_root = path.join(root, 'test', 'suite', 'other')
+ self.test_root = path.join(root, 'test', 'suite', 'converted')
+ self.lib_root = path.join(root, 'test', 'harness')
+ self.strict_mode = stric_mode
+ self.include_cache = { }
+
+ def Validate(self):
+ if not path.exists(self.test_root):
+ ReportError("No test repository found")
+ if not path.exists(self.lib_root):
+ ReportError("No test library found")
+
+ def IsHidden(self, path):
+ return path.startswith('.') or path == 'CVS'
+
+ def IsTestCase(self, path):
+ return path.endswith('.js')
+
+ def ShouldRun(self, rel_path, tests):
+ if len(tests) == 0:
+ return True
+ for test in tests:
+ if test in rel_path:
+ return True
+ return False
+
+ def GetTimeZoneInfoInclude(self):
+ dst_attribs = GetDaylightSavingsAttribs()
+ if not dst_attribs:
+ return None
+ lines = []
+ for key in sorted(dst_attribs.keys()):
+ lines.append('var $DST_%s = %s;' % (key, str(dst_attribs[key])))
+ localtz = time.timezone / -3600
+ lines.append('var $LocalTZ = %i;' % localtz)
+ return "\n".join(lines)
+
+ def GetSpecialInclude(self, name):
+ if name == "environment.js":
+ return self.GetTimeZoneInfoInclude()
+ else:
+ return None
+
+ def GetInclude(self, name, strip_header=True):
+ key = (name, strip_header)
+ if not key in self.include_cache:
+ value = self.GetSpecialInclude(name)
+ if value:
+ self.include_cache[key] = value
+ else:
+ static = path.join(self.lib_root, name)
+ if path.exists(static):
+ f = open(static)
+ contents = f.read()
+ if strip_header:
+ contents = StripHeader(contents)
+ self.include_cache[key] = contents + "\n"
+ f.close()
+ else:
+ self.include_cache[key] = ""
+ return self.include_cache[key]
+
+ def EnumerateTests(self, tests):
+ logging.info("Listing tests in %s", self.test_root)
+ cases = []
+ for root, dirs, files in os.walk(self.test_root):
+ for f in [x for x in dirs if self.IsHidden(x)]:
+ dirs.remove(f)
+ dirs.sort()
+ for f in sorted(files):
+ if self.IsTestCase(f):
+ full_path = path.join(root, f)
+ if full_path.startswith(self.test_root):
+ rel_path = full_path[len(self.test_root)+1:]
+ else:
+ logging.warning("Unexpected path %s", full_path)
+ rel_path = full_path
+ if self.ShouldRun(rel_path, tests):
+ basename = path.basename(full_path)[:-3]
+ name = rel_path.split(path.sep)[:-1] + [basename]
+ cases.append(TestCase(self, name, full_path, False))
+ if self.strict_mode:
+ cases.append(TestCase(self, name, full_path, True))
+ logging.info("Done listing tests")
+ return cases
+
+ def PrintSummary(self, progress):
+ print
+ print "=== Summary ==="
+ count = progress.count
+ succeeded = progress.succeeded
+ failed = progress.failed
+ print " - Ran %i test%s" % MakePlural(count)
+ if progress.failed == 0:
+ print " - All tests succeeded"
+ else:
+ percent = ((100.0 * succeeded) / count,)
+ print " - Passed %i test%s (%.1f%%)" % (MakePlural(succeeded) + percent)
+ percent = ((100.0 * failed) / count,)
+ print " - Failed %i test%s (%.1f%%)" % (MakePlural(failed) + percent)
+ positive = [c for c in progress.failed_tests if not c.case.IsNegative()]
+ negative = [c for c in progress.failed_tests if c.case.IsNegative()]
+ if len(positive) > 0:
+ print
+ print "Failed tests"
+ for result in positive:
+ print " %s" % result.case.GetName()
+ if len(negative) > 0:
+ print
+ print "Expected to fail but passed ---"
+ for result in negative:
+ print " %s" % result.case.GetName()
+
+ def PrintFailureOutput(self, progress):
+ for result in progress.failed_tests:
+ print
+ result.ReportOutcome(False)
+
+ def Run(self, command_template, tests, print_summary, full_summary):
+ if not "{{path}}" in command_template:
+ command_template += " {{path}}"
+ cases = self.EnumerateTests(tests)
+ if len(cases) == 0:
+ ReportError("No tests to run")
+ progress = ProgressIndicator(len(cases))
+ for case in cases:
+ result = case.Run(command_template)
+ progress.HasRun(result)
+ if print_summary:
+ self.PrintSummary(progress)
+ if full_summary:
+ self.PrintFailureOutput(progress)
+ else:
+ print
+ print "Use --full-summary to see output from failed tests"
+ print
+
+ def Print(self, tests):
+ cases = self.EnumerateTests(tests)
+ if len(cases) > 0:
+ cases[0].Print()
+
+
+def GetDaylightSavingsTimes():
+ # Is the given floating-point time in DST?
+ def IsDst(t):
+ return time.localtime(t)[-1]
+ # Binary search to find an interval between the two times no greater than
+ # delta where DST switches, returning the midpoint.
+ def FindBetween(start, end, delta):
+ while end - start > delta:
+ middle = (end + start) / 2
+ if IsDst(middle) == IsDst(start):
+ start = middle
+ else:
+ end = middle
+ return (start + end) / 2
+ now = time.time()
+ one_month = (30 * 24 * 60 * 60)
+ # First find a date with different daylight savings. To avoid corner cases
+ # we try four months before and after today.
+ after = now + 4 * one_month
+ before = now - 4 * one_month
+ if IsDst(now) == IsDst(before) and IsDst(now) == IsDst(after):
+ logging.warning("Was unable to determine DST info.")
+ return None
+ # Determine when the change occurs between now and the date we just found
+ # in a different DST.
+ if IsDst(now) != IsDst(before):
+ first = FindBetween(before, now, 1)
+ else:
+ first = FindBetween(now, after, 1)
+ # Determine when the change occurs between three and nine months from the
+ # first.
+ second = FindBetween(first + 3 * one_month, first + 9 * one_month, 1)
+ # Find out which switch is into and which if out of DST
+ if IsDst(first - 1) and not IsDst(first + 1):
+ start = second
+ end = first
+ else:
+ start = first
+ end = second
+ return (start, end)
+
+
+def GetDaylightSavingsAttribs():
+ times = GetDaylightSavingsTimes()
+ if not times:
+ return None
+ (start, end) = times
+ def DstMonth(t):
+ return time.localtime(t)[1] - 1
+ def DstHour(t):
+ return time.localtime(t - 1)[3] + 1
+ def DstSunday(t):
+ if time.localtime(t)[2] > 15:
+ return "'last'"
+ else:
+ return "'first'"
+ def DstMinutes(t):
+ return (time.localtime(t - 1)[4] + 1) % 60
+ attribs = { }
+ attribs['start_month'] = DstMonth(start)
+ attribs['end_month'] = DstMonth(end)
+ attribs['start_sunday'] = DstSunday(start)
+ attribs['end_sunday'] = DstSunday(end)
+ attribs['start_hour'] = DstHour(start)
+ attribs['end_hour'] = DstHour(end)
+ attribs['start_minutes'] = DstMinutes(start)
+ attribs['end_minutes'] = DstMinutes(end)
+ return attribs
+
+
+def Main():
+ parser = BuildOptions()
+ (options, args) = parser.parse_args()
+ ValidateOptions(options)
+ test_suite = TestSuite(options.tests, options.enable_strict_mode)
+ test_suite.Validate()
+ if options.cat:
+ test_suite.Print(args)
+ else:
+ test_suite.Run(options.command, args,
+ options.summary or options.full_summary,
+ options.full_summary)
+
+
+if __name__ == '__main__':
+ try:
+ Main()
+ sys.exit(0)
+ except Test262Error, e:
+ print "Error: %s" % e.message
+ sys.exit(1)