Add text miner tool for test262 logs
This commit is contained in:
parent
b57f6c3031
commit
7450604677
375
tools/ecmaTestProcessor.js
Normal file
375
tools/ecmaTestProcessor.js
Normal file
|
|
@ -0,0 +1,375 @@
|
||||||
|
var fs = require("fs");
|
||||||
|
|
||||||
|
var async = require("async");
|
||||||
|
var yargs = require("yargs");
|
||||||
|
|
||||||
|
var OK = 0;
|
||||||
|
var FAIL = 1;
|
||||||
|
|
||||||
|
var MESSAGE = /^(=== )?[^\s]* (passed|failed|was expected to fail) in (non-)?strict mode( as expected|, but didn't)?( ===)?$/;
|
||||||
|
var SELF_ERROR = /A minified script failed to run after being minified\. Please report node version, test and error to the maintainer of the minifier tool\./;
|
||||||
|
var BOTH_ERROR = /A minified script failed to run both minified and unminified\. This is likely an invalid js file/;
|
||||||
|
var SELF_ERROR_FILTER = function(input) {
|
||||||
|
return SELF_ERROR.test(input)
|
||||||
|
};
|
||||||
|
var BOTH_ERROR_FILTER = function(input) {
|
||||||
|
return BOTH_ERROR.test(input)
|
||||||
|
};
|
||||||
|
|
||||||
|
var roundToPercentage = function(input) {
|
||||||
|
return Math.round(input * 10000) / 100;
|
||||||
|
};
|
||||||
|
|
||||||
|
var isNodeOnlyError = function(summary, output) {
|
||||||
|
// Errors caused when failing both running minified and unminified are very likely node caused
|
||||||
|
if (output.filter(BOTH_ERROR_FILTER).length > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var isUglifyOnlyError = function(summary, output) {
|
||||||
|
// Errors caused when being minified, unminified ran fine
|
||||||
|
if (output.filter(SELF_ERROR_FILTER).length > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var ARGS = yargs.usage(
|
||||||
|
"$0 logs.txt -o output.md\n\n" +
|
||||||
|
"Processes test262 logs and convert it to more human readable markup files.\n"
|
||||||
|
)
|
||||||
|
.describe("h", "Get help")
|
||||||
|
.describe("o", "Output file")
|
||||||
|
|
||||||
|
.describe("debug", "Prints out very large json debug file")
|
||||||
|
.describe("debugFormat", "Prints out structure of json debug file")
|
||||||
|
.describe("es5", "Notifies that tests are run on test262 es5 mode")
|
||||||
|
|
||||||
|
.alias("h", "help")
|
||||||
|
.alias("o", "output")
|
||||||
|
|
||||||
|
.boolean("es5")
|
||||||
|
|
||||||
|
.wrap(80)
|
||||||
|
|
||||||
|
.argv;
|
||||||
|
|
||||||
|
if (ARGS.h || ARGS.help) {
|
||||||
|
console.log(yargs.help());
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS.debugFormat) {
|
||||||
|
console.log("Root:")
|
||||||
|
console.log(" logs: <Object LogObject> See LogObject");
|
||||||
|
console.log(" unitTestUrl: <String> Base url to test suite, used as link template for tests");
|
||||||
|
console.log("");
|
||||||
|
console.log("LogObject:");
|
||||||
|
console.log(" errors: <Int> Total errors in this LogObjects and childs");
|
||||||
|
console.log(" errors_local: <Int> Total errors in this LogObject alone");
|
||||||
|
console.log(" node_error: <Int> Total errors caused by Node in this LogObject and childs");
|
||||||
|
console.log(" node_error_local: <Int> Total errors caused by Node in this LogObject alone");
|
||||||
|
console.log(" self_error: <Int> Total errors caused by Uglify in this LogObject and childs");
|
||||||
|
console.log(" self_error_local: <Int> Total errors caused by Uglify in this LogObject alone");
|
||||||
|
console.log(" testsCount: <Int> Total number of tests");
|
||||||
|
console.log(" testsCount_local: <Int> Total number of local tests");
|
||||||
|
console.log(" dir: <Object LogObject> Contains LogObject childs");
|
||||||
|
console.log(" tests: <Array TestArrayList> See TestObject");
|
||||||
|
console.log("");
|
||||||
|
console.log("TestArrayList:");
|
||||||
|
console.log(" {for every array element}: <Array TestArray>");
|
||||||
|
console.log("");
|
||||||
|
console.log("TestArray:");
|
||||||
|
console.log(" 0: <Int> OK or FAIL to indicate that the test has succeed");
|
||||||
|
console.log(" 1: <String> Summary message");
|
||||||
|
console.log(" 2: <Object Error | undefined> Error Object containing details if any necessary");
|
||||||
|
console.log(" 3: <Int> Line of test in log");
|
||||||
|
console.log("");
|
||||||
|
console.log("Error:");
|
||||||
|
console.log(" logs: <Array String> stdout of the test failure");
|
||||||
|
console.log(" node_error: <Bool> True if likelyhood that node screwed up is very high, Indicated use only.");
|
||||||
|
console.log(" self_error: <Bool> True if likelyhood that Uglify screwed up is very high. Indicated use only.");
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARGS._.length === 0) {
|
||||||
|
console.log("Please provide logs\nYou can add --help to the command to get help");
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data;
|
||||||
|
|
||||||
|
// Read files
|
||||||
|
async.mapLimit(ARGS._, 1, function(file, callback) {
|
||||||
|
var content = fs.readFileSync(file, {
|
||||||
|
encoding: 'utf8'
|
||||||
|
}).split(/\s*(?:\r(?:\n)?|\n)/);
|
||||||
|
|
||||||
|
var output = [];
|
||||||
|
var errors = 0;
|
||||||
|
|
||||||
|
var tmp;
|
||||||
|
for (var i = 0; i < content.length; i++) {
|
||||||
|
// Make sure the message is done in the expected format
|
||||||
|
if (!MESSAGE.test(content[i])) {
|
||||||
|
if (/^\s*$/.test(content[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Unexpected message at line " + i + ":");
|
||||||
|
console.log(content[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push test formatted to the output
|
||||||
|
if (content[i].indexOf("=") === -1) {
|
||||||
|
output.push([OK, content[i], undefined, i]);
|
||||||
|
} else if (/^===/.test(content[i])) {
|
||||||
|
tmp = i;
|
||||||
|
// Find first line without errors
|
||||||
|
do {
|
||||||
|
errors++;
|
||||||
|
i++;
|
||||||
|
while (!/^.*===$/.test(content[i])) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} while (!MESSAGE.test(content[i + 1]));
|
||||||
|
|
||||||
|
var error = {
|
||||||
|
logs: content.slice(tmp + 1, i),
|
||||||
|
};
|
||||||
|
var summary = content[tmp].substr(4, content[tmp].length - 8)
|
||||||
|
error.node_error = isNodeOnlyError(summary, error.logs);
|
||||||
|
error.self_error = isUglifyOnlyError(summary, error.logs);
|
||||||
|
|
||||||
|
output.push([
|
||||||
|
FAIL,
|
||||||
|
summary,
|
||||||
|
error,
|
||||||
|
i
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
console.log("Unknown message at line " + i + ":");
|
||||||
|
console.log(content[i]);
|
||||||
|
console.log("Error message started at line " + tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats in console
|
||||||
|
console.log(content.length + " lines of logs");
|
||||||
|
console.log(output.length + " tests found");
|
||||||
|
console.log(errors + " errors found");
|
||||||
|
|
||||||
|
if (errors > 0) {
|
||||||
|
console.log("Failure rate: " + roundToPercentage(errors / output.length) + "%");
|
||||||
|
} else {
|
||||||
|
console.log("Failure rate: 0%");
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(undefined, output);
|
||||||
|
}, function(err, results) {
|
||||||
|
// Check if everything succeed without errors
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
process.exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
var getNewLogsObject = function() {
|
||||||
|
return {
|
||||||
|
errors: 0,
|
||||||
|
errors_local: 0,
|
||||||
|
node_error: 0,
|
||||||
|
node_error_local: 0,
|
||||||
|
self_error: 0,
|
||||||
|
self_error_local: 0,
|
||||||
|
testsCount: 0,
|
||||||
|
testsCount_local: 0,
|
||||||
|
dir: {},
|
||||||
|
tests: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge log results
|
||||||
|
data = {
|
||||||
|
logs: getNewLogsObject()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ARGS.es5) {
|
||||||
|
data.unitTestUrl = "https://github.com/tc39/test262/blob/es5-tests/test/suite/";
|
||||||
|
} else {
|
||||||
|
data.unitTestUrl = "https://github.com/tc39/test262/blob/master/test/";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < results.length; i++) {
|
||||||
|
for (var j = 0; j < results[i].length; j++) {
|
||||||
|
var status = results[i][j][0];
|
||||||
|
var test = results[i][j][1];
|
||||||
|
var error = results[i][j][2];
|
||||||
|
var line = results[i][j][3];
|
||||||
|
var node_error = status === FAIL && error.node_error ? 1 : 0;
|
||||||
|
var self_error = status === FAIL && error.self_error ? 1 : 0;
|
||||||
|
|
||||||
|
if (typeof test !== "string") {
|
||||||
|
console.log("Error!");
|
||||||
|
console.log(test);
|
||||||
|
}
|
||||||
|
var path = test.split(" ")[0].split(/\/|\\/);
|
||||||
|
|
||||||
|
var pos = data.logs;
|
||||||
|
for (var k = 0; k < path.length - 1; k++) {
|
||||||
|
var dir = path[k] + "/";
|
||||||
|
|
||||||
|
pos.errors += status;
|
||||||
|
pos.node_error += node_error;
|
||||||
|
pos.self_error += self_error;
|
||||||
|
pos.testsCount++;
|
||||||
|
|
||||||
|
if (!(dir in pos.dir)) {
|
||||||
|
pos.dir[dir] = getNewLogsObject();
|
||||||
|
}
|
||||||
|
pos = pos.dir[dir];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pos.errors += status;
|
||||||
|
pos.errors_local += status;
|
||||||
|
pos.node_error += node_error;
|
||||||
|
pos.node_error_local += node_error;
|
||||||
|
pos.self_error += self_error;
|
||||||
|
pos.self_error_local += self_error;
|
||||||
|
pos.testsCount++;
|
||||||
|
pos.testsCount_local++;
|
||||||
|
|
||||||
|
if (path[path.length - 1] + ".js" in pos.tests) {
|
||||||
|
pos.tests[path[path.length - 1] + ".js"].push(results[i][j]);
|
||||||
|
} else {
|
||||||
|
pos.tests[path[path.length - 1] + ".js"] = [results[i][j]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Produce debug output
|
||||||
|
if (ARGS.debug) {
|
||||||
|
fs.writeFile(ARGS.debug, JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intro
|
||||||
|
var mdFile = "# Test262 results\n\n";
|
||||||
|
mdFile += "All tests are passed as js code to UglifyJS, and run through the minify method. " +
|
||||||
|
"The minified code will then be run by node. If the minified code errors, " +
|
||||||
|
"the original code will be run through node and the results will be compared. " +
|
||||||
|
"Ultimately, the test runner decides if a test fail. Some tests may be expected to fail.\n\n";
|
||||||
|
mdFile += "Tests only working without UglifyJS are marked in bold.\n\n";
|
||||||
|
mdFile += "Most tests are tested in strict and non-strict mode. " +
|
||||||
|
"While failures may be listed twice, the test will only be shown once.\n\n";
|
||||||
|
mdFile += "Note: there is a bug that prevents links from being visible, " +
|
||||||
|
"see https://github.com/vmg/redcarpet/issues/443\n\n";
|
||||||
|
|
||||||
|
// Show tests, failures, percentage and used tools
|
||||||
|
mdFile += "## Global stats\n\n";
|
||||||
|
mdFile += "- Found " + data.logs.testsCount + " executed tests\n";
|
||||||
|
mdFile += "- " + data.logs.errors + " tests failed (" + roundToPercentage(data.logs.errors / data.logs.testsCount) + "%)\n";
|
||||||
|
mdFile += "- " + data.logs.node_error + " tests failed becuase of Node (" + roundToPercentage(data.logs.node_error / data.logs.testsCount) + "%)\n"
|
||||||
|
mdFile += "- " + data.logs.self_error + " tests failed because of UglifyJS (" + roundToPercentage(data.logs.self_error / data.logs.testsCount) + "%)\n";
|
||||||
|
mdFile += "\n";
|
||||||
|
|
||||||
|
function getSummary(obj) {
|
||||||
|
var subDirectoryCount = Object.keys(obj.dir).length;
|
||||||
|
|
||||||
|
// Templates:
|
||||||
|
// 1 failures whereof 1 caused by UglifyJS (1 failure over 10 local tests with 1 caused by UglifyJS, with 10 subdirectories)
|
||||||
|
// 0 failures (no local tests, 10 subdirectories)
|
||||||
|
return obj.errors + " failures over " + obj.testsCount + " tests" + (
|
||||||
|
obj.self_error > 0 ? " whereof " + obj.self_error + " caused by UglifyJS" : ""
|
||||||
|
) + " (" + (
|
||||||
|
obj.testsCount_local > 0 ? (
|
||||||
|
obj.errors_local > 0 ? obj.errors_local + " failures over " + obj.testsCount_local + " local tests" + (
|
||||||
|
obj.self_error_local > 0 ? " with " + obj.self_error_local + " caused by UglifyJS" : ""
|
||||||
|
) : "No local failures"
|
||||||
|
) : "No local tests"
|
||||||
|
) + ", with " + (subDirectoryCount > 0 ? subDirectoryCount : "no") + " subdirectories)"
|
||||||
|
}
|
||||||
|
|
||||||
|
function printResult(obj, level) {
|
||||||
|
var prefix = "";
|
||||||
|
|
||||||
|
for (var c = 0; c < level; c++) {
|
||||||
|
prefix += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print current directory
|
||||||
|
var dirs = "";
|
||||||
|
for (var i in obj.dir) {
|
||||||
|
dirs += prefix + "- [" + (obj.dir[i].errors === 0 ? "x" : " ") +"] `" + i + "` "
|
||||||
|
+ getSummary(obj.dir[i]) + "\n";
|
||||||
|
|
||||||
|
if (obj.dir[i].errors > 0) {
|
||||||
|
dirs += printResult(obj.dir[i], level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fails = "";
|
||||||
|
for (var j in obj.tests) {
|
||||||
|
var node_error_count = 0;
|
||||||
|
var self_error_count = 0;
|
||||||
|
var fail_count = 0;
|
||||||
|
var total_count = 0;
|
||||||
|
|
||||||
|
for (var k = 0; k < obj.tests[j].length; k++) {
|
||||||
|
total_count++;
|
||||||
|
|
||||||
|
if (obj.tests[j][k][0] !== FAIL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fail_count++;
|
||||||
|
if (obj.tests[j][k][2].node_error) {
|
||||||
|
node_error_count++;
|
||||||
|
}
|
||||||
|
if (obj.tests[j][k][2].self_error) {
|
||||||
|
self_error_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fail_count > 0) {
|
||||||
|
var counter = " - failed: " + fail_count + "/" + total_count;
|
||||||
|
var error_style = self_error_count > 0 ? "**" : (node_error_count > 0 ? "~~" : "");
|
||||||
|
|
||||||
|
// Add formatted link
|
||||||
|
var test = obj.tests[j][0][1]
|
||||||
|
.replace(/\\/g, "\\\\")
|
||||||
|
.replace(/-/g, "\\-");
|
||||||
|
var links = " [\\[test\\]](" + data.unitTestUrl +
|
||||||
|
obj.tests[j][0][1]
|
||||||
|
.split(" ")[0]
|
||||||
|
.replace(/\\/g, "/") + ".js)";
|
||||||
|
|
||||||
|
var cause = "";
|
||||||
|
if (self_error_count > 0 && node_error_count === 0) {
|
||||||
|
cause = " (Caused by UglifyJS)";
|
||||||
|
} else if (self_error_count === 0 && node_error_count > 0) {
|
||||||
|
cause = " (Caused by Node)";
|
||||||
|
} else if (self_error_count > 0 && node_error_count > 0) {
|
||||||
|
cause = " (Some caused by Node and some by UglifyJS)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add error summary
|
||||||
|
fails += prefix + "- *" + error_style + test + error_style + "*" +
|
||||||
|
links + cause + counter + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fails + dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
mdFile += "## Error tree\n\n";
|
||||||
|
mdFile += getSummary(data.logs) + "\n\n";
|
||||||
|
mdFile += printResult(data.logs, 0);
|
||||||
|
|
||||||
|
if (ARGS.o) {
|
||||||
|
fs.writeFile(ARGS.o,mdFile);
|
||||||
|
} else {
|
||||||
|
console.log(mdFile);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user