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