Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80713 views
1
/*
2
Copyright (C) 2012 Ariya Hidayat <[email protected]>
3
Copyright (C) 2012 Joost-Wim Boekesteijn <[email protected]>
4
Copyright (C) 2012 Yusuke Suzuki <[email protected]>
5
Copyright (C) 2012 Arpad Borsos <[email protected]>
6
Copyright (C) 2011 Ariya Hidayat <[email protected]>
7
Copyright (C) 2011 Yusuke Suzuki <[email protected]>
8
Copyright (C) 2011 Arpad Borsos <[email protected]>
9
10
Redistribution and use in source and binary forms, with or without
11
modification, are permitted provided that the following conditions are met:
12
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.
18
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.
29
*/
30
31
/*jslint browser:true node:true */
32
/*global esprima:true, testFixture:true */
33
34
var runTests;
35
36
function NotMatchingError(expected, actual) {
37
'use strict';
38
Error.call(this, 'Expected ');
39
this.expected = expected;
40
this.actual = actual;
41
}
42
NotMatchingError.prototype = new Error();
43
44
function errorToObject(e) {
45
'use strict';
46
var msg = e.toString();
47
48
// Opera 9.64 produces an non-standard string in toString().
49
if (msg.substr(0, 6) !== 'Error:') {
50
if (typeof e.message === 'string') {
51
msg = 'Error: ' + e.message;
52
}
53
}
54
55
return {
56
index: e.index,
57
lineNumber: e.lineNumber,
58
column: e.column,
59
message: msg
60
};
61
}
62
63
function sortedObject(o) {
64
if (o === null) {
65
return o;
66
}
67
if (Array.isArray(o)) {
68
return o.map(sortedObject);
69
}
70
if (typeof o !== 'object') {
71
return o;
72
}
73
if (o instanceof RegExp) {
74
return o;
75
}
76
var keys = Object.keys(o);
77
var result = {
78
range: undefined,
79
loc: undefined
80
};
81
keys.forEach(function (key) {
82
if (o.hasOwnProperty(key)){
83
result[key] = sortedObject(o[key]);
84
}
85
});
86
return result;
87
}
88
89
function hasAttachedComment(syntax) {
90
var key;
91
for (key in syntax) {
92
if (key === 'leadingComments' || key === 'trailingComments') {
93
return true;
94
}
95
if (typeof syntax[key] === 'object' && syntax[key] !== null) {
96
if (hasAttachedComment(syntax[key])) {
97
return true;
98
}
99
}
100
}
101
return false;
102
}
103
104
function testParse(esprima, code, syntax) {
105
'use strict';
106
var expected, tree, actual, options, StringObject, i, len;
107
108
// alias, so that JSLint does not complain.
109
StringObject = String;
110
111
options = {
112
comment: (typeof syntax.comments !== 'undefined'),
113
range: true,
114
loc: true,
115
tokens: (typeof syntax.tokens !== 'undefined'),
116
raw: true,
117
tolerant: (typeof syntax.errors !== 'undefined'),
118
source: null,
119
sourceType: syntax.sourceType
120
};
121
122
if (options.comment) {
123
options.attachComment = hasAttachedComment(syntax);
124
}
125
126
if (typeof syntax.tokens !== 'undefined') {
127
if (syntax.tokens.length > 0) {
128
options.range = (typeof syntax.tokens[0].range !== 'undefined');
129
options.loc = (typeof syntax.tokens[0].loc !== 'undefined');
130
}
131
}
132
133
if (typeof syntax.comments !== 'undefined') {
134
if (syntax.comments.length > 0) {
135
options.range = (typeof syntax.comments[0].range !== 'undefined');
136
options.loc = (typeof syntax.comments[0].loc !== 'undefined');
137
}
138
}
139
140
if (options.loc) {
141
options.source = syntax.loc.source;
142
}
143
144
syntax = sortedObject(syntax);
145
expected = JSON.stringify(syntax, null, 4);
146
try {
147
// Some variations of the options.
148
tree = esprima.parse(code, { tolerant: options.tolerant, sourceType: options.sourceType });
149
tree = esprima.parse(code, { tolerant: options.tolerant, sourceType: options.sourceType, range: true });
150
tree = esprima.parse(code, { tolerant: options.tolerant, sourceType: options.sourceType, loc: true });
151
152
tree = esprima.parse(code, options);
153
154
if (options.tolerant) {
155
for (i = 0, len = tree.errors.length; i < len; i += 1) {
156
tree.errors[i] = errorToObject(tree.errors[i]);
157
}
158
}
159
tree = sortedObject(tree);
160
actual = JSON.stringify(tree, null, 4);
161
162
// Only to ensure that there is no error when using string object.
163
esprima.parse(new StringObject(code), options);
164
165
} catch (e) {
166
throw new NotMatchingError(expected, e.toString());
167
}
168
if (expected !== actual) {
169
throw new NotMatchingError(expected, actual);
170
}
171
172
function filter(key, value) {
173
return (key === 'loc' || key === 'range') ? undefined : value;
174
}
175
176
if (options.tolerant) {
177
return;
178
}
179
180
181
// Check again without any location info.
182
options.range = false;
183
options.loc = false;
184
syntax = sortedObject(syntax);
185
expected = JSON.stringify(syntax, filter, 4);
186
try {
187
tree = esprima.parse(code, options);
188
189
if (options.tolerant) {
190
for (i = 0, len = tree.errors.length; i < len; i += 1) {
191
tree.errors[i] = errorToObject(tree.errors[i]);
192
}
193
}
194
tree = sortedObject(tree);
195
actual = JSON.stringify(tree, filter, 4);
196
} catch (e) {
197
throw new NotMatchingError(expected, e.toString());
198
}
199
if (expected !== actual) {
200
throw new NotMatchingError(expected, actual);
201
}
202
}
203
204
function testTokenize(esprima, code, tokens) {
205
'use strict';
206
var options, expected, actual, tree;
207
208
options = {
209
comment: true,
210
tolerant: true,
211
loc: true,
212
range: true
213
};
214
215
expected = JSON.stringify(tokens, null, 4);
216
217
try {
218
tree = esprima.tokenize(code, options);
219
actual = JSON.stringify(tree, null, 4);
220
} catch (e) {
221
throw new NotMatchingError(expected, e.toString());
222
}
223
if (expected !== actual) {
224
throw new NotMatchingError(expected, actual);
225
}
226
}
227
228
229
function testModule(esprima, code, exception) {
230
'use strict';
231
var i, options, expected, actual, err, handleInvalidRegexFlag, tokenize;
232
233
// Different parsing options should give the same error.
234
options = [
235
{ sourceType: 'module' },
236
{ sourceType: 'module', comment: true },
237
{ sourceType: 'module', raw: true },
238
{ sourceType: 'module', raw: true, comment: true }
239
];
240
241
if (!exception.message) {
242
exception.message = 'Error: Line 1: ' + exception.description;
243
}
244
exception.description = exception.message.replace(/Error: Line [0-9]+: /, '');
245
246
expected = JSON.stringify(exception);
247
248
for (i = 0; i < options.length; i += 1) {
249
250
try {
251
esprima.parse(code, options[i]);
252
} catch (e) {
253
err = errorToObject(e);
254
err.description = e.description;
255
actual = JSON.stringify(err);
256
}
257
258
if (expected !== actual) {
259
260
// Compensate for old V8 which does not handle invalid flag.
261
if (exception.message.indexOf('Invalid regular expression') > 0) {
262
if (typeof actual === 'undefined' && !handleInvalidRegexFlag) {
263
return;
264
}
265
}
266
267
throw new NotMatchingError(expected, actual);
268
}
269
270
}
271
}
272
273
function testError(esprima, code, exception) {
274
'use strict';
275
var i, options, expected, actual, err, handleInvalidRegexFlag, tokenize;
276
277
// Different parsing options should give the same error.
278
options = [
279
{},
280
{ comment: true },
281
{ raw: true },
282
{ raw: true, comment: true }
283
];
284
285
// If handleInvalidRegexFlag is true, an invalid flag in a regular expression
286
// will throw an exception. In some old version of V8, this is not the case
287
// and hence handleInvalidRegexFlag is false.
288
handleInvalidRegexFlag = false;
289
try {
290
'test'.match(new RegExp('[a-z]', 'x'));
291
} catch (e) {
292
handleInvalidRegexFlag = true;
293
}
294
295
exception.description = exception.message.replace(/Error: Line [0-9]+: /, '');
296
297
if (exception.tokenize) {
298
tokenize = true;
299
exception.tokenize = undefined;
300
}
301
expected = JSON.stringify(exception);
302
303
for (i = 0; i < options.length; i += 1) {
304
305
try {
306
if (tokenize) {
307
esprima.tokenize(code, options[i]);
308
} else {
309
esprima.parse(code, options[i]);
310
}
311
} catch (e) {
312
err = errorToObject(e);
313
err.description = e.description;
314
actual = JSON.stringify(err);
315
}
316
317
if (expected !== actual) {
318
319
// Compensate for old V8 which does not handle invalid flag.
320
if (exception.message.indexOf('Invalid regular expression') > 0) {
321
if (typeof actual === 'undefined' && !handleInvalidRegexFlag) {
322
return;
323
}
324
}
325
326
throw new NotMatchingError(expected, actual);
327
}
328
329
}
330
}
331
332
function testAPI(esprima, code, expected) {
333
var result;
334
// API test.
335
expected = JSON.stringify(expected, null, 4);
336
try {
337
result = eval(code);
338
result = JSON.stringify(result, null, 4);
339
} catch (e) {
340
throw new NotMatchingError(expected, e.toString());
341
}
342
if (expected !== result) {
343
throw new NotMatchingError(expected, result);
344
}
345
}
346
347
function generateTestCase(esprima, testCase) {
348
var tree, fileName = testCase.key + ".tree.json";
349
try {
350
tree = esprima.parse(testCase.case, {loc: true, range: true});
351
tree = JSON.stringify(tree, null, 4);
352
} catch (e) {
353
if (typeof e.index === 'undefined') {
354
console.error("Failed to generate test result.");
355
throw e;
356
}
357
tree = errorToObject(e);
358
tree.description = e.description;
359
tree = JSON.stringify(tree);
360
fileName = testCase.key + ".failure.json";
361
}
362
require('fs').writeFileSync(fileName, tree);
363
console.error("Done.");
364
}
365
366
if (typeof window === 'undefined') {
367
(function () {
368
'use strict';
369
370
var esprima = require('../esprima'),
371
vm = require('vm'),
372
fs = require('fs'),
373
diff = require('json-diff').diffString,
374
total = 0,
375
result,
376
failures = [],
377
cases = {},
378
context = {source: '', result: null},
379
tick = new Date(),
380
expected,
381
testCase,
382
header;
383
384
function enumerateFixtures(root) {
385
var dirs = fs.readdirSync(root), key, kind,
386
kinds = ['case', 'source', 'module', 'run', 'tree', 'tokens', 'failure', 'result'],
387
suffices = ['js', 'js', 'json', 'js', 'json', 'json', 'json', 'json'];
388
389
dirs.forEach(function (item) {
390
var i;
391
if (fs.statSync(root + '/' + item).isDirectory()) {
392
enumerateFixtures(root + '/' + item);
393
} else {
394
kind = 'case';
395
key = item.slice(0, -3);
396
for (i = 1; i < kinds.length; i++) {
397
var suffix = '.' + kinds[i] + '.' + suffices[i];
398
if (item.slice(-suffix.length) === suffix) {
399
key = item.slice(0, -suffix.length);
400
kind = kinds[i];
401
}
402
}
403
key = root + '/' + key;
404
if (!cases[key]) {
405
total++;
406
cases[key] = { key: key };
407
}
408
cases[key][kind] = fs.readFileSync(root + '/' + item, 'utf-8');
409
}
410
});
411
}
412
413
enumerateFixtures(__dirname + '/fixtures');
414
415
for (var key in cases) {
416
if (cases.hasOwnProperty(key)) {
417
testCase = cases[key];
418
419
if (testCase.hasOwnProperty('source')) {
420
testCase.case = eval(testCase.source + ';source');
421
}
422
423
try {
424
if (testCase.hasOwnProperty('module')) {
425
testModule(esprima, testCase.case, JSON.parse(testCase.module));
426
} else if (testCase.hasOwnProperty('tree')) {
427
testParse(esprima, testCase.case, JSON.parse(testCase.tree));
428
} else if (testCase.hasOwnProperty('tokens')) {
429
testTokenize(esprima, testCase.case, JSON.parse(testCase.tokens));
430
} else if (testCase.hasOwnProperty('failure')) {
431
testError(esprima, testCase.case, JSON.parse(testCase.failure));
432
} else if (testCase.hasOwnProperty('result')) {
433
testAPI(esprima, testCase.run, JSON.parse(testCase.result));
434
} else {
435
console.error('Incomplete test case:' + testCase.key + '. Generating test result...');
436
generateTestCase(esprima, testCase);
437
}
438
} catch (e) {
439
if (!e.expected) {
440
throw e;
441
}
442
e.source = testCase.case || testCase.key;
443
failures.push(e);
444
}
445
}
446
}
447
448
tick = (new Date()) - tick;
449
450
header = total + ' tests. ' + failures.length + ' failures. ' +
451
tick + ' ms';
452
if (failures.length) {
453
console.error(header);
454
failures.forEach(function (failure) {
455
try {
456
var expectedObject = JSON.parse(failure.expected);
457
var actualObject = JSON.parse(failure.actual);
458
459
console.error(failure.source + ': Expected\n ' +
460
failure.expected.split('\n').join('\n ') +
461
'\nto match\n ' + failure.actual + '\nDiff:\n' +
462
diff(expectedObject, actualObject));
463
} catch (ex) {
464
console.error(failure.source + ': Expected\n ' +
465
failure.expected.split('\n').join('\n ') +
466
'\nto match\n ' + failure.actual);
467
}
468
});
469
} else {
470
console.log(header);
471
}
472
process.exit(failures.length === 0 ? 0 : 1);
473
474
}());
475
}
476
477