Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80682 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
// 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) {
40
'use strict';
41
if (key === 'value' && value instanceof RegExp) {
42
value = value.toString();
43
}
44
return value;
45
}
46
47
function NotMatchingError(expected, actual) {
48
'use strict';
49
Error.call(this, 'Expected ');
50
this.expected = expected;
51
this.actual = actual;
52
}
53
NotMatchingError.prototype = new Error();
54
55
function errorToObject(e) {
56
'use strict';
57
var msg = e.toString();
58
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;
63
}
64
}
65
66
return {
67
index: e.index,
68
lineNumber: e.lineNumber,
69
column: e.column,
70
message: msg
71
};
72
}
73
74
function needLoc(syntax) {
75
var need = true;
76
if (typeof syntax.tokens !== 'undefined' && syntax.tokens.length > 0) {
77
need = (typeof syntax.tokens[0].loc !== 'undefined');
78
}
79
if (typeof syntax.comments !== 'undefined' && syntax.comments.length > 0) {
80
need = (typeof syntax.comments[0].loc !== 'undefined');
81
}
82
return need;
83
}
84
85
function needRange(syntax) {
86
var need = true;
87
if (typeof syntax.tokens !== 'undefined' && syntax.tokens.length > 0) {
88
need = (typeof syntax.tokens[0].range !== 'undefined');
89
}
90
if (typeof syntax.comments !== 'undefined' && syntax.comments.length > 0) {
91
need = (typeof syntax.comments[0].range !== 'undefined');
92
}
93
return need;
94
}
95
96
function testParse(esprima, code, syntax) {
97
'use strict';
98
var expected, tree, actual, options, StringObject, i, len, err;
99
100
// alias, so that JSLint does not complain.
101
StringObject = String;
102
103
options = {
104
comment: (typeof syntax.comments !== 'undefined'),
105
range: needRange(syntax),
106
loc: needLoc(syntax),
107
tokens: (typeof syntax.tokens !== 'undefined'),
108
raw: true,
109
tolerant: (typeof syntax.errors !== 'undefined'),
110
source: null
111
};
112
113
if (options.loc) {
114
options.source = syntax.loc.source;
115
}
116
117
expected = JSON.stringify(syntax, null, 4);
118
try {
119
tree = esprima.parse(code, options);
120
tree = (options.comment || options.tokens || options.tolerant) ? tree : tree.body[0];
121
122
if (options.tolerant) {
123
for (i = 0, len = tree.errors.length; i < len; i += 1) {
124
tree.errors[i] = errorToObject(tree.errors[i]);
125
}
126
}
127
128
actual = JSON.stringify(tree, adjustRegexLiteral, 4);
129
130
// Only to ensure that there is no error when using string object.
131
esprima.parse(new StringObject(code), options);
132
133
} catch (e) {
134
throw new NotMatchingError(expected, e.toString());
135
}
136
if (expected !== actual) {
137
throw new NotMatchingError(expected, actual);
138
}
139
140
function filter(key, value) {
141
if (key === 'value' && value instanceof RegExp) {
142
value = value.toString();
143
}
144
return (key === 'loc' || key === 'range') ? undefined : value;
145
}
146
147
if (options.tolerant) {
148
return;
149
}
150
151
152
// Check again without any location info.
153
options.range = false;
154
options.loc = false;
155
expected = JSON.stringify(syntax, filter, 4);
156
try {
157
tree = esprima.parse(code, options);
158
tree = (options.comment || options.tokens) ? tree : tree.body[0];
159
160
if (options.tolerant) {
161
for (i = 0, len = tree.errors.length; i < len; i += 1) {
162
tree.errors[i] = errorToObject(tree.errors[i]);
163
}
164
}
165
166
actual = JSON.stringify(tree, filter, 4);
167
} catch (e) {
168
throw new NotMatchingError(expected, e.toString());
169
}
170
if (expected !== actual) {
171
throw new NotMatchingError(expected, actual);
172
}
173
}
174
175
function mustHaveLocRange(testName, node, needLoc, needRange, stack) {
176
var error;
177
if (node.hasOwnProperty('type')) {
178
if (needLoc && !node.loc) {
179
error = "doesn't have 'loc' property";
180
}
181
if (needRange && !node.range) {
182
error = "doesn't have 'range' property";
183
}
184
if (error) {
185
stack = stack.length ? ' at [' + stack.join('][') + ']' : '';
186
throw new Error("Test '" + testName + "'" + stack + " (type = " + node.type + ") " + error);
187
}
188
}
189
for (i in node) {
190
if (node.hasOwnProperty(i) && node[i] !== null && typeof node[i] === 'object') {
191
stack.push(i);
192
mustHaveLocRange(testName, node[i], needLoc, needRange, stack);
193
stack.pop();
194
}
195
}
196
}
197
198
function testTokenize(esprima, code, tokens) {
199
'use strict';
200
var options, expected, actual, tree;
201
202
options = {
203
comment: true,
204
tolerant: true,
205
loc: true,
206
range: true
207
};
208
209
expected = JSON.stringify(tokens, null, 4);
210
211
try {
212
tree = esprima.tokenize(code, options);
213
actual = JSON.stringify(tree, null, 4);
214
} catch (e) {
215
throw new NotMatchingError(expected, e.toString());
216
}
217
if (expected !== actual) {
218
throw new NotMatchingError(expected, actual);
219
}
220
}
221
222
function testError(esprima, code, exception) {
223
'use strict';
224
var i, options, expected, actual, err, handleInvalidRegexFlag, tokenize;
225
226
// Different parsing options should give the same error.
227
options = [
228
{},
229
{ comment: true },
230
{ raw: true },
231
{ raw: true, comment: true }
232
];
233
234
// If handleInvalidRegexFlag is true, an invalid flag in a regular expression
235
// will throw an exception. In some old version V8, this is not the case
236
// and hence handleInvalidRegexFlag is false.
237
handleInvalidRegexFlag = false;
238
try {
239
'test'.match(new RegExp('[a-z]', 'x'));
240
} catch (e) {
241
handleInvalidRegexFlag = true;
242
}
243
244
exception.description = exception.message.replace(/Error: Line [0-9]+: /, '');
245
246
if (exception.tokenize) {
247
tokenize = true;
248
exception.tokenize = undefined;
249
}
250
expected = JSON.stringify(exception);
251
252
for (i = 0; i < options.length; i += 1) {
253
254
try {
255
if (tokenize) {
256
esprima.tokenize(code, options[i])
257
} else {
258
esprima.parse(code, options[i]);
259
}
260
} catch (e) {
261
err = errorToObject(e);
262
err.description = e.description;
263
actual = JSON.stringify(err);
264
}
265
266
if (expected !== actual) {
267
268
// Compensate for old V8 which does not handle invalid flag.
269
if (exception.message.indexOf('Invalid regular expression') > 0) {
270
if (typeof actual === 'undefined' && !handleInvalidRegexFlag) {
271
return;
272
}
273
}
274
275
throw new NotMatchingError(expected, actual);
276
}
277
278
}
279
}
280
281
function testAPI(esprima, code, result) {
282
'use strict';
283
var expected, res, actual;
284
285
expected = JSON.stringify(result.result, null, 4);
286
try {
287
if (typeof result.property !== 'undefined') {
288
res = esprima[result.property];
289
} else {
290
res = esprima[result.call].apply(esprima, result.args);
291
}
292
actual = JSON.stringify(res, adjustRegexLiteral, 4);
293
} catch (e) {
294
throw new NotMatchingError(expected, e.toString());
295
}
296
if (expected !== actual) {
297
throw new NotMatchingError(expected, actual);
298
}
299
}
300
301
function runTest(esprima, code, result) {
302
'use strict';
303
if (result.hasOwnProperty('lineNumber')) {
304
testError(esprima, code, result);
305
} else if (result.hasOwnProperty('result')) {
306
testAPI(esprima, code, result);
307
} else if (result instanceof Array) {
308
testTokenize(esprima, code, result);
309
} else {
310
testParse(esprima, code, result);
311
}
312
}
313
314
if (typeof window !== 'undefined') {
315
// Run all tests in a browser environment.
316
runTests = function () {
317
'use strict';
318
var total = 0,
319
failures = 0,
320
category,
321
fixture,
322
source,
323
tick,
324
expected,
325
index,
326
len;
327
328
function setText(el, str) {
329
if (typeof el.innerText === 'string') {
330
el.innerText = str;
331
} else {
332
el.textContent = str;
333
}
334
}
335
336
function startCategory(category) {
337
var report, e;
338
report = document.getElementById('report');
339
e = document.createElement('h4');
340
setText(e, category);
341
report.appendChild(e);
342
}
343
344
function reportSuccess(code) {
345
var report, e;
346
report = document.getElementById('report');
347
e = document.createElement('pre');
348
e.setAttribute('class', 'code');
349
setText(e, code);
350
report.appendChild(e);
351
}
352
353
function reportFailure(code, expected, actual) {
354
var report, e;
355
356
report = document.getElementById('report');
357
358
e = document.createElement('p');
359
setText(e, 'Code:');
360
report.appendChild(e);
361
362
e = document.createElement('pre');
363
e.setAttribute('class', 'code');
364
setText(e, code);
365
report.appendChild(e);
366
367
e = document.createElement('p');
368
setText(e, 'Expected');
369
report.appendChild(e);
370
371
e = document.createElement('pre');
372
e.setAttribute('class', 'expected');
373
setText(e, expected);
374
report.appendChild(e);
375
376
e = document.createElement('p');
377
setText(e, 'Actual');
378
report.appendChild(e);
379
380
e = document.createElement('pre');
381
e.setAttribute('class', 'actual');
382
setText(e, actual);
383
report.appendChild(e);
384
}
385
386
setText(document.getElementById('version'), esprima.version);
387
388
tick = new Date();
389
for (category in testFixture) {
390
if (testFixture.hasOwnProperty(category)) {
391
startCategory(category);
392
fixture = testFixture[category];
393
for (source in fixture) {
394
if (fixture.hasOwnProperty(source)) {
395
expected = fixture[source];
396
total += 1;
397
try {
398
runTest(esprima, source, expected);
399
reportSuccess(source, JSON.stringify(expected, null, 4));
400
} catch (e) {
401
failures += 1;
402
reportFailure(source, e.expected, e.actual);
403
}
404
}
405
}
406
}
407
}
408
tick = (new Date()) - tick;
409
410
if (failures > 0) {
411
document.getElementById('status').className = 'alert-box alert';
412
setText(document.getElementById('status'), total + ' tests. ' +
413
'Failures: ' + failures + '. ' + tick + ' ms.');
414
} else {
415
document.getElementById('status').className = 'alert-box success';
416
setText(document.getElementById('status'), total + ' tests. ' +
417
'No failure. ' + tick + ' ms.');
418
}
419
};
420
} else {
421
(function () {
422
'use strict';
423
424
var esprima = require('../esprima'),
425
vm = require('vm'),
426
fs = require('fs'),
427
diff = require('json-diff').diffString,
428
total = 0,
429
failures = [],
430
tick = new Date(),
431
expected,
432
header;
433
434
vm.runInThisContext(fs.readFileSync(__dirname + '/test.js', 'utf-8'));
435
vm.runInThisContext(fs.readFileSync(__dirname + '/harmonytest.js', 'utf-8'));
436
vm.runInThisContext(fs.readFileSync(__dirname + '/fbtest.js', 'utf-8'));
437
438
Object.keys(testFixture).forEach(function (category) {
439
Object.keys(testFixture[category]).forEach(function (source) {
440
total += 1;
441
expected = testFixture[category][source];
442
if (!expected.hasOwnProperty('lineNumber') && !expected.hasOwnProperty('result')) {
443
mustHaveLocRange(source, expected, needLoc(expected), needRange(expected), []);
444
}
445
try {
446
runTest(esprima, source, expected);
447
} catch (e) {
448
e.source = source;
449
failures.push(e);
450
}
451
});
452
});
453
tick = (new Date()) - tick;
454
455
header = total + ' tests. ' + failures.length + ' failures. ' +
456
tick + ' ms';
457
if (failures.length) {
458
console.error(header);
459
failures.forEach(function (failure) {
460
try {
461
var expectedObject = JSON.parse(failure.expected);
462
var actualObject = JSON.parse(failure.actual);
463
464
console.error(failure.source + ': Expected\n ' +
465
failure.expected.split('\n').join('\n ') +
466
'\nto match\n ' + failure.actual + '\nDiff:\n' +
467
diff(expectedObject, actualObject));
468
} catch (ex) {
469
console.error(failure.source + ': Expected\n ' +
470
failure.expected.split('\n').join('\n ') +
471
'\nto match\n ' + failure.actual);
472
}
473
});
474
} else {
475
console.log(header);
476
}
477
process.exit(failures.length === 0 ? 0 : 1);
478
}());
479
}
480
481