2 Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
3 Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
4 Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
5 Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
6 Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
7 Copyright (C) 2011 Yusuke Suzuki <utatane.tea@gmail.com>
8 Copyright (C) 2011 Arpad Borsos <arpad.borsos@googlemail.com>
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
13 * Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
23 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 /*jslint browser:true node:true */
32 /*global esprima:true, testFixture:true */
36 // Special handling for regular expression literal since we need to
37 // convert it to a string literal, otherwise it will be decoded
38 // as object "{}" and the regular expression would be lost.
39 function adjustRegexLiteral(key, value) {
41 if (key === 'value' && value instanceof RegExp) {
42 value = value.toString();
47 function NotMatchingError(expected, actual) {
49 Error.call(this, 'Expected ');
50 this.expected = expected;
53 NotMatchingError.prototype = new Error();
55 function errorToObject(e) {
57 var msg = e.toString();
59 // Opera 9.64 produces an non-standard string in toString().
60 if (msg.substr(0, 6) !== 'Error:') {
61 if (typeof e.message === 'string') {
62 msg = 'Error: ' + e.message;
68 lineNumber: e.lineNumber,
74 function testParse(esprima, code, syntax) {
76 var expected, tree, actual, options, StringObject, i, len, err;
78 // alias, so that JSLint does not complain.
79 StringObject = String;
82 comment: (typeof syntax.comments !== 'undefined'),
85 tokens: (typeof syntax.tokens !== 'undefined'),
87 tolerant: (typeof syntax.errors !== 'undefined')
90 if (typeof syntax.tokens !== 'undefined') {
91 if (syntax.tokens.length > 0) {
92 options.range = (typeof syntax.tokens[0].range !== 'undefined');
93 options.loc = (typeof syntax.tokens[0].loc !== 'undefined');
97 if (typeof syntax.comments !== 'undefined') {
98 if (syntax.comments.length > 0) {
99 options.range = (typeof syntax.comments[0].range !== 'undefined');
100 options.loc = (typeof syntax.comments[0].loc !== 'undefined');
104 expected = JSON.stringify(syntax, null, 4);
106 tree = esprima.parse(code, options);
107 tree = (options.comment || options.tokens || options.tolerant) ? tree : tree.body[0];
109 if (options.tolerant) {
110 for (i = 0, len = tree.errors.length; i < len; i += 1) {
111 tree.errors[i] = errorToObject(tree.errors[i]);
115 actual = JSON.stringify(tree, adjustRegexLiteral, 4);
117 // Only to ensure that there is no error when using string object.
118 esprima.parse(new StringObject(code), options);
121 throw new NotMatchingError(expected, e.toString());
123 if (expected !== actual) {
124 throw new NotMatchingError(expected, actual);
127 function filter(key, value) {
128 if (key === 'value' && value instanceof RegExp) {
129 value = value.toString();
131 return (key === 'loc' || key === 'range') ? undefined : value;
134 if (options.tolerant) {
139 // Check again without any location info.
140 options.range = false;
142 expected = JSON.stringify(syntax, filter, 4);
144 tree = esprima.parse(code, options);
145 tree = (options.comment || options.tokens) ? tree : tree.body[0];
147 if (options.tolerant) {
148 for (i = 0, len = tree.errors.length; i < len; i += 1) {
149 tree.errors[i] = errorToObject(tree.errors[i]);
153 actual = JSON.stringify(tree, filter, 4);
155 throw new NotMatchingError(expected, e.toString());
157 if (expected !== actual) {
158 throw new NotMatchingError(expected, actual);
162 function testError(esprima, code, exception) {
164 var i, options, expected, actual, handleInvalidRegexFlag;
166 // Different parsing options should give the same error.
171 { raw: true, comment: true }
174 // If handleInvalidRegexFlag is true, an invalid flag in a regular expression
175 // will throw an exception. In some old version V8, this is not the case
176 // and hence handleInvalidRegexFlag is false.
177 handleInvalidRegexFlag = false;
179 'test'.match(new RegExp('[a-z]', 'x'));
181 handleInvalidRegexFlag = true;
184 expected = JSON.stringify(exception);
186 for (i = 0; i < options.length; i += 1) {
189 esprima.parse(code, options[i]);
191 actual = JSON.stringify(errorToObject(e));
194 if (expected !== actual) {
196 // Compensate for old V8 which does not handle invalid flag.
197 if (exception.message.indexOf('Invalid regular expression') > 0) {
198 if (typeof actual === 'undefined' && !handleInvalidRegexFlag) {
203 throw new NotMatchingError(expected, actual);
209 function testAPI(esprima, code, result) {
211 var expected, res, actual;
213 expected = JSON.stringify(result.result, null, 4);
215 if (typeof result.property !== 'undefined') {
216 res = esprima[result.property];
218 res = esprima[result.call].apply(esprima, result.args);
220 actual = JSON.stringify(res, adjustRegexLiteral, 4);
222 throw new NotMatchingError(expected, e.toString());
224 if (expected !== actual) {
225 throw new NotMatchingError(expected, actual);
229 function runTest(esprima, code, result) {
231 if (result.hasOwnProperty('lineNumber')) {
232 testError(esprima, code, result);
233 } else if (result.hasOwnProperty('result')) {
234 testAPI(esprima, code, result);
236 testParse(esprima, code, result);
240 if (typeof window !== 'undefined') {
241 // Run all tests in a browser environment.
242 runTests = function () {
254 function setText(el, str) {
255 if (typeof el.innerText === 'string') {
258 el.textContent = str;
262 function startCategory(category) {
264 report = document.getElementById('report');
265 e = document.createElement('h4');
266 setText(e, category);
267 report.appendChild(e);
270 function reportSuccess(code) {
272 report = document.getElementById('report');
273 e = document.createElement('pre');
274 e.setAttribute('class', 'code');
276 report.appendChild(e);
279 function reportFailure(code, expected, actual) {
282 report = document.getElementById('report');
284 e = document.createElement('p');
286 report.appendChild(e);
288 e = document.createElement('pre');
289 e.setAttribute('class', 'code');
291 report.appendChild(e);
293 e = document.createElement('p');
294 setText(e, 'Expected');
295 report.appendChild(e);
297 e = document.createElement('pre');
298 e.setAttribute('class', 'expected');
299 setText(e, expected);
300 report.appendChild(e);
302 e = document.createElement('p');
303 setText(e, 'Actual');
304 report.appendChild(e);
306 e = document.createElement('pre');
307 e.setAttribute('class', 'actual');
309 report.appendChild(e);
312 setText(document.getElementById('version'), esprima.version);
315 for (category in testFixture) {
316 if (testFixture.hasOwnProperty(category)) {
317 startCategory(category);
318 fixture = testFixture[category];
319 for (source in fixture) {
320 if (fixture.hasOwnProperty(source)) {
321 expected = fixture[source];
324 runTest(esprima, source, expected);
325 reportSuccess(source, JSON.stringify(expected, null, 4));
328 reportFailure(source, e.expected, e.actual);
334 tick = (new Date()) - tick;
337 setText(document.getElementById('status'), total + ' tests. ' +
338 'Failures: ' + failures + '. ' + tick + ' ms');
340 setText(document.getElementById('status'), total + ' tests. ' +
341 'No failure. ' + tick + ' ms');
348 var esprima = require('../esprima'),
357 vm.runInThisContext(fs.readFileSync(__dirname + '/test.js', 'utf-8'));
359 Object.keys(testFixture).forEach(function (category) {
360 Object.keys(testFixture[category]).forEach(function (source) {
362 expected = testFixture[category][source];
364 runTest(esprima, source, expected);
371 tick = (new Date()) - tick;
373 header = total + ' tests. ' + failures.length + ' failures. ' +
375 if (failures.length) {
376 console.error(header);
377 failures.forEach(function (failure) {
378 console.error(failure.source + ': Expected\n ' +
379 failure.expected.split('\n').join('\n ') +
380 '\nto match\n ' + failure.actual);
385 process.exit(failures.length === 0 ? 0 : 1);