Enhance shouldBe()/shouldNotBe() to accept anonymous function arguments
[WebKit-https.git] / LayoutTests / resources / js-test.js
1 if (self.testRunner) {
2         // svg/dynamic-updates tests set enablePixelTesting=true, as we want to dump text + pixel results
3     if (self.enablePixelTesting)
4         testRunner.dumpAsTextWithPixelResults();
5     else
6         testRunner.dumpAsText();
7
8     // If the test file URL ends in "-private.html", enable private browsing.
9     if (window.location.href.endsWith("-private.html") || self.enablePrivateBrowsing)
10         testRunner.setPrivateBrowsingEnabled(true);
11 }
12
13 var description, debug, didFailSomeTests, successfullyParsed;
14
15 didFailSomeTests = false;
16
17 var expectingError; // set by shouldHaveError()
18 var expectedErrorMessage; // set by onerror when expectingError is true
19 var unexpectedErrorMessage; // set by onerror when expectingError is not true
20
21 (function() {
22
23     function createHTMLElement(tagName)
24     {
25         // FIXME: In an XML document, document.createElement() creates an element with a null namespace URI.
26         // So, we need use document.createElementNS() to explicitly create an element with the specified
27         // tag name in the HTML namespace. We can remove this function and use document.createElement()
28         // directly once we fix <https://bugs.webkit.org/show_bug.cgi?id=131074>.
29         if (document.createElementNS)
30             return document.createElementNS("http://www.w3.org/1999/xhtml", tagName);
31         return document.createElement(tagName);
32     }
33
34     function getOrCreate(id, tagName)
35     {
36         var element = document.getElementById(id);
37         if (element)
38             return element;
39
40         element = createHTMLElement(tagName);
41         element.id = id;
42         var refNode;
43         var parent = document.body || document.documentElement;
44         if (id == "description")
45             refNode = getOrCreate("console", "div");
46         else
47             refNode = parent.firstChild;
48
49         parent.insertBefore(element, refNode);
50         return element;
51     }
52
53     description = function description(msg, quiet)
54     {
55         // For MSIE 6 compatibility
56         var span = createHTMLElement("span");
57         if (quiet)
58             span.innerHTML = '<p>' + msg + '</p><p>On success, you will see no "<span class="fail">FAIL</span>" messages, followed by "<span class="pass">TEST COMPLETE</span>".</p>';
59         else
60             span.innerHTML = '<p>' + msg + '</p><p>On success, you will see a series of "<span class="pass">PASS</span>" messages, followed by "<span class="pass">TEST COMPLETE</span>".</p>';
61
62         var description = getOrCreate("description", "p");
63         if (description.firstChild)
64             description.replaceChild(span, description.firstChild);
65         else
66             description.appendChild(span);
67     };
68
69     debug = function debug(msg)
70     {
71         var span = createHTMLElement("span");
72         getOrCreate("console", "div").appendChild(span); // insert it first so XHTML knows the namespace
73         span.innerHTML = msg + '<br />';
74     };
75
76     var css =
77         ".pass {" +
78             "font-weight: bold;" +
79             "color: green;" +
80         "}" +
81         ".fail {" +
82             "font-weight: bold;" +
83             "color: red;" +
84         "}" +
85         "#console {" +
86             "white-space: pre-wrap;" +
87             "font-family: monospace;" +
88         "}";
89
90     function insertStyleSheet()
91     {
92         var styleElement = createHTMLElement("style");
93         styleElement.textContent = css;
94         (document.head || document.documentElement).appendChild(styleElement);
95     }
96
97     function handleTestFinished()
98     {
99         // FIXME: Get rid of this boolean.
100         wasPostTestScriptParsed = true;
101         if (window.jsTestIsAsync) {
102             if (window.testRunner)
103                 testRunner.waitUntilDone();
104             if (window.wasFinishJSTestCalled)
105                 finishJSTest();
106         } else
107             finishJSTest();
108     }
109
110     if (!isWorker()) {
111         window.addEventListener('DOMContentLoaded', function() {
112             // Some tests set jsTestIsAsync in load event handler. Adding the listener late
113             // makes handleTestFinished() run after the test handles load events.
114             window.addEventListener("load", handleTestFinished, false);
115         }, false);
116         insertStyleSheet();
117     }
118
119     if (!self.isOnErrorTest) {
120         self.onerror = function(message)
121         {
122             if (self.expectingError) {
123                 self.expectedErrorMessage = message;
124                 self.expectingError = false;
125                 return;
126             }
127             self.unexpectedErrorMessage = message;
128             if (self.jsTestIsAsync) {
129                 self.testFailed("Unexpected error: " + message);
130                 finishJSTest();
131             }
132         };
133     }
134 })();
135
136 function isWorker()
137 {
138     // It's conceivable that someone would stub out 'document' in a worker so
139     // also check for childNodes, an arbitrary DOM-related object that is
140     // meaningless in a WorkerContext.
141     return (typeof document === 'undefined' || typeof document.childNodes === 'undefined') && !!self.importScripts;
142 }
143
144 function descriptionQuiet(msg) { description(msg, true); }
145
146 function escapeHTML(text)
147 {
148     return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/\0/g, "\\0");
149 }
150
151 function testPassed(msg)
152 {
153     debug('<span><span class="pass">PASS</span> ' + escapeHTML(msg) + '</span>');
154 }
155
156 function testFailed(msg)
157 {
158     didFailSomeTests = true;
159     debug('<span><span class="fail">FAIL</span> ' + escapeHTML(msg) + '</span>');
160 }
161
162 function areArraysEqual(a, b)
163 {
164     try {
165         if (a.length !== b.length)
166             return false;
167         for (var i = 0; i < a.length; i++)
168             if (a[i] !== b[i])
169                 return false;
170     } catch (ex) {
171         return false;
172     }
173     return true;
174 }
175
176 function isMinusZero(n)
177 {
178     // the only way to tell 0 from -0 in JS is the fact that 1/-0 is
179     // -Infinity instead of Infinity
180     return n === 0 && 1/n < 0;
181 }
182
183 function isNewSVGTearOffType(v)
184 {
185     return ['[object SVGLength]', '[object SVGLengthList]', '[object SVGPoint]', '[object SVGPointList]', '[object SVGNumber]'].indexOf(""+v) != -1;
186 }
187
188 function isResultCorrect(actual, expected)
189 {
190     if (expected === 0)
191         return actual === expected && (1/actual) === (1/expected);
192     if (actual === expected)
193         return true;
194     // http://crbug.com/308818 : The new implementation of SVGListProperties do not necessary return the same wrapper object, so === operator would not work. We compare for their string representation instead.
195     if (isNewSVGTearOffType(expected) && typeof(expected) == typeof(actual) && actual.valueAsString == expected.valueAsString)
196         return true;
197     if (typeof(expected) == "number" && isNaN(expected))
198         return typeof(actual) == "number" && isNaN(actual);
199     if (expected && (Object.prototype.toString.call(expected) == Object.prototype.toString.call([])))
200         return areArraysEqual(actual, expected);
201     return false;
202 }
203
204 function stringify(v)
205 {
206     if (isNewSVGTearOffType(v))
207         return v.valueAsString;
208     if (v === 0 && 1/v < 0)
209         return "-0";
210     else return "" + v;
211 }
212
213 function evalAndLog(_a, _quiet)
214 {
215   if (typeof _a != "string")
216     debug("WARN: tryAndLog() expects a string argument");
217
218   // Log first in case things go horribly wrong or this causes a sync event.
219   if (!_quiet)
220     debug(_a);
221
222   var _av;
223   try {
224      _av = eval(_a);
225   } catch (e) {
226     testFailed(_a + " threw exception " + e);
227   }
228   return _av;
229 }
230
231 function shouldBe(_a, _b, quiet)
232 {
233   if ((typeof _a != "function" && typeof _a != "string") || (typeof _b != "function" && typeof _b != "string"))
234     debug("WARN: shouldBe() expects function or string arguments");
235   var _exception;
236   var _av;
237   try {
238     _av = (typeof _a == "function" ? _a() : eval(_a));
239   } catch (e) {
240     _exception = e;
241   }
242   var _bv = (typeof _b == "function" ? _b() : eval(_b));
243
244   if (_exception)
245     testFailed(_a + " should be " + _bv + ". Threw exception " + _exception);
246   else if (isResultCorrect(_av, _bv)) {
247     if (!quiet) {
248       testPassed(_a + " is " + (typeof _b == "function" ? _bv : _b));
249     }
250   } else if (typeof(_av) == typeof(_bv))
251     testFailed(_a + " should be " + _bv + ". Was " + stringify(_av) + ".");
252   else
253     testFailed(_a + " should be " + _bv + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ").");
254 }
255
256 // Execute condition every 5 milliseconds until it succeeds.
257 function _waitForCondition(condition, completionHandler)
258 {
259   if (condition())
260     completionHandler();
261   else
262     setTimeout(_waitForCondition, 5, condition, completionHandler);
263 }
264
265 function shouldBecomeEqual(_a, _b, completionHandler)
266 {
267   if (typeof _a != "string" || typeof _b != "string")
268     debug("WARN: shouldBecomeEqual() expects string arguments");
269
270   function condition() {
271     var exception;
272     var _av;
273     try {
274       _av = eval(_a);
275     } catch (e) {
276       exception = e;
277     }
278     var _bv = eval(_b);
279     if (exception)
280       testFailed(_a + " should become " + _bv + ". Threw exception " + exception);
281     if (isResultCorrect(_av, _bv)) {
282       testPassed(_a + " became " + _b);
283       return true;
284     }
285     return false;
286   }
287   setTimeout(_waitForCondition, 0, condition, completionHandler);
288 }
289
290 function shouldBecomeEqualToString(value, reference, completionHandler)
291 {
292   if (typeof value !== "string" || typeof reference !== "string")
293     debug("WARN: shouldBecomeEqualToString() expects string arguments");
294   var unevaledString = JSON.stringify(reference);
295   shouldBecomeEqual(value, unevaledString, completionHandler);
296 }
297
298 function shouldBeType(_a, _type) {
299   var _exception;
300   var _av;
301   try {
302     _av = eval(_a);
303   } catch (e) {
304     _exception = e;
305   }
306
307   var _typev = eval(_type);
308   if (_av instanceof _typev) {
309     testPassed(_a + " is an instance of " + _type);
310   } else {
311     testFailed(_a + " is not an instance of " + _type);
312   }
313 }
314
315 // Variant of shouldBe()--confirms that result of eval(_to_eval) is within
316 // numeric _tolerance of numeric _target.
317 function shouldBeCloseTo(_to_eval, _target, _tolerance, _quiet)
318 {
319   if (typeof _to_eval != "string") {
320     testFailed("shouldBeCloseTo() requires string argument _to_eval. was type " + typeof _to_eval);
321     return;
322   }
323   if (typeof _target != "number") {
324     testFailed("shouldBeCloseTo() requires numeric argument _target. was type " + typeof _target);
325     return;
326   }
327   if (typeof _tolerance != "number") {
328     testFailed("shouldBeCloseTo() requires numeric argument _tolerance. was type " + typeof _tolerance);
329     return;
330   }
331
332   var _result;
333   try {
334      _result = eval(_to_eval);
335   } catch (e) {
336     testFailed(_to_eval + " should be within " + _tolerance + " of "
337                + _target + ". Threw exception " + e);
338     return;
339   }
340
341   if (typeof(_result) != typeof(_target)) {
342     testFailed(_to_eval + " should be of type " + typeof _target
343                + " but was of type " + typeof _result);
344   } else if (Math.abs(_result - _target) <= _tolerance) {
345     if (!_quiet) {
346         testPassed(_to_eval + " is within " + _tolerance + " of " + _target);
347     }
348   } else {
349     testFailed(_to_eval + " should be within " + _tolerance + " of " + _target
350                + ". Was " + _result + ".");
351   }
352 }
353
354 function shouldNotBe(_a, _b, _quiet)
355 {
356   if ((typeof _a != "function" && typeof _a != "string") || (typeof _b != "function" && typeof _b != "string"))
357     debug("WARN: shouldNotBe() expects function or string arguments");
358   var _exception;
359   var _av;
360   try {
361     _av = (typeof _a == "function" ? _a() : eval(_a));
362   } catch (e) {
363     _exception = e;
364   }
365   var _bv = (typeof _b == "function" ? _b() : eval(_b));
366
367   if (_exception)
368     testFailed(_a + " should not be " + _bv + ". Threw exception " + _exception);
369   else if (!isResultCorrect(_av, _bv)) {
370     if (!_quiet) {
371       testPassed(_a + " is not " + (typeof _b == "function" ? _bv : _b));
372     }
373   } else
374     testFailed(_a + " should not be " + _bv + ".");
375 }
376
377 function shouldBecomeDifferent(_a, _b, completionHandler)
378 {
379   if (typeof _a != "string" || typeof _b != "string")
380     debug("WARN: shouldBecomeDifferent() expects string arguments");
381
382   function condition() {
383     var exception;
384     var _av;
385     try {
386       _av = eval(_a);
387     } catch (e) {
388       exception = e;
389     }
390     var _bv = eval(_b);
391     if (exception)
392       testFailed(_a + " should became not equal to " + _bv + ". Threw exception " + exception);
393     if (!isResultCorrect(_av, _bv)) {
394       testPassed(_a + " became different from " + _b);
395       return true;
396     }
397     return false;
398   }
399   setTimeout(_waitForCondition, 0, condition, completionHandler);
400 }
401
402 function shouldBeTrue(a, quiet) { shouldBe(a, "true", quiet); }
403 function shouldBeTrueQuiet(a) { shouldBe(a, "true", true); }
404 function shouldBeFalse(a, quiet) { shouldBe(a, "false", quiet); }
405 function shouldBeNaN(a, quiet) { shouldBe(a, "NaN", quiet); }
406 function shouldBeNull(a, quiet) { shouldBe(a, "null", quiet); }
407 function shouldBeZero(a, quiet) { shouldBe(a, "0", quiet); }
408
409 function shouldBeEqualToString(a, b)
410 {
411   if (typeof a !== "string" || typeof b !== "string")
412     debug("WARN: shouldBeEqualToString() expects string arguments");
413   var unevaledString = JSON.stringify(b);
414   shouldBe(a, unevaledString);
415 }
416
417 function shouldBeEqualToNumber(a, b)
418 {
419   if (typeof a !== "string" || typeof b !== "number")
420     debug("WARN: shouldBeEqualToNumber() expects a string and a number arguments");
421   var unevaledString = JSON.stringify(b);
422   shouldBe(a, unevaledString);
423 }
424
425 function shouldBeEmptyString(a) { shouldBeEqualToString(a, ""); }
426
427 function shouldEvaluateTo(actual, expected) {
428   // A general-purpose comparator.  'actual' should be a string to be
429   // evaluated, as for shouldBe(). 'expected' may be any type and will be
430   // used without being eval'ed.
431   if (expected == null) {
432     // Do this before the object test, since null is of type 'object'.
433     shouldBeNull(actual);
434   } else if (typeof expected == "undefined") {
435     shouldBeUndefined(actual);
436   } else if (typeof expected == "function") {
437     // All this fuss is to avoid the string-arg warning from shouldBe().
438     try {
439       var actualValue = eval(actual);
440     } catch (e) {
441       testFailed("Evaluating " + actual + ": Threw exception " + e);
442       return;
443     }
444     shouldBe("'" + actualValue.toString().replace(/\n/g, "") + "'",
445              "'" + expected.toString().replace(/\n/g, "") + "'");
446   } else if (typeof expected == "object") {
447     shouldBeTrue(actual + " == '" + expected + "'");
448   } else if (typeof expected == "string") {
449     shouldBe(actual, expected);
450   } else if (typeof expected == "boolean") {
451     shouldBe("typeof " + actual, "'boolean'");
452     if (expected)
453       shouldBeTrue(actual);
454     else
455       shouldBeFalse(actual);
456   } else if (typeof expected == "number") {
457     shouldBe(actual, stringify(expected));
458   } else {
459     debug(expected + " is unknown type " + typeof expected);
460     shouldBeTrue(actual, "'"  +expected.toString() + "'");
461   }
462 }
463
464 function shouldBeNonZero(_a)
465 {
466   var _exception;
467   var _av;
468   try {
469      _av = eval(_a);
470   } catch (e) {
471      _exception = e;
472   }
473
474   if (_exception)
475     testFailed(_a + " should be non-zero. Threw exception " + _exception);
476   else if (_av != 0)
477     testPassed(_a + " is non-zero.");
478   else
479     testFailed(_a + " should be non-zero. Was " + _av);
480 }
481
482 function shouldBeNonNull(_a)
483 {
484   var _exception;
485   var _av;
486   try {
487      _av = eval(_a);
488   } catch (e) {
489      _exception = e;
490   }
491
492   if (_exception)
493     testFailed(_a + " should be non-null. Threw exception " + _exception);
494   else if (_av != null)
495     testPassed(_a + " is non-null.");
496   else
497     testFailed(_a + " should be non-null. Was " + _av);
498 }
499
500 function shouldBeUndefined(_a)
501 {
502   var _exception;
503   var _av;
504   try {
505      _av = eval(_a);
506   } catch (e) {
507       _exception = e;
508   }
509
510   if (_exception)
511     testFailed(_a + " should be undefined. Threw exception " + _exception);
512   else if (typeof _av == "undefined")
513     testPassed(_a + " is undefined.");
514   else
515     testFailed(_a + " should be undefined. Was " + _av);
516 }
517
518 function shouldBeDefined(_a)
519 {
520   var _exception;
521   var _av;
522   try {
523      _av = eval(_a);
524   } catch (e) {
525      _exception = e;
526   }
527
528   if (_exception)
529     testFailed(_a + " should be defined. Threw exception " + _exception);
530   else if (_av !== undefined)
531     testPassed(_a + " is defined.");
532   else
533     testFailed(_a + " should be defined. Was " + _av);
534 }
535
536 function shouldBeGreaterThanOrEqual(_a, _b) {
537     if (typeof _a != "string" || typeof _b != "string")
538         debug("WARN: shouldBeGreaterThanOrEqual expects string arguments");
539
540     var _exception;
541     var _av;
542     try {
543         _av = eval(_a);
544     } catch (e) {
545         _exception = e;
546     }
547     var _bv = eval(_b);
548
549     if (_exception)
550         testFailed(_a + " should be >= " + _b + ". Threw exception " + _exception);
551     else if (typeof _av == "undefined" || _av < _bv)
552         testFailed(_a + " should be >= " + _b + ". Was " + _av + " (of type " + typeof _av + ").");
553     else
554         testPassed(_a + " is >= " + _b);
555 }
556
557 function expectTrue(v, msg) {
558   if (v) {
559     testPassed(msg);
560   } else {
561     testFailed(msg);
562   }
563 }
564
565 function shouldNotThrow(_a, _message) {
566     try {
567         typeof _a == "function" ?  _a() : eval(_a);
568         testPassed((_message ? _message : _a) + " did not throw exception.");
569     } catch (e) {
570         testFailed((_message ? _message : _a) + " should not throw exception. Threw exception " + e + ".");
571     }
572 }
573
574 function shouldThrow(_a, _e, _message)
575 {
576     var _exception;
577     var _av;
578     try {
579         _av = typeof _a == "function" ? _a() : eval(_a);
580     } catch (e) {
581         _exception = e;
582     }
583
584     var _ev;
585     if (_e)
586         _ev = eval(_e);
587
588     if (_exception) {
589         if (typeof _e == "undefined" || _exception == _ev)
590             testPassed((_message ? _message : _a) + " threw exception " + _exception + ".");
591         else
592             testFailed((_message ? _message : _a) + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + _exception + ".");
593     } else if (typeof _av == "undefined")
594         testFailed((_message ? _message : _a) + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined.");
595     else
596         testFailed((_message ? _message : _a) + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + ".");
597 }
598
599 function shouldBeNow(a, delta)
600 {
601     // Right now, V8 and Chromium / Blink use two different clock
602     // implementations. On Windows, the implementations are non-trivial and can
603     // be slightly out of sync. The delta is intended to compensate for that.
604     //
605     // FIXME: reconsider this when the V8 and Blink clocks get unified, see http://crbug.com/324110
606     if (delta === undefined)
607         delta = 1000;
608
609     for (var i = 0; i < 1000; ++i) {
610         var startDate = Date.now();
611         var av = eval(a);
612         var date = av.valueOf();
613         var endDate = Date.now();
614
615         // On some occasions such as NTP updates, the current time can go
616         // backwards. This should only happen rarely, so we can get away with
617         // retrying the test a few times if we detect the time going backwards.
618         if (startDate > endDate)
619             continue;
620
621         if (typeof date !== "number") {
622             testFailed(a + " is not a number or a Date. Got " + av);
623             return;
624         }
625         if (date < startDate - delta) {
626             testFailed(a + " is not the curent time. Got " + av + " which is " + (startDate - date) / 1000 + " seconds in the past.");
627             return;
628         }
629         if (date > endDate + delta) {
630             testFailed(a + " is not the current time. Got " + av + " which is " + (date - endDate) / 1000 + " seconds in the future.");
631             return;
632         }
633
634         testPassed(a + " is equivalent to Date.now().");
635         return;
636     }
637     testFailed(a + " cannot be tested against the current time. The clock is going backwards too often.");
638 }
639
640 function expectError()
641 {
642     if (expectingError) {
643         testFailed("shouldHaveError() called twice before an error occurred!");
644     }
645     expectingError = true;
646 }
647
648 function shouldHaveHadError(message)
649 {
650     if (expectingError) {
651         testFailed("No error thrown between expectError() and shouldHaveHadError()");
652         return;
653     }
654
655     if (expectedErrorMessage) {
656         if (!message)
657             testPassed("Got expected error");
658         else if (expectedErrorMessage.indexOf(message) !== -1)
659             testPassed("Got expected error: '" + message + "'");
660         else
661             testFailed("Unexpected error '" + message + "'");
662         expectedErrorMessage = undefined;
663         return;
664     }
665
666     testFailed("expectError() not called before shouldHaveHadError()");
667 }
668
669 function gc() {
670     if (typeof GCController !== "undefined")
671         GCController.collect();
672     else {
673         var gcRec = function (n) {
674             if (n < 1)
675                 return {};
676             var temp = {i: "ab" + i + (i / 100000)};
677             temp += "foo";
678             gcRec(n-1);
679         };
680         for (var i = 0; i < 1000; i++)
681             gcRec(10);
682     }
683 }
684
685 function minorGC() {
686     if (typeof GCController !== "undefined")
687         GCController.minorCollect();
688     else
689         testFailed("Minor GC is available only when you enable the --expose-gc option in V8.");
690 }
691
692 function isSuccessfullyParsed()
693 {
694     // FIXME: Remove this and only report unexpected syntax errors.
695     successfullyParsed = !unexpectedErrorMessage;
696     shouldBeTrue("successfullyParsed");
697     if (didFailSomeTests)
698         debug("Some tests failed.");
699     debug('<br /><span class="pass">TEST COMPLETE</span>');
700 }
701
702 // It's possible for an async test to call finishJSTest() before js-test-post.js
703 // has been parsed.
704 function finishJSTest()
705 {
706     wasFinishJSTestCalled = true;
707     if (!self.wasPostTestScriptParsed)
708         return;
709     isSuccessfullyParsed();
710     if (self.jsTestIsAsync && self.testRunner)
711         testRunner.notifyDone();
712 }
713
714 function areObjectsEqual(a, b) {
715         for (var property in a) {
716                 if (!b.hasOwnProperty(property))
717                         return false;
718
719                 switch (typeof (a[property])) {
720                 case 'function':
721                         if (typeof b[property] == 'undefined' || a[property].toString() != b[property].toString())
722                                 return false;
723                         break;
724                 case 'object':
725                         if (!areObjectsEqual(a, b))
726                                 return false;
727                         break;
728                 default:
729                         if (a[property] != b[property])
730                                 return false;
731                 }
732         }
733  
734         for (var property in b) {
735                 if (!a.hasOwnProperty(property))
736                         return false;
737         }
738         
739         return true;
740 };
741
742 function startWorker(testScriptURL)
743 {
744     self.jsTestIsAsync = true;
745     debug('Starting worker: ' + testScriptURL);
746     var worker = new Worker(testScriptURL);
747     worker.onmessage = function(event)
748     {
749         var workerPrefix = "[Worker] ";
750         if (event.data.length < 5 || event.data.charAt(4) != ':') {
751           debug(workerPrefix + event.data);
752           return;
753         }
754         var code = event.data.substring(0, 4);
755         var payload = workerPrefix + event.data.substring(5);
756         if (code == "PASS")
757             testPassed(payload);
758         else if (code == "FAIL")
759             testFailed(payload);
760         else if (code == "DESC")
761             description(payload);
762         else if (code == "DONE")
763             finishJSTest();
764         else
765             debug(workerPrefix + event.data);
766     };
767
768     worker.onerror = function(event)
769     {
770         debug('Got error from worker: ' + event.message);
771         finishJSTest();
772     };
773
774     return worker;
775 }
776
777 if (isWorker()) {
778     var workerPort = self;
779     description = function(msg, quiet) {
780         workerPort.postMessage('DESC:' + msg);
781     };
782     testFailed = function(msg) {
783         workerPort.postMessage('FAIL:' + msg);
784     };
785     testPassed = function(msg) {
786         workerPort.postMessage('PASS:' + msg);
787     };
788     finishJSTest = function() {
789         workerPort.postMessage('DONE:');
790     };
791     debug = function(msg) {
792         workerPort.postMessage(msg);
793     };
794 }