Bug 449371 Firefox/Thunderbird crashes at exit [@ gdk_display_x11_finalize], p=Brian...
[wine-gecko.git] / testing / mochitest / tests / SimpleTest / SimpleTest.js
blobd4ac154af8734a13ca01cf6153ac7e7dd2ce3d8a
1 /**
2 * SimpleTest, a partial Test.Simple/Test.More API compatible test library.
4 * Why?
6 * Test.Simple doesn't work on IE < 6.
7 * TODO:
8 * * Support the Test.Simple API used by MochiKit, to be able to test MochiKit
9 * itself against IE 5.5
11 **/
13 if (typeof(SimpleTest) == "undefined") {
14 var SimpleTest = {};
17 var parentRunner = null;
18 if (typeof(parent) != "undefined" && parent.TestRunner) {
19 parentRunner = parent.TestRunner;
20 } else if (parent && parent.wrappedJSObject &&
21 parent.wrappedJSObject.TestRunner) {
22 parentRunner = parent.wrappedJSObject.TestRunner;
25 // Check to see if the TestRunner is present and has logging
26 if (parentRunner) {
27 SimpleTest._logEnabled = parentRunner.logEnabled;
30 SimpleTest._tests = [];
31 SimpleTest._stopOnLoad = true;
33 /**
34 * Something like assert.
35 **/
36 SimpleTest.ok = function (condition, name, diag) {
37 var test = {'result': !!condition, 'name': name, 'diag': diag || ""};
38 if (SimpleTest._logEnabled)
39 SimpleTest._logResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
40 SimpleTest._tests.push(test);
43 /**
44 * Roughly equivalent to ok(a==b, name)
45 **/
46 SimpleTest.is = function (a, b, name) {
47 var repr = MochiKit.Base.repr;
48 SimpleTest.ok(a == b, name, "got " + repr(a) + ", expected " + repr(b));
51 SimpleTest.isnot = function (a, b, name) {
52 var repr = MochiKit.Base.repr;
53 SimpleTest.ok(a != b, name, "Didn't expect " + repr(a) + ", but got it.");
56 // --------------- Test.Builder/Test.More todo() -----------------
58 SimpleTest.todo = function(condition, name, diag) {
59 var test = {'result': !!condition, 'name': name, 'diag': diag || "", todo: true};
60 if (SimpleTest._logEnabled)
61 SimpleTest._logResult(test, "TEST-UNEXPECTED-PASS", "TEST-KNOWN-FAIL");
62 SimpleTest._tests.push(test);
65 SimpleTest._logResult = function(test, passString, failString) {
66 var msg = test.result ? passString : failString;
67 msg += " | ";
68 if (parentRunner.currentTestURL)
69 msg += parentRunner.currentTestURL;
70 msg += " | " + test.name;
71 var diag = "";
72 if (test.diag)
73 diag = " - " + test.diag;
74 if (test.result) {
75 if (test.todo)
76 parentRunner.logger.error(msg + diag);
77 else
78 parentRunner.logger.log(msg);
79 } else {
80 if (test.todo)
81 parentRunner.logger.log(msg);
82 else
83 parentRunner.logger.error(msg + diag);
87 /**
88 * Copies of is and isnot with the call to ok replaced by a call to todo.
89 **/
91 SimpleTest.todo_is = function (a, b, name) {
92 var repr = MochiKit.Base.repr;
93 SimpleTest.todo(a == b, name, "got " + repr(a) + ", expected " + repr(b));
96 SimpleTest.todo_isnot = function (a, b, name) {
97 var repr = MochiKit.Base.repr;
98 SimpleTest.todo(a != b, name, "Didn't expect " + repr(a) + ", but got it.");
103 * Makes a test report, returns it as a DIV element.
105 SimpleTest.report = function () {
106 var DIV = MochiKit.DOM.DIV;
107 var passed = 0;
108 var failed = 0;
109 var todo = 0;
110 var results = MochiKit.Base.map(
111 function (test) {
112 var cls, msg;
113 if (test.todo && !test.result) {
114 todo++;
115 cls = "test_todo";
116 msg = "todo - " + test.name + " " + test.diag;
117 } else if (test.result &&!test.todo) {
118 passed++;
119 cls = "test_ok";
120 msg = "ok - " + test.name;
121 } else {
122 failed++;
123 cls = "test_not_ok";
124 msg = "not ok - " + test.name + " " + test.diag;
126 return DIV({"class": cls}, msg);
128 SimpleTest._tests
130 var summary_class = ((failed == 0) ? 'all_pass' : 'some_fail');
131 return DIV({'class': 'tests_report'},
132 DIV({'class': 'tests_summary ' + summary_class},
133 DIV({'class': 'tests_passed'}, "Passed: " + passed),
134 DIV({'class': 'tests_failed'}, "Failed: " + failed),
135 DIV({'class': 'tests_todo'}, "Todo: " + todo)),
136 results
141 * Toggle element visibility
143 SimpleTest.toggle = function(el) {
144 if (MochiKit.Style.computedStyle(el, 'display') == 'block') {
145 el.style.display = 'none';
146 } else {
147 el.style.display = 'block';
152 * Toggle visibility for divs with a specific class.
154 SimpleTest.toggleByClass = function (cls, evt) {
155 var elems = getElementsByTagAndClassName('div', cls);
156 MochiKit.Base.map(SimpleTest.toggle, elems);
157 if (evt)
158 evt.preventDefault();
162 * Shows the report in the browser
165 SimpleTest.showReport = function() {
166 var togglePassed = A({'href': '#'}, "Toggle passed tests");
167 var toggleFailed = A({'href': '#'}, "Toggle failed tests");
168 togglePassed.onclick = partial(SimpleTest.toggleByClass, 'test_ok');
169 toggleFailed.onclick = partial(SimpleTest.toggleByClass, 'test_not_ok');
170 var body = document.body; // Handles HTML documents
171 if (!body) {
172 // Do the XML thing
173 body = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml",
174 "body")[0]
176 var firstChild = body.childNodes[0];
177 var addNode;
178 if (firstChild) {
179 addNode = function (el) {
180 body.insertBefore(el, firstChild);
182 } else {
183 addNode = function (el) {
184 body.appendChild(el)
187 addNode(togglePassed);
188 addNode(SPAN(null, " "));
189 addNode(toggleFailed);
190 addNode(SimpleTest.report());
194 * Tells SimpleTest to don't finish the test when the document is loaded,
195 * useful for asynchronous tests.
197 * When SimpleTest.waitForExplicitFinish is called,
198 * explicit SimpleTest.finish() is required.
200 SimpleTest.waitForExplicitFinish = function () {
201 SimpleTest._stopOnLoad = false;
205 * Talks to the TestRunner if being ran on a iframe and the parent has a
206 * TestRunner object.
208 SimpleTest.talkToRunner = function () {
209 if (parentRunner) {
210 parentRunner.testFinished(document);
215 * Finishes the tests. This is automatically called, except when
216 * SimpleTest.waitForExplicitFinish() has been invoked.
218 SimpleTest.finish = function () {
219 SimpleTest.showReport();
220 SimpleTest.talkToRunner();
224 addLoadEvent(function() {
225 if (SimpleTest._stopOnLoad) {
226 SimpleTest.finish();
230 // --------------- Test.Builder/Test.More isDeeply() -----------------
233 SimpleTest.DNE = {dne: 'Does not exist'};
234 SimpleTest.LF = "\r\n";
235 SimpleTest._isRef = function (object) {
236 var type = typeof(object);
237 return type == 'object' || type == 'function';
241 SimpleTest._deepCheck = function (e1, e2, stack, seen) {
242 var ok = false;
243 // Either they're both references or both not.
244 var sameRef = !(!SimpleTest._isRef(e1) ^ !SimpleTest._isRef(e2));
245 if (e1 == null && e2 == null) {
246 ok = true;
247 } else if (e1 != null ^ e2 != null) {
248 ok = false;
249 } else if (e1 == SimpleTest.DNE ^ e2 == SimpleTest.DNE) {
250 ok = false;
251 } else if (sameRef && e1 == e2) {
252 // Handles primitives and any variables that reference the same
253 // object, including functions.
254 ok = true;
255 } else if (SimpleTest.isa(e1, 'Array') && SimpleTest.isa(e2, 'Array')) {
256 ok = SimpleTest._eqArray(e1, e2, stack, seen);
257 } else if (typeof e1 == "object" && typeof e2 == "object") {
258 ok = SimpleTest._eqAssoc(e1, e2, stack, seen);
259 } else {
260 // If we get here, they're not the same (function references must
261 // always simply rererence the same function).
262 stack.push({ vals: [e1, e2] });
263 ok = false;
265 return ok;
268 SimpleTest._eqArray = function (a1, a2, stack, seen) {
269 // Return if they're the same object.
270 if (a1 == a2) return true;
272 // JavaScript objects have no unique identifiers, so we have to store
273 // references to them all in an array, and then compare the references
274 // directly. It's slow, but probably won't be much of an issue in
275 // practice. Start by making a local copy of the array to as to avoid
276 // confusing a reference seen more than once (such as [a, a]) for a
277 // circular reference.
278 for (var j = 0; j < seen.length; j++) {
279 if (seen[j][0] == a1) {
280 return seen[j][1] == a2;
284 // If we get here, we haven't seen a1 before, so store it with reference
285 // to a2.
286 seen.push([ a1, a2 ]);
288 var ok = true;
289 // Only examines enumerable attributes. Only works for numeric arrays!
290 // Associative arrays return 0. So call _eqAssoc() for them, instead.
291 var max = a1.length > a2.length ? a1.length : a2.length;
292 if (max == 0) return SimpleTest._eqAssoc(a1, a2, stack, seen);
293 for (var i = 0; i < max; i++) {
294 var e1 = i > a1.length - 1 ? SimpleTest.DNE : a1[i];
295 var e2 = i > a2.length - 1 ? SimpleTest.DNE : a2[i];
296 stack.push({ type: 'Array', idx: i, vals: [e1, e2] });
297 if (ok = SimpleTest._deepCheck(e1, e2, stack, seen)) {
298 stack.pop();
299 } else {
300 break;
303 return ok;
306 SimpleTest._eqAssoc = function (o1, o2, stack, seen) {
307 // Return if they're the same object.
308 if (o1 == o2) return true;
310 // JavaScript objects have no unique identifiers, so we have to store
311 // references to them all in an array, and then compare the references
312 // directly. It's slow, but probably won't be much of an issue in
313 // practice. Start by making a local copy of the array to as to avoid
314 // confusing a reference seen more than once (such as [a, a]) for a
315 // circular reference.
316 seen = seen.slice(0);
317 for (var j = 0; j < seen.length; j++) {
318 if (seen[j][0] == o1) {
319 return seen[j][1] == o2;
323 // If we get here, we haven't seen o1 before, so store it with reference
324 // to o2.
325 seen.push([ o1, o2 ]);
327 // They should be of the same class.
329 var ok = true;
330 // Only examines enumerable attributes.
331 var o1Size = 0; for (var i in o1) o1Size++;
332 var o2Size = 0; for (var i in o2) o2Size++;
333 var bigger = o1Size > o2Size ? o1 : o2;
334 for (var i in bigger) {
335 var e1 = o1[i] == undefined ? SimpleTest.DNE : o1[i];
336 var e2 = o2[i] == undefined ? SimpleTest.DNE : o2[i];
337 stack.push({ type: 'Object', idx: i, vals: [e1, e2] });
338 if (ok = SimpleTest._deepCheck(e1, e2, stack, seen)) {
339 stack.pop();
340 } else {
341 break;
344 return ok;
347 SimpleTest._formatStack = function (stack) {
348 var variable = '$Foo';
349 for (var i = 0; i < stack.length; i++) {
350 var entry = stack[i];
351 var type = entry['type'];
352 var idx = entry['idx'];
353 if (idx != null) {
354 if (/^\d+$/.test(idx)) {
355 // Numeric array index.
356 variable += '[' + idx + ']';
357 } else {
358 // Associative array index.
359 idx = idx.replace("'", "\\'");
360 variable += "['" + idx + "']";
365 var vals = stack[stack.length-1]['vals'].slice(0, 2);
366 var vars = [
367 variable.replace('$Foo', 'got'),
368 variable.replace('$Foo', 'expected')
371 var out = "Structures begin differing at:" + SimpleTest.LF;
372 for (var i = 0; i < vals.length; i++) {
373 var val = vals[i];
374 if (val == null) {
375 val = 'undefined';
376 } else {
377 val == SimpleTest.DNE ? "Does not exist" : "'" + val + "'";
381 out += vars[0] + ' = ' + vals[0] + SimpleTest.LF;
382 out += vars[1] + ' = ' + vals[1] + SimpleTest.LF;
384 return ' ' + out;
388 SimpleTest.isDeeply = function (it, as, name) {
389 var ok;
390 // ^ is the XOR operator.
391 if (SimpleTest._isRef(it) ^ SimpleTest._isRef(as)) {
392 // One's a reference, one isn't.
393 ok = false;
394 } else if (!SimpleTest._isRef(it) && !SimpleTest._isRef(as)) {
395 // Neither is an object.
396 ok = SimpleTest.is(it, as, name);
397 } else {
398 // We have two objects. Do a deep comparison.
399 var stack = [], seen = [];
400 if ( SimpleTest._deepCheck(it, as, stack, seen)) {
401 ok = SimpleTest.ok(true, name);
402 } else {
403 ok = SimpleTest.ok(false, name, SimpleTest._formatStack(stack));
406 return ok;
409 SimpleTest.typeOf = function (object) {
410 var c = Object.prototype.toString.apply(object);
411 var name = c.substring(8, c.length - 1);
412 if (name != 'Object') return name;
413 // It may be a non-core class. Try to extract the class name from
414 // the constructor function. This may not work in all implementations.
415 if (/function ([^(\s]+)/.test(Function.toString.call(object.constructor))) {
416 return RegExp.$1;
418 // No idea. :-(
419 return name;
422 SimpleTest.isa = function (object, clas) {
423 return SimpleTest.typeOf(object) == clas;
426 // Global symbols:
427 var ok = SimpleTest.ok;
428 var is = SimpleTest.is;
429 var isnot = SimpleTest.isnot;
430 var todo = SimpleTest.todo;
431 var todo_is = SimpleTest.todo_is;
432 var todo_isnot = SimpleTest.todo_isnot;
433 var isDeeply = SimpleTest.isDeeply;
434 var oldOnError = window.onerror;
435 window.onerror = function (ev) {
436 is(0, 1, "Error thrown during test: " + ev);
437 if (oldOnError) {
438 try {
439 oldOnError(ev);
440 } catch (e) {
443 if (SimpleTest._stopOnLoad == false) {
444 // Need to finish() manually here
445 SimpleTest.finish();