test262-parser
Version:
Parse a test262-format test and provide API
228 lines (195 loc) • 4.92 kB
JavaScript
// Copyright (C) 2014, Microsoft Corporation. All rights reserved.
// This code is governed by the BSD License found in the LICENSE file.
/*jslint regexp:true*/
'use strict';
var through = require('through'),
yaml = require('js-yaml'),
doneRegex = /\$DONE/,
yamlStart = "/*---",
yamlEnd = "---*/";
/**
* @module Test262Parser
*/
/**
* @class Test262File
*/
/**
* filename
* @property {string} file
*/
/**
* test code
* @property {string} contents
*/
/**
* parsed, normalized attributes
* @property {Test262FileAttrs} attrs
*/
/**
* copyright message
* @property {string} copyright
*/
/**
* @class Test262FileAttrs
*/
/**
* list of harness files to include
* @attribute {Array} includes
*/
/**
* test flags; valid values include:
* - onlyStrict
* - noStrict
* @attribute {Object} flags
*/
/**
* author name
* @attribute {String} author
* @optional
*/
/**
* @class Test262Parser
*/
/**
* Extract copyright message
*
* @method extractCopyright
* @param {Test262File} file - file object
* @return {string} the copyright string extracted from contents
* @private
*/
function extractCopyright(file) {
var result = /^(?:(?:\/\/.*\n)*)/.exec(file.contents);
return result ? result[0] : "";
}
/**
* Normalize a potential string into a file object
* @method normalizeFile
* @param {Test262File|string} file - file object or a string to convert into one
* @return {Test262File} the file object if passed, or a Test262File with filename '<unknown>' if passed a string
* @private
*/
function normalizeFile(file) {
if (typeof file === 'string') {
return { file: '<unknown>', contents: file };
}
return file;
}
/**
* Extract YAML frontmatter from a test262 test
* @method extractYAML
* @param {string} text - text of test file
* @return {string} the YAML frontmatter or empty string if none
*/
function extractYAML(text) {
var start = text.indexOf(yamlStart),
end;
if (start > -1) {
end = text.indexOf(yamlEnd);
return text.substring(start + 5, end);
}
return "";
}
/**
* Extract test body
*
* @method extract
* @param {Test262File} file - file object
* @return {string} the test body (after all frontmatter)
* @private
*/
function extractBody(file) {
var text = file.contents,
end = text.indexOf(yamlEnd),
start = text.indexOf(yamlStart);
if (end > -1) {
return text.slice(0, start) + text.slice(end + 5);
}
return text;
}
/**
* Get async status
*
* @method isAsync
* @param {Test262File} file - file object
* @return {boolean} whether test is async
* @private
*/
function isAsync(file) {
return doneRegex.test(file.contents);
}
/**
* Extract and parse frontmatter from a test
* @method loadAttrs
* @param {Test262File} file - file object
* @return {Object} - raw, unnormalized attributes
* @private
*/
function loadAttrs(file) {
var y = extractYAML(file.contents);
if (y) {
try {
return yaml.load(y);
} catch (e) {
throw new Error("Error loading frontmatter from file " +
file.file + "\n" + e.message);
}
}
return {};
}
/**
* Normalize attributes; ensure that flags, includes exist
*
* @method normalizeAttrs
* @param {Object} attrs raw, unnormalized attributes
* @return {Test262FileAttrs} normalized attributes
* @private
*/
function normalizeAttrs(attrs) {
attrs.flags = attrs.flags || [];
attrs.flags = attrs.flags.reduce(function (acc, v) {
acc[v] = true;
return acc;
}, {});
attrs.includes = attrs.includes || [];
return attrs;
}
/**
* Parse a test file:
* - identify and parse frontmatter
* - set up normalized attributes
*
* @method parseFile
* @param {Test262File|string} file - file object (only name, contents expected) or contents string
* @return {Test262File} file object with attrs, async added
* @throws Error if error parsing YAML
*/
function parseFile(file) {
file = normalizeFile(file);
file.attrs = normalizeAttrs(loadAttrs(file));
file.async = isAsync(file);
file.copyright = extractCopyright(file);
file.contents = extractBody(file);
file.isATest = (file.copyright !== "");
return file;
}
/**
* Adapter function to provide a stream interface
* - file object is read from input stream
* - use `parseFile` to parse front matter
* - successfully parsed files are queued to output stream
* - errors are emitted as 'error' events
*
* @method throughParseFile
* @param {Test262File} file - file object (only name, contents expected)
*/
function throughParseFile(data) {
try {
this.queue(parseFile(data));
} catch (e) {
this.emit('error', e);
}
}
module.exports = through(throughParseFile);
module.exports.parseFile = parseFile;
module.exports.extractYAML = extractYAML;