Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80684 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
};
120
121
if (options.comment) {
122
options.attachComment = hasAttachedComment(syntax);
123
}
124
125
if (typeof syntax.tokens !== 'undefined') {
126
if (syntax.tokens.length > 0) {
127
options.range = (typeof syntax.tokens[0].range !== 'undefined');
128
options.loc = (typeof syntax.tokens[0].loc !== 'undefined');
129
}
130
}
131
132
if (typeof syntax.comments !== 'undefined') {
133
if (syntax.comments.length > 0) {
134
options.range = (typeof syntax.comments[0].range !== 'undefined');
135
options.loc = (typeof syntax.comments[0].loc !== 'undefined');
136
}
137
}
138
139
if (options.loc) {
140
options.source = syntax.loc.source;
141
}
142
143
syntax = sortedObject(syntax);
144
expected = JSON.stringify(syntax, null, 4);
145
try {
146
// Some variations of the options.
147
tree = esprima.parse(code, { tolerant: options.tolerant });
148
tree = esprima.parse(code, { tolerant: options.tolerant, range: true });
149
tree = esprima.parse(code, { tolerant: options.tolerant, loc: true });
150
151
tree = esprima.parse(code, options);
152
153
if (options.tolerant) {
154
for (i = 0, len = tree.errors.length; i < len; i += 1) {
155
tree.errors[i] = errorToObject(tree.errors[i]);
156
}
157
}
158
tree = sortedObject(tree);
159
actual = JSON.stringify(tree, null, 4);
160
161
// Only to ensure that there is no error when using string object.
162
esprima.parse(new StringObject(code), options);
163
164
} catch (e) {
165
throw new NotMatchingError(expected, e.toString());
166
}
167
if (expected !== actual) {
168
throw new NotMatchingError(expected, actual);
169
}
170
171
function filter(key, value) {
172
return (key === 'loc' || key === 'range') ? undefined : value;
173
}
174
175
if (options.tolerant) {
176
return;
177
}
178
179
180
// Check again without any location info.
181
options.range = false;
182
options.loc = false;
183
syntax = sortedObject(syntax);
184
expected = JSON.stringify(syntax, filter, 4);
185
try {
186
tree = esprima.parse(code, options);
187
188
if (options.tolerant) {
189
for (i = 0, len = tree.errors.length; i < len; i += 1) {
190
tree.errors[i] = errorToObject(tree.errors[i]);
191
}
192
}
193
tree = sortedObject(tree);
194
actual = JSON.stringify(tree, filter, 4);
195
} catch (e) {
196
throw new NotMatchingError(expected, e.toString());
197
}
198
if (expected !== actual) {
199
throw new NotMatchingError(expected, actual);
200
}
201
}
202
203
function testTokenize(esprima, code, tokens) {
204
'use strict';
205
var options, expected, actual, tree;
206
207
options = {
208
comment: true,
209
tolerant: true,
210
loc: true,
211
range: true
212
};
213
214
expected = JSON.stringify(tokens, null, 4);
215
216
try {
217
tree = esprima.tokenize(code, options);
218
actual = JSON.stringify(tree, null, 4);
219
} catch (e) {
220
throw new NotMatchingError(expected, e.toString());
221
}
222
if (expected !== actual) {
223
throw new NotMatchingError(expected, actual);
224
}
225
}
226
227
function testError(esprima, code, exception) {
228
'use strict';
229
var i, options, expected, actual, err, handleInvalidRegexFlag, tokenize;
230
231
// Different parsing options should give the same error.
232
options = [
233
{},
234
{ comment: true },
235
{ raw: true },
236
{ raw: true, comment: true }
237
];
238
239
// If handleInvalidRegexFlag is true, an invalid flag in a regular expression
240
// will throw an exception. In some old version of V8, this is not the case
241
// and hence handleInvalidRegexFlag is false.
242
handleInvalidRegexFlag = false;
243
try {
244
'test'.match(new RegExp('[a-z]', 'x'));
245
} catch (e) {
246
handleInvalidRegexFlag = true;
247
}
248
249
exception.description = exception.message.replace(/Error: Line [0-9]+: /, '');
250
251
if (exception.tokenize) {
252
tokenize = true;
253
exception.tokenize = undefined;
254
}
255
expected = JSON.stringify(exception);
256
257
for (i = 0; i < options.length; i += 1) {
258
259
try {
260
if (tokenize) {
261
esprima.tokenize(code, options[i]);
262
} else {
263
esprima.parse(code, options[i]);
264
}
265
} catch (e) {
266
err = errorToObject(e);
267
err.description = e.description;
268
actual = JSON.stringify(err);
269
}
270
271
if (expected !== actual) {
272
273
// Compensate for old V8 which does not handle invalid flag.
274
if (exception.message.indexOf('Invalid regular expression') > 0) {
275
if (typeof actual === 'undefined' && !handleInvalidRegexFlag) {
276
return;
277
}
278
}
279
280
throw new NotMatchingError(expected, actual);
281
}
282
283
}
284
}
285
286
function testAPI(esprima, code, expected) {
287
var result;
288
// API test.
289
expected = JSON.stringify(expected, null, 4);
290
try {
291
result = eval(code);
292
result = JSON.stringify(result, null, 4);
293
} catch (e) {
294
throw new NotMatchingError(expected, e.toString());
295
}
296
if (expected !== result) {
297
throw new NotMatchingError(expected, result);
298
}
299
}
300
301
function generateTestCase(esprima, testCase) {
302
var tree, fileName = testCase.key + ".tree.json";
303
try {
304
tree = esprima.parse(testCase.case, {loc: true, range: true});
305
tree = JSON.stringify(tree, null, 4);
306
} catch (e) {
307
if (typeof e.index === 'undefined') {
308
console.error("Failed to generate test result.");
309
throw e;
310
}
311
tree = errorToObject(e);
312
tree.description = e.description;
313
tree = JSON.stringify(tree);
314
fileName = testCase.key + ".failure.json";
315
}
316
require('fs').writeFileSync(fileName, tree);
317
console.error("Done.");
318
}
319
320
if (typeof window === 'undefined') {
321
(function () {
322
'use strict';
323
324
var esprima = require('../esprima'),
325
vm = require('vm'),
326
fs = require('fs'),
327
diff = require('json-diff').diffString,
328
total = 0,
329
result,
330
failures = [],
331
cases = {},
332
context = {source: '', result: null},
333
tick = new Date(),
334
expected,
335
testCase,
336
header;
337
338
function enumerateFixtures(root) {
339
var dirs = fs.readdirSync(root), key, kind,
340
kinds = ['case', 'source', 'run', 'tree', 'tokens', 'failure', 'result'],
341
suffices = ['js', 'js', 'js', 'json', 'json', 'json', 'json'];
342
343
dirs.forEach(function (item) {
344
var i;
345
if (fs.statSync(root + '/' + item).isDirectory()) {
346
enumerateFixtures(root + '/' + item);
347
} else {
348
kind = 'case';
349
key = item.slice(0, -3);
350
for (i = 1; i < kinds.length; i++) {
351
var suffix = '.' + kinds[i] + '.' + suffices[i];
352
if (item.slice(-suffix.length) === suffix) {
353
key = item.slice(0, -suffix.length);
354
kind = kinds[i];
355
}
356
}
357
key = root + '/' + key;
358
if (!cases[key]) {
359
total++;
360
cases[key] = { key: key };
361
}
362
cases[key][kind] = fs.readFileSync(root + '/' + item, 'utf-8');
363
}
364
});
365
}
366
367
enumerateFixtures(__dirname + '/fixtures');
368
369
for (var key in cases) {
370
if (cases.hasOwnProperty(key)) {
371
testCase = cases[key];
372
373
if (testCase.hasOwnProperty('source')) {
374
testCase.case = eval(testCase.source + ';source');
375
}
376
377
try {
378
if (testCase.hasOwnProperty('tree')) {
379
testParse(esprima, testCase.case, JSON.parse(testCase.tree));
380
} else if (testCase.hasOwnProperty('tokens')) {
381
testTokenize(esprima, testCase.case, JSON.parse(testCase.tokens));
382
} else if (testCase.hasOwnProperty('failure')) {
383
testError(esprima, testCase.case, JSON.parse(testCase.failure));
384
} else if (testCase.hasOwnProperty('result')) {
385
testAPI(esprima, testCase.run, JSON.parse(testCase.result));
386
} else {
387
console.error('Incomplete test case:' + testCase.key + '. Generating test result...');
388
generateTestCase(esprima, testCase);
389
}
390
} catch (e) {
391
if (!e.expected) {
392
throw e;
393
}
394
e.source = testCase.case || testCase.key;
395
failures.push(e);
396
}
397
}
398
}
399
400
tick = (new Date()) - tick;
401
402
header = total + ' tests. ' + failures.length + ' failures. ' +
403
tick + ' ms';
404
if (failures.length) {
405
console.error(header);
406
failures.forEach(function (failure) {
407
try {
408
var expectedObject = JSON.parse(failure.expected);
409
var actualObject = JSON.parse(failure.actual);
410
411
console.error(failure.source + ': Expected\n ' +
412
failure.expected.split('\n').join('\n ') +
413
'\nto match\n ' + failure.actual + '\nDiff:\n' +
414
diff(expectedObject, actualObject));
415
} catch (ex) {
416
console.error(failure.source + ': Expected\n ' +
417
failure.expected.split('\n').join('\n ') +
418
'\nto match\n ' + failure.actual);
419
}
420
});
421
} else {
422
console.log(header);
423
}
424
process.exit(failures.length === 0 ? 0 : 1);
425
}());
426
}
427
428