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