summaryrefslogtreecommitdiffstats
path: root/image/test/mochitest/animationPolling.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--image/test/mochitest/animationPolling.js469
1 files changed, 469 insertions, 0 deletions
diff --git a/image/test/mochitest/animationPolling.js b/image/test/mochitest/animationPolling.js
new file mode 100644
index 0000000000..f20377cf9d
--- /dev/null
+++ b/image/test/mochitest/animationPolling.js
@@ -0,0 +1,469 @@
+// This file expects imgutils.js to be loaded as well.
+/* import-globals-from imgutils.js */
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+var currentTest;
+var gIsRefImageLoaded = false;
+const gShouldOutputDebugInfo = false;
+
+function pollForSuccess() {
+ if (!currentTest.isTestFinished) {
+ if (
+ !currentTest.reusingReferenceImage ||
+ (currentTest.reusingReferenceImage && gIsRefImageLoaded)
+ ) {
+ currentTest.checkImage();
+ }
+
+ setTimeout(pollForSuccess, currentTest.pollFreq);
+ }
+}
+
+function reuseImageCallback() {
+ gIsRefImageLoaded = true;
+}
+
+function failTest() {
+ if (currentTest.isTestFinished || currentTest.closeFunc) {
+ return;
+ }
+
+ ok(
+ false,
+ "timing out after " +
+ currentTest.timeout +
+ "ms. " +
+ "Animated image still doesn't look correct, after poll #" +
+ currentTest.pollCounter
+ );
+ currentTest.wereFailures = true;
+
+ if (currentTest.currentSnapshotDataURI) {
+ currentTest.outputDebugInfo(
+ "Snapshot #" + currentTest.pollCounter,
+ "snapNum" + currentTest.pollCounter,
+ currentTest.currentSnapshotDataURI
+ );
+ }
+
+ currentTest.enableDisplay(
+ document.getElementById(currentTest.debugElementId)
+ );
+
+ currentTest.cleanUpAndFinish();
+}
+
+/**
+ * Create a new AnimationTest object.
+ *
+ * @param pollFreq The amount of time (in ms) to wait between consecutive
+ * snapshots if the reference image and the test image don't match.
+ * @param timeout The total amount of time (in ms) to wait before declaring the
+ * test as failed.
+ * @param referenceElementId The id attribute of the reference image element, or
+ * the source of the image to change to, once the reference snapshot has
+ * been successfully taken. This latter option could be used if you don't
+ * want the image to become invisible at any time during the test.
+ * @param imageElementId The id attribute of the test image element.
+ * @param debugElementId The id attribute of the div where links should be
+ * appended if the test fails.
+ * @param cleanId The id attribute of the div or element to use as the 'clean'
+ * test. This element is only enabled when we are testing to verify that
+ * the reference image has been loaded. It can be undefined.
+ * @param srcAttr The location of the source of the image, for preloading. This
+ * is usually not required, but it useful for preloading reference
+ * images.
+ * @param xulTest A boolean value indicating whether or not this is a XUL test
+ * (uses hidden=true/false rather than display: none to hide/show
+ * elements).
+ * @param closeFunc A function that should be called when this test is finished.
+ * If null, then cleanUpAndFinish() will be called. This can be used to
+ * chain tests together, so they are all finished exactly once.
+ * @returns {AnimationTest}
+ */
+function AnimationTest(
+ pollFreq,
+ timeout,
+ referenceElementId,
+ imageElementId,
+ debugElementId,
+ cleanId,
+ srcAttr,
+ xulTest,
+ closeFunc
+) {
+ // We want to test the cold loading behavior, so clear cache in case an
+ // earlier test got our image in there already.
+ clearAllImageCaches();
+
+ this.wereFailures = false;
+ this.pollFreq = pollFreq;
+ this.timeout = timeout;
+ this.imageElementId = imageElementId;
+ this.referenceElementId = referenceElementId;
+
+ if (!document.getElementById(referenceElementId)) {
+ // In this case, we're assuming the user passed in a string that
+ // indicates the source of the image they want to change to,
+ // after the reference image has been taken.
+ this.reusingImageAsReference = true;
+ }
+
+ this.srcAttr = srcAttr;
+ this.debugElementId = debugElementId;
+ this.referenceSnapshot = ""; // value will be set in takeReferenceSnapshot()
+ this.pollCounter = 0;
+ this.isTestFinished = false;
+ this.numRefsTaken = 0;
+ this.blankWaitTime = 0;
+
+ this.cleanId = cleanId ? cleanId : "";
+ this.xulTest = xulTest ? xulTest : "";
+ this.closeFunc = closeFunc ? closeFunc : "";
+}
+
+AnimationTest.prototype.preloadImage = function () {
+ if (this.srcAttr) {
+ this.myImage = new Image();
+ this.myImage.onload = function () {
+ currentTest.continueTest();
+ };
+ this.myImage.src = this.srcAttr;
+ } else {
+ this.continueTest();
+ }
+};
+
+AnimationTest.prototype.outputDebugInfo = function (message, id, dataUri) {
+ if (!gShouldOutputDebugInfo) {
+ return;
+ }
+ var debugElement = document.getElementById(this.debugElementId);
+ var newDataUriElement = document.createElement("a");
+ newDataUriElement.setAttribute("id", id);
+ newDataUriElement.setAttribute("href", dataUri);
+ newDataUriElement.appendChild(document.createTextNode(message));
+ debugElement.appendChild(newDataUriElement);
+ var brElement = document.createElement("br");
+ debugElement.appendChild(brElement);
+ todo(false, "Debug (" + id + "): " + message + " " + dataUri);
+};
+
+AnimationTest.prototype.isFinished = function () {
+ return this.isTestFinished;
+};
+
+AnimationTest.prototype.takeCleanSnapshot = function () {
+ var cleanElement;
+ if (this.cleanId) {
+ cleanElement = document.getElementById(this.cleanId);
+ }
+
+ // Enable clean page comparison element
+ if (cleanElement) {
+ this.enableDisplay(cleanElement);
+ }
+
+ // Take a snapshot of the initial (clean) page
+ this.cleanSnapshot = snapshotWindow(window, false);
+
+ // Disable the clean page comparison element
+ if (cleanElement) {
+ this.disableDisplay(cleanElement);
+ }
+
+ var dataString1 = "Clean Snapshot";
+ this.outputDebugInfo(
+ dataString1,
+ "cleanSnap",
+ this.cleanSnapshot.toDataURL()
+ );
+};
+
+AnimationTest.prototype.takeBlankSnapshot = function () {
+ // Take a snapshot of the initial (essentially blank) page
+ this.blankSnapshot = snapshotWindow(window, false);
+
+ var dataString1 = "Initial Blank Snapshot";
+ this.outputDebugInfo(
+ dataString1,
+ "blank1Snap",
+ this.blankSnapshot.toDataURL()
+ );
+};
+
+/**
+ * Begin the AnimationTest. This will utilize the information provided in the
+ * constructor to invoke a mochitest on animated images. It will automatically
+ * fail if allowed to run past the timeout. This will attempt to preload an
+ * image, if applicable, and then asynchronously call continueTest(), or if not
+ * applicable, synchronously trigger a call to continueTest().
+ */
+AnimationTest.prototype.beginTest = function () {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+ currentTest = this;
+ this.preloadImage();
+};
+
+/**
+ * This is the second part of the test. It is triggered (eventually) from
+ * beginTest() either synchronously or asynchronously, as an image load
+ * callback.
+ */
+AnimationTest.prototype.continueTest = async function () {
+ // In case something goes wrong, fail earlier than mochitest timeout,
+ // and with more information.
+ setTimeout(failTest, this.timeout);
+
+ if (!this.reusingImageAsReference) {
+ this.disableDisplay(document.getElementById(this.imageElementId));
+ }
+
+ let tookReference = new Promise(resolve => {
+ this.takeReferenceSnapshot(resolve);
+ });
+
+ tookReference.then(() => {
+ this.setupPolledImage();
+ SimpleTest.executeSoon(pollForSuccess);
+ });
+};
+
+AnimationTest.prototype.setupPolledImage = function () {
+ // Make sure the image is visible
+ if (!this.reusingImageAsReference) {
+ this.enableDisplay(document.getElementById(this.imageElementId));
+ var currentSnapshot = snapshotWindow(window, false);
+ var result = compareSnapshots(
+ currentSnapshot,
+ this.referenceSnapshot,
+ true
+ );
+
+ this.currentSnapshotDataURI = currentSnapshot.toDataURL();
+
+ if (result[0]) {
+ // SUCCESS!
+ ok(true, "Animated image looks correct, at poll #" + this.pollCounter);
+
+ this.outputDebugInfo(
+ "Animated image",
+ "animImage",
+ this.currentSnapshotDataURI
+ );
+
+ this.outputDebugInfo(
+ "Reference image",
+ "refImage",
+ this.referenceSnapshot.toDataURL()
+ );
+
+ this.cleanUpAndFinish();
+ }
+ } else if (!gIsRefImageLoaded) {
+ this.myImage = new Image();
+ this.myImage.onload = reuseImageCallback;
+ document
+ .getElementById(this.imageElementId)
+ .setAttribute("src", this.referenceElementId);
+ }
+};
+
+AnimationTest.prototype.checkImage = function () {
+ if (this.isTestFinished) {
+ return;
+ }
+
+ this.pollCounter++;
+
+ // We need this for some tests, because we need to force the
+ // test image to be visible.
+ if (!this.reusingImageAsReference) {
+ this.enableDisplay(document.getElementById(this.imageElementId));
+ }
+
+ var currentSnapshot = snapshotWindow(window, false);
+ var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
+
+ this.currentSnapshotDataURI = currentSnapshot.toDataURL();
+
+ if (result[0]) {
+ // SUCCESS!
+ ok(true, "Animated image looks correct, at poll #" + this.pollCounter);
+
+ this.outputDebugInfo("Animated image", "animImage", result[1]);
+
+ this.outputDebugInfo("Reference image", "refImage", result[2]);
+
+ this.cleanUpAndFinish();
+ }
+};
+
+AnimationTest.prototype.takeReferenceSnapshot = function (resolve) {
+ this.numRefsTaken++;
+
+ // Test to make sure the reference image doesn't match a clean snapshot
+ if (!this.cleanSnapshot) {
+ this.takeCleanSnapshot();
+ }
+
+ // Used later to verify that the reference div disappeared
+ if (!this.blankSnapshot) {
+ this.takeBlankSnapshot();
+ }
+
+ if (this.reusingImageAsReference) {
+ // Show reference elem (which is actually our image), & take a snapshot
+ var referenceElem = document.getElementById(this.imageElementId);
+ this.enableDisplay(referenceElem);
+
+ this.referenceSnapshot = snapshotWindow(window, false);
+
+ let snapResult = compareSnapshots(
+ this.cleanSnapshot,
+ this.referenceSnapshot,
+ false
+ );
+ if (!snapResult[0]) {
+ if (this.blankWaitTime > 2000) {
+ // if it took longer than two seconds to load the image, we probably
+ // have a problem.
+ this.wereFailures = true;
+ ok(
+ snapResult[0],
+ "Reference snapshot shouldn't match clean (non-image) snapshot"
+ );
+ } else {
+ this.blankWaitTime += currentTest.pollFreq;
+ // let's wait a bit and see if it clears up
+ setTimeout(
+ () => this.takeReferenceSnapshot(resolve),
+ currentTest.pollFreq
+ );
+ return;
+ }
+ }
+
+ ok(
+ snapResult[0],
+ "Reference snapshot shouldn't match clean (non-image) snapshot"
+ );
+
+ let dataString = "Reference Snapshot #" + this.numRefsTaken;
+ this.outputDebugInfo(
+ dataString,
+ "refSnapId",
+ this.referenceSnapshot.toDataURL()
+ );
+ } else {
+ // Make sure the animation section is hidden
+ this.disableDisplay(document.getElementById(this.imageElementId));
+
+ // Show reference div, & take a snapshot
+ var referenceDiv = document.getElementById(this.referenceElementId);
+ this.enableDisplay(referenceDiv);
+
+ this.referenceSnapshot = snapshotWindow(window, false);
+ let snapResult = compareSnapshots(
+ this.cleanSnapshot,
+ this.referenceSnapshot,
+ false
+ );
+ if (!snapResult[0]) {
+ if (this.blankWaitTime > 2000) {
+ // if it took longer than two seconds to load the image, we probably
+ // have a problem.
+ this.wereFailures = true;
+ ok(
+ snapResult[0],
+ "Reference snapshot shouldn't match clean (non-image) snapshot"
+ );
+ } else {
+ this.blankWaitTime += 20;
+ // let's wait a bit and see if it clears up
+ setTimeout(() => this.takeReferenceSnapshot(resolve), 20);
+ return;
+ }
+ }
+
+ ok(
+ snapResult[0],
+ "Reference snapshot shouldn't match clean (non-image) snapshot"
+ );
+
+ let dataString = "Reference Snapshot #" + this.numRefsTaken;
+ this.outputDebugInfo(
+ dataString,
+ "refSnapId",
+ this.referenceSnapshot.toDataURL()
+ );
+
+ // Re-hide reference div, and take another snapshot to be sure it's gone
+ this.disableDisplay(referenceDiv);
+ this.testBlankCameBack();
+ }
+ resolve();
+};
+
+AnimationTest.prototype.enableDisplay = function (element) {
+ if (!element) {
+ return;
+ }
+
+ if (!this.xulTest) {
+ element.style.display = "";
+ } else {
+ element.setAttribute("hidden", "false");
+ }
+};
+
+AnimationTest.prototype.disableDisplay = function (element) {
+ if (!element) {
+ return;
+ }
+
+ if (!this.xulTest) {
+ element.style.display = "none";
+ } else {
+ element.setAttribute("hidden", "true");
+ }
+};
+
+AnimationTest.prototype.testBlankCameBack = function () {
+ var blankSnapshot2 = snapshotWindow(window, false);
+ var result = compareSnapshots(this.blankSnapshot, blankSnapshot2, true);
+ ok(
+ result[0],
+ "Reference image should disappear when it becomes display:none"
+ );
+
+ if (!result[0]) {
+ this.wereFailures = true;
+ var dataString = "Second Blank Snapshot";
+ this.outputDebugInfo(dataString, "blank2SnapId", result[2]);
+ }
+};
+
+AnimationTest.prototype.cleanUpAndFinish = function () {
+ // On the off chance that failTest and checkImage are triggered
+ // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
+ if (this.isTestFinished) {
+ return;
+ }
+
+ this.isTestFinished = true;
+
+ // Call our closing function, if one exists
+ if (this.closeFunc) {
+ this.closeFunc();
+ return;
+ }
+
+ if (this.wereFailures) {
+ document.getElementById(this.debugElementId).style.display = "block";
+ }
+
+ SimpleTest.finish();
+ document.getElementById(this.debugElementId).style.display = "";
+};