diff options
author | David Fugate <dfugate@microsoft.com> | 2011-09-12 11:25:23 -0700 |
---|---|---|
committer | David Fugate <dfugate@microsoft.com> | 2011-09-12 11:25:23 -0700 |
commit | 807a3ba1b741b6e4b763855b00bfeaec2a88c450 (patch) | |
tree | aa479fc421eba4d3228150653572cab1f153f56b /tools | |
parent | 2c16b93983cce6aa72458f82d801a4702d61506a (diff) | |
parent | 68d44bb52a11ba7a9a2f08ad2ea45e23f0224a7a (diff) |
Merge.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/SputnikConverter/ES5TestCase.cs | 2 | ||||
-rw-r--r-- | tools/converter/browserPlatform.js | 171 | ||||
-rw-r--r-- | tools/converter/convert.js | 512 | ||||
-rw-r--r-- | tools/converter/utils.js | 118 | ||||
-rw-r--r-- | tools/converter/v8PosixPlatform.js | 368 | ||||
-rw-r--r-- | tools/test262.py | 531 |
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) |