1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
3 var gIsRefImageLoaded = false;
4 const gShouldOutputDebugInfo = false;
6 function pollForSuccess()
8 if (!currentTest.isTestFinished) {
9 if (!currentTest.reusingReferenceImage || (currentTest.reusingReferenceImage
10 && gRefImageLoaded)) {
11 currentTest.checkImage();
14 setTimeout(pollForSuccess, currentTest.pollFreq);
18 function referencePoller()
20 currentTest.takeReferenceSnapshot();
23 function reuseImageCallback()
25 gIsRefImageLoaded = true;
30 if (currentTest.isTestFinished || currentTest.closeFunc) {
34 ok(false, "timing out after " + currentTest.timeout + "ms. "
35 + "Animated image still doesn't look correct, after poll #"
36 + currentTest.pollCounter);
37 currentTest.wereFailures = true;
39 if (currentTest.currentSnapshotDataURI) {
40 currentTest.outputDebugInfo("Snapshot #" + currentTest.pollCounter,
41 "snapNum" + currentTest.pollCounter,
42 currentTest.currentSnapshotDataURI);
45 currentTest.enableDisplay(document.getElementById(currentTest.debugElementId));
47 currentTest.cleanUpAndFinish();
51 * Create a new AnimationTest object.
53 * @param pollFreq The amount of time (in ms) to wait between consecutive
54 * snapshots if the reference image and the test image don't match.
55 * @param timeout The total amount of time (in ms) to wait before declaring the
57 * @param referenceElementId The id attribute of the reference image element, or
58 * the source of the image to change to, once the reference snapshot has
59 * been successfully taken. This latter option could be used if you don't
60 * want the image to become invisible at any time during the test.
61 * @param imageElementId The id attribute of the test image element.
62 * @param debugElementId The id attribute of the div where links should be
63 * appended if the test fails.
64 * @param cleanId The id attribute of the div or element to use as the 'clean'
65 * test. This element is only enabled when we are testing to verify that
66 * the reference image has been loaded. It can be undefined.
67 * @param srcAttr The location of the source of the image, for preloading. This
68 * is usually not required, but it useful for preloading reference
70 * @param xulTest A boolean value indicating whether or not this is a XUL test
71 * (uses hidden=true/false rather than display: none to hide/show
73 * @param closeFunc A function that should be called when this test is finished.
74 * If null, then cleanUpAndFinish() will be called. This can be used to
75 * chain tests together, so they are all finished exactly once.
76 * @returns {AnimationTest}
78 function AnimationTest(pollFreq, timeout, referenceElementId, imageElementId,
79 debugElementId, cleanId, srcAttr, xulTest, closeFunc)
81 // We want to test the cold loading behavior, so clear cache in case an
82 // earlier test got our image in there already.
85 this.wereFailures = false;
86 this.pollFreq = pollFreq;
87 this.timeout = timeout;
88 this.imageElementId = imageElementId;
89 this.referenceElementId = referenceElementId;
91 if (!document.getElementById(referenceElementId)) {
92 // In this case, we're assuming the user passed in a string that
93 // indicates the source of the image they want to change to,
94 // after the reference image has been taken.
95 this.reusingImageAsReference = true;
98 this.srcAttr = srcAttr;
99 this.debugElementId = debugElementId;
100 this.referenceSnapshot = ""; // value will be set in takeReferenceSnapshot()
101 this.pollCounter = 0;
102 this.isTestFinished = false;
103 this.numRefsTaken = 0;
104 this.blankWaitTime = 0;
106 this.cleanId = cleanId ? cleanId : '';
107 this.xulTest = xulTest ? xulTest : '';
108 this.closeFunc = closeFunc ? closeFunc : '';
111 AnimationTest.prototype.preloadImage = function()
114 this.myImage = new Image();
115 this.myImage.onload = function() { currentTest.continueTest(); };
116 this.myImage.src = this.srcAttr;
122 AnimationTest.prototype.outputDebugInfo = function(message, id, dataUri)
124 if (!gShouldOutputDebugInfo) {
127 var debugElement = document.getElementById(this.debugElementId);
128 var newDataUriElement = document.createElement("a");
129 newDataUriElement.setAttribute("id", id);
130 newDataUriElement.setAttribute("href", dataUri);
131 newDataUriElement.appendChild(document.createTextNode(message));
132 debugElement.appendChild(newDataUriElement);
133 var brElement = document.createElement("br");
134 debugElement.appendChild(brElement);
135 todo(false, "Debug (" + id + "): " + message + " " + dataUri);
138 AnimationTest.prototype.isFinished = function()
140 return this.isTestFinished;
143 AnimationTest.prototype.takeCleanSnapshot = function()
147 cleanElement = document.getElementById(this.cleanId);
150 // Enable clean page comparison element
152 this.enableDisplay(cleanElement);
155 // Take a snapshot of the initial (clean) page
156 this.cleanSnapshot = snapshotWindow(window, false);
158 // Disable the clean page comparison element
160 this.disableDisplay(cleanElement);
163 var dataString1 = "Clean Snapshot";
164 this.outputDebugInfo(dataString1, 'cleanSnap',
165 this.cleanSnapshot.toDataURL());
168 AnimationTest.prototype.takeBlankSnapshot = function()
170 // Take a snapshot of the initial (essentially blank) page
171 this.blankSnapshot = snapshotWindow(window, false);
173 var dataString1 = "Initial Blank Snapshot";
174 this.outputDebugInfo(dataString1, 'blank1Snap',
175 this.blankSnapshot.toDataURL());
179 * Begin the AnimationTest. This will utilize the information provided in the
180 * constructor to invoke a mochitest on animated images. It will automatically
181 * fail if allowed to run past the timeout. This will attempt to preload an
182 * image, if applicable, and then asynchronously call continueTest(), or if not
183 * applicable, synchronously trigger a call to continueTest().
185 AnimationTest.prototype.beginTest = function()
187 SimpleTest.waitForExplicitFinish();
194 * This is the second part of the test. It is triggered (eventually) from
195 * beginTest() either synchronously or asynchronously, as an image load
198 AnimationTest.prototype.continueTest = function()
200 // In case something goes wrong, fail earlier than mochitest timeout,
201 // and with more information.
202 setTimeout(failTest, this.timeout);
204 if (!this.reusingImageAsReference) {
205 this.disableDisplay(document.getElementById(this.imageElementId));
208 this.takeReferenceSnapshot();
209 this.setupPolledImage();
210 SimpleTest.executeSoon(pollForSuccess);
213 AnimationTest.prototype.setupPolledImage = function ()
215 // Make sure the image is visible
216 if (!this.reusingImageAsReference) {
217 this.enableDisplay(document.getElementById(this.imageElementId));
218 var currentSnapshot = snapshotWindow(window, false);
219 var result = compareSnapshots(currentSnapshot,
220 this.referenceSnapshot, true);
222 this.currentSnapshotDataURI = currentSnapshot.toDataURL();
226 ok(true, "Animated image looks correct, at poll #"
229 this.cleanUpAndFinish();
232 if (!gIsRefImageLoaded) {
233 this.myImage = new Image();
234 this.myImage.onload = reuseImageCallback;
235 document.getElementById(this.imageElementId).setAttribute('src',
236 this.referenceElementId);
241 AnimationTest.prototype.checkImage = function ()
243 if (this.isTestFinished) {
249 // We need this for some tests, because we need to force the
250 // test image to be visible.
251 if (!this.reusingImageAsReference) {
252 this.enableDisplay(document.getElementById(this.imageElementId));
255 var currentSnapshot = snapshotWindow(window, false);
256 var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
258 this.currentSnapshotDataURI = currentSnapshot.toDataURL();
262 ok(true, "Animated image looks correct, at poll #"
265 this.cleanUpAndFinish();
269 AnimationTest.prototype.takeReferenceSnapshot = function ()
273 // Test to make sure the reference image doesn't match a clean snapshot
274 if (!this.cleanSnapshot) {
275 this.takeCleanSnapshot();
278 // Used later to verify that the reference div disappeared
279 if (!this.blankSnapshot) {
280 this.takeBlankSnapshot();
283 if (this.reusingImageAsReference) {
284 // Show reference elem (which is actually our image), & take a snapshot
285 var referenceElem = document.getElementById(this.imageElementId);
286 this.enableDisplay(referenceElem);
288 this.referenceSnapshot = snapshotWindow(window, false);
290 var snapResult = compareSnapshots(this.cleanSnapshot,
291 this.referenceSnapshot, false);
292 if (!snapResult[0]) {
293 if (this.blankWaitTime > 2000) {
294 // if it took longer than two seconds to load the image, we probably
296 this.wereFailures = true;
298 "Reference snapshot shouldn't match clean (non-image) snapshot");
300 this.blankWaitTime += currentTest.pollFreq;
301 // let's wait a bit and see if it clears up
302 setTimeout(referencePoller, currentTest.pollFreq);
308 "Reference snapshot shouldn't match clean (non-image) snapshot");
310 var dataString = "Reference Snapshot #" + this.numRefsTaken;
311 this.outputDebugInfo(dataString, 'refSnapId',
312 this.referenceSnapshot.toDataURL());
314 // Make sure the animation section is hidden
315 this.disableDisplay(document.getElementById(this.imageElementId));
317 // Show reference div, & take a snapshot
318 var referenceDiv = document.getElementById(this.referenceElementId);
319 this.enableDisplay(referenceDiv);
321 this.referenceSnapshot = snapshotWindow(window, false);
322 var snapResult = compareSnapshots(this.cleanSnapshot,
323 this.referenceSnapshot, false);
324 if (!snapResult[0]) {
325 if (this.blankWaitTime > 2000) {
326 // if it took longer than two seconds to load the image, we probably
328 this.wereFailures = true;
330 "Reference snapshot shouldn't match clean (non-image) snapshot");
332 this.blankWaitTime += 20;
333 // let's wait a bit and see if it clears up
334 setTimeout(referencePoller, 20);
340 "Reference snapshot shouldn't match clean (non-image) snapshot");
342 var dataString = "Reference Snapshot #" + this.numRefsTaken;
343 this.outputDebugInfo(dataString, 'refSnapId',
344 this.referenceSnapshot.toDataURL());
346 // Re-hide reference div, and take another snapshot to be sure it's gone
347 this.disableDisplay(referenceDiv);
348 this.testBlankCameBack();
352 AnimationTest.prototype.enableDisplay = function(element)
359 element.style.display = '';
361 element.setAttribute('hidden', 'false');
365 AnimationTest.prototype.disableDisplay = function(element)
372 element.style.display = 'none';
374 element.setAttribute('hidden', 'true');
378 AnimationTest.prototype.testBlankCameBack = function()
380 var blankSnapshot2 = snapshotWindow(window, false);
381 var result = compareSnapshots(this.blankSnapshot, blankSnapshot2, true);
382 ok(result[0], "Reference image should disappear when it becomes display:none");
385 this.wereFailures = true;
386 var dataString = "Second Blank Snapshot";
387 this.outputDebugInfo(dataString, 'blank2SnapId', result[2]);
391 AnimationTest.prototype.cleanUpAndFinish = function ()
393 // On the off chance that failTest and checkImage are triggered
394 // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
395 if (this.isTestFinished) {
399 this.isTestFinished = true;
401 // Call our closing function, if one exists
402 if (this.closeFunc) {
407 if (this.wereFailures) {
408 document.getElementById(this.debugElementId).style.display = 'block';
412 document.getElementById(this.debugElementId).style.display = "";