7f4a1738bb9e8570283aaa5b6c5136a100b940bd
[platform/framework/web/crosswalk-tizen.git] /
1 /*
2   Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
3   Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
4   Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
5   Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
6   Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
7   Copyright (C) 2011 Yusuke Suzuki <utatane.tea@gmail.com>
8   Copyright (C) 2011 Arpad Borsos <arpad.borsos@googlemail.com>
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 }