Quick change to the license.
[jquery.git] / test / lib / Test / Harness.js
1 // # $Id: Kinetic.pm 1493 2005-04-07 19:20:18Z theory $
2
3 // Set up namespace.
4 if (typeof self != 'undefined') {
5     //Browser
6     if (typeof Test == 'undefined') Test = {PLATFORM: 'browser'};
7     else Test.PLATFORM = 'browser';
8 } else if (typeof _player != 'undefined'){
9     //Director
10     if (typeof _global.Test != "object") _global.Test = {PLATFORM: 'director'};
11     else _global.Test.PLATFORM = 'director';
12 } else {
13     throw new Error("Test.Harness does not support your platform");
14 }
15
16 Test.Harness = function () {};
17 Test.Harness.VERSION = '0.11';
18 Test.Harness.Done = 0;
19
20 // Stoopid IE.
21 Test.Harness.LF = typeof document != "undefined"
22                   && typeof document.all != "undefined"
23   ? "\r"
24   : "\n";
25
26 Test.Harness.prototype.isDone = Test.Harness.isDone;
27
28 /*
29
30     bonus           Number of individual todo tests unexpectedly passed
31     ran             Number of individual tests ran
32     ok              Number of individual tests passed
33     subSkipped      Number of individual tests skipped
34     todo            Number of individual todo tests
35
36     files           Number of test files ran
37     good            Number of test files passed
38     bad             Number of test files failed
39     tests           Number of test files originally given
40     skipped         Number of test files skipped
41
42 */
43
44 Test.Harness.prototype.bonus      = 0;
45 Test.Harness.prototype.ran        = 0;
46 Test.Harness.prototype.ok         = 0;
47 Test.Harness.prototype.subSkipped = 0;
48 Test.Harness.prototype.todo       = 0;
49 Test.Harness.prototype.files      = 0;
50 Test.Harness.prototype.good       = 0;
51 Test.Harness.prototype.bad        = 0;
52 Test.Harness.prototype.tests      = 0;
53 Test.Harness.prototype.skipped    = 0;
54 Test.Harness.prototype.failures   = [];
55
56 Test.Harness.runTests = function () {
57     // XXX Can't handle inheritance, right? Or can we?
58     var harness = new Test.Harness();
59     harness.runTests.apply(harness, arguments);
60 };
61
62 Test.Harness.prototype.outFileNames = function (files) {
63     var len = 0;
64     for (var i = 0; i < files.length; i++) {
65         if (files[i].length > len) len = files[i].length;
66     }
67     len += 3;
68     var ret = [];
69     for (var i = 0; i < files.length; i++) {
70         var outName = files[i];
71         var add = len - files[i].length;
72         // Where is Perl's x operator when I need it??
73         for (var j = 0; j < add; j++) {
74             outName += '.';
75         }
76         ret.push(outName);
77     }
78     return ret;
79 };
80
81 Test.Harness.prototype.outputResults = function (test, file, fn, attrs) {
82     this.tests++;
83     this.ran += test.TestResults.length;
84     var fails = [];
85     var track = {
86       fn:       file,
87       total:    test.expectedTests,
88       ok:       0,
89       failList: []
90     };
91
92     if (test.TestResults.length) {
93         this.files++;
94         var pass = true;
95         for (var i = 0; i < test.TestResults.length; i++) {
96             var ok = "ok";
97             if (test.TestResults[i].ok) {
98                 this.ok++;
99                 track.ok++
100                 if (test.TestResults[i].type == 'todo') {
101                     // Handle unexpected pass.
102                     if (test.TestResults[i].actualOK) this.bonus++;
103                     this.todo ++;
104                 } else if (test.TestResults[i].type == 'skip') this.subSkipped++;
105             } else {
106                 if (test.TestResults[i].type == 'todo') {
107                     // Expected failure.
108                     this.todo++;
109                 } else {
110                     pass = false;
111                     track.failList.push(i + 1);
112                 }
113                 ok = "not ok"; // XXX Need to handle TODO and TODO Skipped.
114             }
115             
116             if (!pass || attrs.verbose) fn(test.TestResults[i].output);
117         }
118         
119         if (pass) {
120             this.good++;
121             fn("ok" + Test.Harness.LF);
122         } else {
123             this.bad++;
124             var err = "NOK # Failed ";
125             if (track.failList.length == 1) {
126                 err += "test " + track.failList[0];
127             } else {
128                 err += "tests " + this._failList(track.failList);
129             }
130             fn(err + " in " + file + Test.Harness.LF);
131         }
132     } else if (test.SkipAll){
133         // All tests skipped.
134         this.skipped++;
135         this.good++;
136         fn("1..0 # Skip 1" + Test.Harness.LF);
137     } else {
138         // Wha happened? Tests ran, but no results!
139         this.files++;
140         this.bad++;
141         fn("FAILED before any test output arrived" + Test.Harness.LF);
142     }
143     if (track.failList.length) this.failures.push(track);
144 };
145
146 Test.Harness.prototype._allOK = function () {
147     return this.bad == 0 && (this.ran || this.skipped) ? true : false;
148 };
149
150 Test.Harness.prototype.outputSummary = function (fn, time) {
151     var bonusmsg = this._bonusmsg();
152     var pct;
153     if (this._allOK()) {
154         fn("All tests successful" + bonusmsg + '.' + Test.Harness.LF);
155     } else if (!this.tests) {
156         fn("FAILED—no tests were run for some reason." + Test.Harness.LF);
157     } else if (!this.ran) {
158         var blurb = this.tests == 1 ? "file" : "files";
159         fn("FAILED—" + this.tests + " test " + blurb + " could be run, "
160            + "alas—no output ever seen." + Test.Harness.LF);
161     } else {
162         pct = this.good / this.tests * 100;
163         var pctOK = 100 * this.ok / this.ran;
164         var subpct = (this.ran - this.ok) + "/" + this.ran
165           + " subtests failed, " + pctOK.toPrecision(4) + "% okay.";
166
167         if (this.bad) {
168             bonusmsg = bonusmsg.replace(/^,?\s*/, '');
169             if (bonusmsg) fn(bonusmsg + '.' + Test.Harness.LF);
170             fn("Failed " + this.bad + "/" + this.tests + " test scripts, "
171                + pct.toPrecision(4) + "% okay. " + subpct + Test.Harness.LF);
172         }
173         this.formatFailures(fn);
174     }
175
176     fn("Files=" + this.tests + ", Tests=" + this.ran + ", " + (time / 1000)
177        + " seconds" + Test.Harness.LF);
178 };
179
180 Test.Harness.prototype.formatFailures = function () {
181     var table = '';
182     var failedStr = "Failed Test";
183     var middleStr = " Total Fail  Failed  ";
184     var listStr = "List of Failed";
185     var cols = 80;
186
187     // Figure out our longest name string for formatting purposes.
188     var maxNamelen = failedStr.length;
189     for (var i = 0; i < this.failures.length; i++) {
190         var len = this.failures[i].length;
191         if (len > maxNamelen) maxNamelen = len;
192     }
193
194     var listLen = cols - middleStr.length - maxNamelen.length;
195     if (listLen < listStr.length) {
196         listLen = listStr.length;
197         maxNamelen = cols - middleStr.length - listLen;
198         if (maxNamelen < failedStr.length) {
199             maxNamelen = failedStr.length;
200             cols = maxNamelen + middleStr.length + listLen;
201         }
202     }
203
204     var out = failedStr;
205     if (out.length < maxNamelen) {
206         for (var j = out.length; j < maxNameLength; j++) {
207             out += ' ';
208         }
209     }
210     out += '  ' + middleStr;
211     // XXX Need to finish implementing the text-only version of the failures
212     // table.
213 };
214
215 Test.Harness.prototype._bonusmsg = function () {
216     var bonusmsg = '';
217     if (this.bonus) {
218         bonusmsg = (" (" + this.bonus + " subtest" + (this.bonus > 1 ? 's' : '')
219           + " UNEXPECTEDLY SUCCEEDED)");
220     }
221
222     if (this.skipped) {
223         bonusmsg += ", " + this.skipped + " test"
224           + (this.skipped != 1 ? 's' : '');
225         if (this.subSkipped) {
226             bonusmsg += " and " + this.subSkipped + " subtest"
227               + (this.subSkipped != 1 ? 's' : '');
228         }
229         bonusmsg += ' skipped';
230     } else if (this.subSkipped) {
231         bonusmsg += ", " + this.subSkipped + " subtest"
232           + (this.subSkipped != 1 ? 's' : '') + " skipped";
233     }
234
235     return bonusmsg;
236 }
237
238 Test.Harness.prototype._failList = function (fails) {
239     var last = -1;
240     var dash = '';
241     var list = [];
242     for (var i = 0; i < fails.length; i++) {
243         if (dash) {
244             // We're in a series of numbers.
245             if (fails[i] - 1 == last) {
246                 // We're still in it.
247                 last = fails[i];
248             } else {
249                 // End of the line.
250                 list[list.length-1] += dash + last;
251                 last = -1;
252                 list.push(fails[i]);
253                 dash = '';
254             }
255         } else if (fails[i] - 1 == last) {
256             // We're in a new series.
257             last = fails[i];
258             dash = '-';
259         } else {
260             // Not in a sequence.
261             list.push(fails[i]);
262             last = fails[i];
263         }
264     }
265     if (dash) list[list.length-1] += dash + last;
266     return list.join(' ');
267 }