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