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