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