source: products/quintagroup.plonetabs/branches/tests/quintagroup/plonetabs/tests/ecmaunits/js/ecmaunit.js @ 3402

Last change on this file since 3402 was 884, checked in by mylan, 17 years ago

Correct reply notification message template.

File size: 16.2 KB
Line 
1/*****************************************************************************
2 *
3 * Copyright (c) 2003-2004 EcmaUnit Contributors. All rights reserved.
4 *
5 * This software is distributed under the terms of the EcmaUnit
6 * License. See LICENSE.txt for license text. For a list of EcmaUnit
7 * Contributors see CREDITS.txt.
8 *
9 *****************************************************************************/
10
11// $Id: ecmaunit.js 23620 2006-02-23 16:16:28Z guido $
12
13/*
14   Object-oriented prototype-based unit test suite
15*/
16
17function TestCase() {
18    /* a single test case */
19    this.name = 'TestCase';
20};
21
22TestCase.prototype.initialize = function(reporter) {
23    // this array's contents will be displayed when done (if it
24    // contains anything)
25    this._exceptions = new Array();
26    this._reporter = reporter;
27};
28
29TestCase.prototype.setUp = function() {
30    /* this will be called on before each test method that is ran */
31};
32
33TestCase.prototype.tearDown = function() {
34    /* this will be called after each test method that has been ran */
35};
36
37TestCase.prototype.assertEquals = function(var1, var2, message) {
38    /* assert whether 2 vars have the same value */
39    if (!message)  {
40        message = '';
41    } else {
42        message = "'" + message + "' ";
43    }
44    if (var1 != var2 &&
45            (!(var1 instanceof Array && var2 instanceof Array) ||
46                !this._arrayDeepCompare(var1, var2))) {
47        this._throwException('Assertion ' + message + 'failed: ' + 
48                                var1 + ' != ' + var2);
49    };
50};
51
52TestCase.prototype.assertNotEquals = function(var1, var2, message) {
53    /* assert whether 2 vars have different values */
54    if (!message)  {
55        message = '';
56    } else {
57        message = "'" + message + "' ";
58    }
59    if (var1 && var1.toSource && var2 && var2.toSource) {
60        if (var1.toSource() == var2.toSource()) {
61            this._throwException('Assertion ' + message + 'failed: ' + 
62                                    var1 + ' == ' + var2);
63        };
64    } else {
65        if (var1 == var2) {
66            this._throwException('Assertion ' + message + 'failed: ' + 
67                                    var1 + ' == ' + var2);
68        };
69    };
70};
71
72TestCase.prototype.debug = function(msg) {
73    this._reporter.debug(msg);
74}
75TestCase.prototype.assert = function(statement, message) {
76    /* assert whether a variable resolves to true */
77    if (!statement) {
78        if (!message) message = (statement && statement.toString) ? 
79                                    statement.toString() : statement;
80        this._throwException('Assertion \'' + message + '\' failed');
81    };
82};
83
84TestCase.prototype.assertTrue = TestCase.prototype.assert;
85
86TestCase.prototype.assertFalse = function(statement, message) {
87    /* assert whether a variable resolves to false */
88    if (statement) {
89        if (!message) message = statement.toString ? 
90                statement.toString() : statement;
91        this._throwException('AssertFalse \'' + message + '\' failed');
92    };
93};
94
95TestCase.prototype.assertThrows = function(func, exception, context) {
96    /* assert whether a certain exception is raised */
97    if (!context) {
98        context = null;
99    };
100    var exception_thrown = false;
101    // remove the first three args, they're the function's normal args
102    var args = [];
103    for (var i=3; i < arguments.length; i++) {
104        args.push(arguments[i]);
105    };
106    try {
107        func.apply(context, args);
108    } catch(e) {
109        // allow catching undefined exceptions too
110        if (exception === undefined) {
111        } else if (exception) {
112            var isinstance = false;
113            try {
114                if (e instanceof exception) {
115                    isinstance = true;
116                };
117            } catch(f) {
118            };
119            if (!isinstance) {
120                if (exception.toSource && e.toSource) {
121                    exception = exception.toSource();
122                    e = e.toSource();
123                };
124                if (exception.toString && e.toString) {
125                    exception = exception.toString();
126                    e = e.toString();
127                };
128                if (e != exception) {
129                    this._throwException('Function threw the wrong ' +
130                            'exception ' + e.toString() + 
131                            ', while expecting ' + exception.toString());
132                };
133            };
134        };
135        exception_thrown = true;
136    };
137    if (!exception_thrown) {
138        if (exception) {
139            this._throwException("function didn\'t raise exception \'" + 
140                                    exception.toString() + "'");
141        } else {
142            this._throwException('function didn\'t raise exception');
143        };
144    };
145};
146
147TestCase.prototype.runTests = function() {
148    /* find all methods of which the name starts with 'test'
149        and call them */
150    var ret = this._runHelper();
151    this._reporter.summarize(ret[0], ret[1], this._exceptions);
152};
153
154TestCase.prototype._runHelper = function() {
155    /* this actually runs the tests
156        return value is an array [total tests ran, total time spent (ms)]
157    */
158    var now = new Date();
159    var starttime = now.getTime();
160    var numtests = 0;
161    for (var attr in this) {
162        if (attr.substr(0, 4) == 'test') {
163            this.setUp();
164            try {
165                this[attr]();
166                this._reporter.reportSuccess(this.name, attr);
167            } catch(e) {
168                var raw = e;
169                if (e && e.name && e.message) { // Microsoft
170                    e = e.name + ': ' + e.message;
171                }
172                this._reporter.reportError(this.name, attr, e, raw);
173                this._exceptions.push(new Array(this.name, attr, e, raw));
174            };
175            this.tearDown();
176            numtests++;
177        };
178    };
179    var now = new Date();
180    var totaltime = now.getTime() - starttime;
181    return new Array(numtests, totaltime);
182};
183
184TestCase.prototype._throwException = function(message) {
185    var lineno = this._getLineNo();
186    if (lineno) {
187        message = 'line ' + lineno + ' - ' + message;
188    };
189    throw(message);
190};
191
192TestCase.prototype._getLineNo = function() {
193    /* tries to get the line no in Moz */
194    var stack = undefined;
195    try {notdefined()} catch(e) {stack = e.stack};
196    if (stack) {
197        stack = stack.toString().split('\n');
198        for (var i=0; i < stack.length; i++) {
199            var line = stack[i].split('@')[1];
200            if (line.indexOf('ecmaunit') == -1) {
201                // return the first line after we get out of ecmaunit
202                var chunks = line.split(':');
203                var lineno = chunks[chunks.length - 1];
204                if (lineno != '0') {
205                    return lineno;
206                };
207            };
208        };
209    } else {
210        return false;
211    };
212};
213
214TestCase.prototype._arrayDeepCompare = function(a1, a2) {
215    if (!(a1 instanceof Array && a2 instanceof Array)) {
216        return false;
217    };
218    if (a1.length != a2.length) {
219        return false;
220    };
221    for (var i=0; i < a1.length; i++) {
222        if (a1[i] instanceof Array) {
223            if (!this._arrayDeepCompare(a1[i], a2[i])) {
224                return false;
225            };
226        } else if (a1[i] != a2[i]) {
227            return false;
228        };
229    };
230    return true;
231};
232
233function TestSuite(reporter) {
234    /* run a suite of tests */
235    if (reporter) {
236        this._reporter = reporter;
237        this._tests = new Array();
238        this._exceptions = new Array();
239    };
240};
241
242TestSuite.prototype.registerTest = function(test) {
243    /* register a test */
244    if (!test) {
245        throw('TestSuite.registerTest() requires a testcase as argument');
246    };
247    this._tests.push(test);
248};
249
250TestSuite.prototype.runSuite = function() {
251    /* run the suite */
252    var now = new Date();
253    var starttime = now.getTime();
254    var testsran = 0;
255    for (var i=0; i < this._tests.length; i++) {
256        var test = new this._tests[i]();
257        test.initialize(this._reporter);
258        testsran += test._runHelper()[0];
259        // the TestCase class handles output of dots and Fs, but we
260        // should take care of the exceptions
261        if (test._exceptions.length) {
262            for (var j=0; j < test._exceptions.length; j++) {
263                // attr, exc in the org array, so here it becomes
264                // name, attr, exc
265                var excinfo = test._exceptions[j];
266                this._exceptions.push(excinfo);
267            };
268        };
269    };
270    var now = new Date();
271    var totaltime = now.getTime() - starttime;
272    this._reporter.summarize(testsran, totaltime, this._exceptions);
273};
274
275function StdoutReporter(verbose) {
276    if (verbose) {
277        this.verbose = verbose;
278    };
279};
280
281StdoutReporter.prototype.debug = function(text) {
282    print(text+"\n");
283}
284
285StdoutReporter.prototype.reportSuccess = function(testcase, attr) {
286    /* report a test success */
287    if (this.verbose) {
288        print(testcase + '.' + attr + '(): OK');
289    } else {
290        print('.');
291    };
292};
293
294StdoutReporter.prototype.reportError = function(testcase, attr, 
295                                                exception, raw) {
296    /* report a test failure */
297    if (this.verbose) {
298        print(testcase + '.' + attr + '(): FAILED!');
299    } else {
300        print('F');
301    };
302};
303
304StdoutReporter.prototype.summarize = function(numtests, time, exceptions) {
305    print('\n' + numtests + ' tests ran in ' + time / 1000.0 + 
306            ' seconds\n');
307    if (exceptions.length) {
308        for (var i=0; i < exceptions.length; i++) {
309            var testcase = exceptions[i][0];
310            var attr = exceptions[i][1];
311            var exception = exceptions[i][2];
312            var raw = exceptions[i][3];
313            print(testcase + '.' + attr + ', exception: ' + exception);
314            if (this.verbose) {
315                this._printStackTrace(raw);
316            };
317        };
318        print('NOT OK!');
319    } else {
320        print('OK!');
321    };
322};
323
324StdoutReporter.prototype._printStackTrace = function(exc) {
325    if (!exc.stack) {
326        print('no stacktrace available');
327        return;
328    };
329    var lines = exc.stack.toString().split('\n');
330    var toprint = [];
331    for (var i=0; i < lines.length; i++) {
332        var line = lines[i];
333        if (line.indexOf('ecmaunit.js') > -1) {
334            // remove useless bit of traceback
335            break;
336        };
337        if (line.charAt(0) == '(') {
338            line = 'function' + line;
339        };
340        var chunks = line.split('@');
341        toprint.push(chunks);
342    };
343    toprint.reverse();
344    for (var i=0; i < toprint.length; i++) {
345        print('  ' + toprint[i][1]);
346        print('    ' + toprint[i][0]);
347    };
348    print();
349};
350
351function HTMLReporter(outputelement, verbose) {
352    if (outputelement) {
353        this.outputelement = outputelement;
354        this.document = outputelement.ownerDocument;
355        this.verbose = verbose;
356    };
357};
358
359HTMLReporter.prototype.debug = function(text) {
360    var msg = this.document.createTextNode(text);
361    var div = this.document.createElement('div');
362    div.appendChild(msg);
363    this.outputelement.appendChild(div);
364};
365
366HTMLReporter.prototype.reportSuccess = function(testcase, attr) {
367    /* report a test success */
368    // a single dot looks rather small
369    var dot = this.document.createTextNode('+');
370    this.outputelement.appendChild(dot);
371};
372
373HTMLReporter.prototype.reportError = function(testcase, attr, exception, raw) {
374    /* report a test failure */
375    var f = this.document.createTextNode('F');
376    this.outputelement.appendChild(f);
377};
378
379HTMLReporter.prototype.summarize = function(numtests, time, exceptions) {
380    /* write the result output to the html node */
381    var p = this.document.createElement('p');
382    var text = this.document.createTextNode(numtests + ' tests ran in ' + 
383                                            time / 1000.0 + ' seconds');
384    p.appendChild(text);
385    this.outputelement.appendChild(p);
386    if (exceptions.length) {
387        for (var i=0; i < exceptions.length; i++) {
388            var testcase = exceptions[i][0];
389            var attr = exceptions[i][1];
390            var exception = exceptions[i][2].toString();
391            var raw = exceptions[i][3];
392            var div = this.document.createElement('div');
393            var lines = exception.toString().split('\n');
394            var text = this.document.createTextNode(
395                testcase + '.' + attr + ', exception ');
396            div.appendChild(text);
397            // add some formatting for Opera: this browser displays nice
398            // tracebacks...
399            for (var j=0; j < lines.length; j++) {
400                var text = lines[j];
401                if (j > 0) {
402                    text = '\xa0\xa0\xa0\xa0' + text;
403                };
404                div.appendChild(this.document.createTextNode(text));
405                div.appendChild(this.document.createElement('br'));
406            };
407            div.style.color = 'red';
408            this.outputelement.appendChild(div);
409            if (this.verbose) {
410                // display stack trace on Moz
411                this._displayStackTrace(raw);
412            };
413        };
414        var div = this.document.createElement('div');
415        var text = this.document.createTextNode('NOT OK!');
416        div.appendChild(text);
417        div.style.backgroundColor = 'red';
418        div.style.color = 'black';
419        div.style.fontWeight = 'bold';
420        div.style.textAlign = 'center';
421        div.style.marginTop = '1em';
422        this.outputelement.appendChild(div);
423    } else {
424        var div = this.document.createElement('div');
425        var text = this.document.createTextNode('OK!');
426        div.appendChild(text);
427        div.style.backgroundColor = 'lightgreen';
428        div.style.color = 'black';
429        div.style.fontWeight = 'bold';
430        div.style.textAlign = 'center';
431        div.style.marginTop = '1em';
432        this.outputelement.appendChild(div);
433    };
434};
435
436HTMLReporter.prototype._displayStackTrace = function(exc) {
437    /*
438    if (arguments.caller) {
439        // IE
440        var caller = arguments;
441        toprint = [];
442        while (caller) {
443            var callee = caller.callee.toString();
444            callee = callee.replace('\n', '').replace(/\s+/g, ' ');
445            var funcsig = /(.*?)\s*\{/.exec(callee)[1];
446            var args = caller.callee.arguments;
447            var displayargs = [];
448            for (var i=0; i < args.length; i++) {
449                displayargs.push(args[i].toString());
450            };
451            toprint.push((funcsig + ' - (' + displayargs + ')'));
452            caller = caller.caller;
453        };
454        toprint.reverse();
455        var pre = this.document.createElement('pre');
456        for (var i=0; i < toprint.length; i++) {
457            pre.appendChild(document.createTextNode(toprint[i]));
458            pre.appendChild(document.createElement('br'));
459        };
460        this.outputelement.appendChild(pre);
461    };
462    */
463    if (exc.stack) {
464        // Moz (sometimes)
465        var lines = exc.stack.toString().split('\n');
466        var toprint = []; // need to reverse this before outputting
467        for (var i=0; i < lines.length; i++) {
468            var line = lines[i];
469            if (line.indexOf('ecmaunit.js') > -1) {
470                // remove useless bit of traceback
471                break;
472            };
473            if (line[0] == '(') {
474                line = 'function' + line;
475            };
476            line = line.split('@');
477            toprint.push(line);
478        };
479        toprint.reverse();
480        var pre = this.document.createElement('pre');
481        for (var i=0; i < toprint.length; i++) {
482            pre.appendChild(
483                this.document.createTextNode(
484                    '  ' + toprint[i][1] + '\n    ' + toprint[i][0] + '\n'
485                )
486            );
487        };
488        pre.appendChild(document.createTextNode('\n'));
489        this.outputelement.appendChild(pre);
490    };
491};
Note: See TracBrowser for help on using the repository browser.