Now generating the format David Fugate and I agreed on this

morning. The rewrite of "function testcase()..." now puts the
assertTrue at the bottom. Preconditions, names, and ids are
removed. And the path in the file is ignored, and is instead set
accurately according to where the file is found.
This commit is contained in:
Mark Miller 2011-09-07 21:42:49 -07:00
parent 57c450002d
commit ca417c93dc
4 changed files with 270 additions and 153 deletions

View File

@ -1,4 +1,4 @@
/// Copyright (c) 2009 Microsoft Corporation
/// Copyright (c) 2009 Microsoft Corporation
///
/// Redistribution and use in source and binary forms, with or without modification, are permitted provided
/// that the following conditions are met:
@ -52,7 +52,7 @@ function BrowserRunner() {
if(currentTest.error instanceof SputnikError) {
currentTest.error = currentTest.message;
} else {
currentTest.error = currentTest.error.name + ": " + currentTest.error.message
currentTest.error = currentTest.error.name + ": " + currentTest.error.message;
}
}
@ -112,8 +112,8 @@ function BrowserRunner() {
$.ajax({
async: false,
url: 'resources/scripts/global/' + include,
success: function(s) { scriptCache[include] = s }
})
success: function(s) { scriptCache[include] = s; }
});
}
// Finally, write the required script to the window.
@ -182,7 +182,7 @@ function BrowserRunner() {
}
doc.writeln("<script type='text/javascript'>testFinished();</script>");
doc.close();
}
};
}
/* Loads tests from the sections specified in testcaseslist.json.
@ -244,7 +244,7 @@ function TestLoader() {
testGroups[i] = {
path: testSuite[i],
tests: []
}
};
}
loader.onInitialized(loader.totalTests, loader.version, loader.date);
getNextXML();
@ -271,13 +271,13 @@ function TestLoader() {
// We're done.
loader.onTestsExhausted();
}
}
};
/* Start over at the beginning */
this.reset = function() {
currentTestIndex = 0;
testGroupIndex = 0;
}
};
}
/* Controls test generation and running, and sends results to the presenter. */
@ -294,22 +294,22 @@ function Controller() {
if(state === 'running')
setTimeout(loader.getNextTest, 10);
}
};
loader.onInitialized = function(totalTests, version, date) {
presenter.setVersion(version);
presenter.setDate(date);
presenter.setTotalTests(totalTests);
}
};
loader.onLoadingNextSection = function(path) {
presenter.updateStatus("Loading: " + path);
}
};
loader.onTestReady = function(id, test) {
presenter.updateStatus("Executing Test: " + id);
runner.run(id, test);
}
};
loader.onTestsExhausted = function() {
state = 'stopped';
@ -317,27 +317,27 @@ function Controller() {
elapsed = elapsed/(1000*60); //minutes
elapsed = elapsed.toFixed(1);
presenter.finished(elapsed);
}
};
this.start = function() {
state = 'running';
startTime = new Date();
loader.getNextTest();
presenter.started();
}
};
this.pause = function() {
elapsed += new Date() - startTime;
state = 'paused';
presenter.paused();
}
};
this.reset = function() {
startTime = new Date();
elapsed = 0;
loader.reset();
presenter.reset();
}
};
this.toggle = function() {
if(state === 'running') {
@ -345,16 +345,16 @@ function Controller() {
} else {
controller.start();
}
}
};
}
var controller = new Controller()
var controller = new Controller();
/* Helper function which shows if we're in the 'debug' mode of the Test262 site.
This mode is only useful for debugging issues with the test harness and
website. */
function isSiteDebugMode() {
var str=window.location.href.substring(window.location.href.indexOf("?")+1)
var str=window.location.href.substring(window.location.href.indexOf("?")+1);
if(str.indexOf("sitedebug") > -1) {
return true;
}

View File

@ -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();

View File

@ -8,6 +8,10 @@
var t262 = global.t262;
var platform = t262.platform;
var regExp = platform.regExp;
var toRelPathStr = platform.toRelPathStr;
var toPathStr = platform.toPathStr;
var toRelPath = platform.toRelPath;
var toPath = platform.toPath;
var headerPattern = /(?:(?:\/\/.*)?\s*\n)*/;
var captureCommentPattern = /\/\*\*?((?:\s|\S)*?)\*\/\s*\n/;
@ -27,8 +31,9 @@
/\}\s*\)/, ')',
/\s*;?(?:\s|\n)*$/);
var captureFuncBodyPattern =
regExp(/^function(?:\s+\w*)?\(\s*\)\s*\{/,
// Matches a named function. Captures both the name and the body.
var captureFuncNameBodyPattern =
regExp(/^function\s+(\w*)\(\s*\)\s*\{/,
'(', anyPattern, ')',
/;?/, blanksPattern,
/\}$/);
@ -80,8 +85,10 @@
propTexts.forEach(function(propText) {
var propName = propText.match(/^\w+/)[0];
var propVal = propText.substring(propName.length);
var propMatch = /^:?([^;]*);?\s*$/.exec(propVal);
if (propMatch) { propVal = propMatch[1]; }
// 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);
@ -106,27 +113,49 @@
}
/**
* Given a function, return the source for an expression that, when
* evaluated in the environment the function assumes, will behave
* the same as calling that function in that environment.
* 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 convertion assumes a one argument
* <code>assertTrue</code> function which throws an indication of
* test failure iff given 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 expressionize(func) {
function functionToProgramSrc(func) {
var funcSrc = '' + func;
var cfbMatch = captureFuncBodyPattern.exec(funcSrc);
if (cfbMatch) {
// Look for special cases
var body = cfbMatch[1].trim();
var cebMatch = captureExprBodyPattern.exec(body);
if (cebMatch) { return '(' + cebMatch[1].trim() + ')'; }
var cpMatch = capturePredicatePattern.exec(body);
if (cpMatch) { return '(' + cpMatch[1].trim() + ')'; }
} else {
// signal an error?
var cfnbMatch = captureFuncNameBodyPattern.exec(funcSrc);
if (!cfnbMatch) {
throw new Error('Could not recognize: "' + funcSrc + '"');
}
return '(' + funcSrc + ').call(this)';
var name = cfnbMatch[1].trim();
var body = cfnbMatch[2].trim();
// Look for special cases
var cebMatch = captureExprBodyPattern.exec(body);
if (cebMatch) {
return 'assertTrue(' + cebMatch[1].trim() + ');';
}
var cpMatch = capturePredicatePattern.exec(body);
if (cpMatch) {
return 'assertTrue(' + cpMatch[1].trim() + ');';
}
// General case
return funcSrc + '\n' +
'assertTrue(' + name + '.call(this));';
}
/**
@ -158,27 +187,25 @@
'forceNonStrict');
if (testRecords.length !== 1) {
// We plan to lift this restriction in order to support test
// 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 +
'assertTrue(' + expressionize(testRecord.test) + ');\n';
testRecord.test = envelope.rest + '\n' +
functionToProgramSrc(testRecord.test);
}
if (typeof testRecord.precondition === 'function') {
var precondition = expressionize(testRecord.precondition);
if (precondition === '(true)') {
delete testRecord.precondition;
} else {
testRecord.precondition = precondition;
}
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
@ -186,11 +213,6 @@
* runners.
*/
function normalizeProps(testRecord) {
if (!testRecord.id && testRecord.name) {
testRecord.id = testRecord.name;
delete testRecord.name;
}
if (!('strict_only' in testRecord) && testRecord.strict === 1) {
testRecord.strict_only = '';
delete testRecord.strict;
@ -206,6 +228,8 @@
}
}
// 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;
@ -219,19 +243,22 @@
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(path, name) {
var nextPath = path.concat([name]);
function parseTestRecord(inBase, relPath, name) {
var nextRelPath = relPath.concat([name]);
var nextPath = inBase.concat(nextRelPath);
var src = platform.read(nextPath);
var testRecord;
if (!src) { throw new Error('no src: ' + nextPath.join('/')); }
if (!src) {
throw new Error('no src: ' + toPathStr(nextPath));
}
var envelope = parseTestEnvelope(src, name);
if (envelope.registerExpr) {
@ -242,16 +269,19 @@
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 = ['id', 'section', 'path', 'description',
var KNOWN_PROPS = ['section', 'path', 'description',
'strict_only', 'negative'];
/**
@ -283,30 +313,31 @@
result += ' * ' + testRecord.comment.replace(/\n/g, '\n * ') + '\n *\n';
}
delete testRecord.comment;
KNOWN_PROPS.concat(['precondition']).forEach(addProp);
KNOWN_PROPS.forEach(addProp);
Object.keys(testRecord).forEach(addProp);
result += ' */\n\n' + test;
return result;
};
}
t262.formatTestRecord = formatTestRecord;
/**
* Reads the test case at pathStr and returns the source of that
* test case converted to canonical test262 style.
*/
function convertTest(pathStr) {
var path = platform.toPath(pathStr);
var name = path.pop();
var testRecord = parseTestRecord(path, name);
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 SRC_DIRS = [
['test', 'suite', 'other'],
['test', 'suite', 'sputnik', 'Conformance'],
['test', 'suite', 'ietestcenter']
['test', 'suite', 'ietestcenter'],
['test', 'suite', 'sputnik', 'Conformance']
];
var CONV_DIR = ['test', 'suite', 'converted'];
@ -315,6 +346,8 @@
var ME_PATH = platform.CONVERTER_PATH.concat('convert.js');
var writeSpawnFailures = [];
/**
* Convert all the testcases found at inBase+relDir to test cases
* in canonical test262 style, to be stored at corresponding
@ -329,27 +362,43 @@
if (platform.isDirectory(inBase.concat(nextRelPath))) {
convertAll(inBase, outBase, nextRelPath);
} else if (/\.js$/.test(name)) {
var inFilePath = inPath.concat([name]);
var outFilePath = outPath.concat([name]);
platform.writeSpawn(
[ME_PATH],
't262.convertTest("' + platform.toPathStr(inFilePath) + '")',
void 0,
outFilePath);
try {
platform.writeSpawn(
[ME_PATH],
'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_relPath) {
function convert(opt_relPathStr) {
var relPath = opt_relPathStr ? toRelPath(opt_relPathStr) : [];
writeSpawnFailures = [];
SRC_DIRS.forEach(function(srcDir) {
convertAll(srcDir, CONV_DIR, opt_relPath || []);
convertAll(srcDir, CONV_DIR, relPath);
});
};
if (writeSpawnFailures.length >= 1) {
print('********* failures **********');
writeSpawnFailures.forEach(function(failure) {
print(failure.error + ': ' + toRelPathStr(failure.relPath));
});
throw writeSpawnFailures[0].error;
}
}
t262.convert = convert;
/**
@ -359,7 +408,7 @@
* runner.
*/
function buildSection(pathStr) {
var path = platform.toPath(pathStr);
var path = toPath(pathStr);
if (!platform.isDirectory(path)) { throw new Error('not dir: ' + path); }
var jsFiles = platform.ls(path).filter(function(name) {
@ -382,7 +431,7 @@
tests: testRecords
}
};
};
}
t262.buildSection = buildSection;
/**
@ -403,14 +452,21 @@
if (hasJS) {
var name = relPath[relPath.length -1] + '.json';
var outFilePath = outBase.concat([name]);
platform.writeSpawn(
[ME_PATH],
't262.asJSONTxt(t262.buildSection("' +
platform.toPathStr(inPath) + '"))',
void 0,
outFilePath);
try {
platform.writeSpawn(
[ME_PATH],
't262.asJSONTxt(t262.buildSection("' +
toPathStr(inPath) + '"))',
void 0,
outFilePath);
} catch (err) {
writeSpawnFailures.push({
error: err,
path: relPath
});
}
}
};
}
t262.buildAll = buildAll;
/**
@ -421,12 +477,13 @@
* files. Once we switch over to converted as the maintained
* sources, we should change this.
*/
function buildWebSite(opt_relPath) {
function buildWebSite(opt_relPathStr) {
var relPath = opt_relPathStr ? toRelPath(opt_relPathStr) : [];
SRC_DIRS.forEach(function(srcDir) {
buildAll(srcDir, OUT_DIR, opt_relPath || []);
buildAll(srcDir, OUT_DIR, relPath);
});
// buildAll(CONV_DIR, OUT_DIR, opt_relPath || []);
};
// buildAll(CONV_DIR, OUT_DIR, relPath);
}
t262.buildWebSite = buildWebSite;
})(this);

View File

@ -34,11 +34,16 @@
/**
* 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 string.
* backslash problem when composing literal strings.
*
* <p>The arguments can be any mixture of RegExps and strings. The
* strings are added as is without escaping -- BEWARE. If
* arguments[0] is a RegExp, we use its flag on the resuting RegExp.
* <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.
@ -95,23 +100,39 @@
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 string on the current platform, according to the operations
* 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.
*
* Note: Don't simply revise this (without renamings) to follow the
* general pattern of also defining a local 'read' function, as it
* will mask the v8 shell's read function, which we use.
*/
platform.read = function(path) {
var text = read(toPathStr(path)).
@ -153,12 +174,12 @@
* @returns If there is a target, then the null string. Otherwise,
* the string result of evaluating opt_exprSrc.
*/
platform.writeSpawn = function(scriptPaths,
opt_exprSrc,
opt_args,
opt_targetPath,
opt_spawn_required,
opt_forceNonStrict) {
function writeSpawn(scriptPaths,
opt_exprSrc,
opt_args,
opt_targetPath,
opt_spawn_required,
opt_forceNonStrict) {
if (opt_exprSrc && !opt_targetPath && !opt_spawn_required) {
var str = '(function(/*var_args*/) {';
if (opt_forceNonStrict !== 'forceNonStrict') {
@ -182,8 +203,21 @@
}
if (VERBOSE || DRY_RUN) { print(cmd); }
if (DRY_RUN) { return ''; }
return os.system('bash', ['-c', cmd]);
};
try {
return os.system('bash', ['-c', cmd]);
} catch (err) {
if (opt_targetPath) {
// The error we catch is almost certainly less interesting
// than the one unfortunately written to the target file.
var message = 'failed: ' + cmd + '\n' +
platform.read(opt_targetPath);
os.system('rm', [toPathStr(opt_targetPath)]);
throw new Error(message);
}
throw err;
}
}
platform.writeSpawn = writeSpawn;
////////////////// Only needed for building /////////////////////
@ -193,7 +227,7 @@
* On platforms (like SES) where this can be a safely confining
* evaluation, it should be. The implementation here is not safe.
*/
platform.evalExprIn = function(exprSrc, env, opt_forceNonStrict) {
function evalExprIn(exprSrc, env, opt_forceNonStrict) {
var varNames = Object.getOwnPropertyNames(env);
var str = '(function(' + varNames.join(',') + ') {';
if (opt_forceNonStrict !== 'forceNonStrict') {
@ -203,12 +237,23 @@
return (1,eval)(str).apply(void 0, varNames.map(function(varName) {
return env[varName];
}));
};
}
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.
*
* See toPathStr.
* <p>See toPathStr.
*/
function toPath(pathStr) {
if (pathStr[0] === '/') {
@ -225,24 +270,38 @@
/**
* Does path name a directory?
*/
platform.isDirectory = function(path) {
var fileOut = os.system('file', [toPathStr(path)]);
var fileMatch = fileOut.match(/:\s*([^:]*)\s*$/);
if (!fileMatch) { return null; }
var fileType = fileMatch[1].trim();
return fileType === 'directory';
};
function isDirectory(path) {
// var fileOut = os.system('file', [toPathStr(path)]);
// var fileMatch = fileOut.match(/:\s*([^:]*)\s*$/);
// if (!fileMatch) { return null; }
// var fileType = fileMatch[1].trim();
// return fileType === 'directory';
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.
*/
platform.ls = function(path) {
function ls(path) {
var pathStr = toPathStr(path);
var lines = os.system('ls', [pathStr]).trim();
if (!isDirectory(path)) { return []; }
var lines;
try {
lines = os.system('ls', [pathStr]).trim();
} catch (err) {
throw err;
}
if (lines === '') { return []; }
return lines.split('\n');
};
}
platform.ls = ls;
/**
* Emits the jsonRecord serialized as JSON, either compactly or
@ -257,7 +316,7 @@
}
global.t262.asJSONTxt = platform.asJSONTxt = asJSONTxt;
platform.mkdir = function(path) {
function mkdir(path) {
var pathStr = toPathStr(path);
if (DRY_RUN) {
print('mkdir ' + pathStr);
@ -269,7 +328,8 @@
print('***could not mkdir: ' + pathStr);
throw err;
}
};
}
platform.mkdir = mkdir;
////////////////// Only needed for running //////////////////////