From 6b40da9ca13d1384414035566601bb89f59bbf81 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Mon, 9 Nov 2015 23:17:41 +0800 Subject: [PATCH] Bug 1219711 - Refactor captureStream_common.js to accept generic pixel testing method. r=jib --HG-- extra : commitid : 1Lds1zOCtPn extra : rebase_source : 391f0a9871e0f5c26d5480a10344e6a1c5fcb608 --- dom/canvas/test/captureStream_common.js | 102 ++++++++++++------ dom/canvas/test/test_capture.html | 49 ++++++--- .../test/webgl-mochitest/test_capture.html | 48 ++++++--- ...arecorder_record_canvas_captureStream.html | 2 +- ...eerConnection_captureStream_canvas_2d.html | 6 +- ...Connection_captureStream_canvas_webgl.html | 6 +- 6 files changed, 142 insertions(+), 71 deletions(-) diff --git a/dom/canvas/test/captureStream_common.js b/dom/canvas/test/captureStream_common.js index 91be55ed513b..6b9b4c66a8fc 100644 --- a/dom/canvas/test/captureStream_common.js +++ b/dom/canvas/test/captureStream_common.js @@ -25,6 +25,7 @@ function CaptureStreamTestHelper(width, height) { CaptureStreamTestHelper.prototype = { /* Predefined colors for use in the methods below. */ black: { data: [0, 0, 0, 255], name: "black" }, + blackTransparent: { data: [0, 0, 0, 0], name: "blackTransparent" }, green: { data: [0, 255, 0, 255], name: "green" }, red: { data: [255, 0, 0, 255], name: "red" }, @@ -52,55 +53,90 @@ CaptureStreamTestHelper.prototype = { video.srcObject.requestFrame(); }, - /* Tests the top left pixel of |video| against |refData|. Format [R,G,B,A]. */ - testPixel: function (video, refData, threshold) { + /* + * Returns the pixel at (|offsetX|, |offsetY|) (from top left corner) of + * |video| as an array of the pixel's color channels: [R,G,B,A]. + */ + getPixel: function (video, offsetX, offsetY) { + offsetX = offsetX || 0; // Set to 0 if not passed in. + offsetY = offsetY || 0; // Set to 0 if not passed in. var ctxout = this.cout.getContext('2d'); ctxout.drawImage(video, 0, 0); - var pixel = ctxout.getImageData(0, 0, 1, 1).data; - return pixel.every((val, i) => Math.abs(val - refData[i]) <= threshold); + return ctxout.getImageData(offsetX, offsetY, 1, 1).data; }, /* - * Returns a promise that resolves when the pixel matches. Use |threshold| - * for fuzzy matching the color on each channel, in the range [0,255]. + * Returns true if px lies within the per-channel |threshold| of the + * referenced color for all channels. px is on the form of an array of color + * channels, [R,G,B,A]. Each channel is in the range [0, 255]. */ - waitForPixel: function (video, refColor, threshold, infoString) { + isPixel: function (px, refColor, threshold) { + threshold = threshold || 0; // Default to 0 (exact match) if not passed in. + return px.every((ch, i) => Math.abs(ch - refColor.data[i]) <= threshold); + }, + + /* + * Returns true if px lies further away than |threshold| of the + * referenced color for any channel. px is on the form of an array of color + * channels, [R,G,B,A]. Each channel is in the range [0, 255]. + */ + isPixelNot: function (px, refColor, threshold) { + if (threshold === undefined) { + // Default to 127 (should be sufficiently far away) if not passed in. + threshold = 127; + } + return px.some((ch, i) => Math.abs(ch - refColor.data[i]) > threshold); + }, + + /* + * Returns a promise that resolves when the provided function |test| + * returns true. + */ + waitForPixel: function (video, offsetX, offsetY, test, timeout) { return new Promise(resolve => { - info("Testing " + video.id + " against [" + refColor.data.join(',') + "]"); + const startTime = video.currentTime; CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout); - video.ontimeupdate = () => { - if (this.testPixel(video, refColor.data, threshold)) { - ok(true, video.id + " " + infoString); - video.ontimeupdate = null; - resolve(); + var ontimeupdate = () => { + const pixelMatch = test(this.getPixel(video, offsetX, offsetY)); + if (!pixelMatch && + (!timeout || video.currentTime < startTime + (timeout / 1000.0))) { + // No match yet and, + // No timeout (waiting indefinitely) or |timeout| has not passed yet. + return; } + video.removeEventListener("timeupdate", ontimeupdate); + resolve(pixelMatch); }; + video.addEventListener("timeupdate", ontimeupdate); }); }, /* - * Returns a promise that resolves after |timeout| ms of playback or when a - * pixel of |video| becomes the color |refData|. The test is failed if the + * Returns a promise that resolves when the top left pixel of |video| matches + * on all channels. Use |threshold| for fuzzy matching the color on each + * channel, in the range [0,255]. + */ + waitForPixelColor: function (video, refColor, threshold, infoString) { + info("Waiting for video " + video.id + " to match [" + + refColor.data.join(',') + "] - " + refColor.name + + " (" + infoString + ")"); + return this.waitForPixel(video, 0, 0, + px => this.isPixel(px, refColor, threshold)) + .then(() => ok(true, video.id + " " + infoString)); + }, + + /* + * Returns a promise that resolves after |timeout| ms of playback or when the + * top left pixel of |video| becomes |refColor|. The test is failed if the * timeout is not reached. */ - waitForPixelToTimeout: function (video, refColor, threshold, timeout, infoString) { - return new Promise(resolve => { - info("Waiting for " + video.id + " to time out after " + timeout + - "ms against [" + refColor.data.join(',') + "] - " + refColor.name); - CaptureStreamTestHelper2D.prototype.clear.call(this, this.cout); - var startTime = video.currentTime; - video.ontimeupdate = () => { - if (this.testPixel(video, refColor.data, threshold)) { - ok(false, video.id + " " + infoString); - video.ontimeupdate = null; - resolve(); - } else if (video.currentTime > startTime + (timeout / 1000.0)) { - ok(true, video.id + " " + infoString); - video.ontimeupdate = null; - resolve(); - } - }; - }); + waitForPixelColorTimeout: function (video, refColor, threshold, timeout, infoString) { + info("Waiting for " + video.id + " to time out after " + timeout + + "ms against [" + refColor.data.join(',') + "] - " + refColor.name); + return this.waitForPixel(video, 0, 0, + px => this.isPixel(px, refColor, threshold), + timeout) + .then(result => ok(!result, video.id + " " + infoString)); }, /* Create an element of type |type| with id |id| and append it to the body. */ diff --git a/dom/canvas/test/test_capture.html b/dom/canvas/test/test_capture.html index ec2902b3ac9f..69222d75a9ec 100644 --- a/dom/canvas/test/test_capture.html +++ b/dom/canvas/test/test_capture.html @@ -23,15 +23,21 @@ function checkDrawColorInitialRed() { vmanual.srcObject = c.captureStream(0); vrate.srcObject = c.captureStream(10); - ok(h.testPixel(vauto, [0, 0, 0, 0], 0), "vauto hould not be drawn to before stable state"); - ok(h.testPixel(vrate, [0, 0, 0, 0], 0), "vrate Should not be drawn to before stable state"); - ok(h.testPixel(vmanual, [0, 0, 0, 0], 0), "vmanual Should not be drawn to before stable state"); + ok(h.isPixel(h.getPixel(vauto), h.blackTransparent, 0), + "vauto should not be drawn to before stable state"); + ok(h.isPixel(h.getPixel(vrate), h.blackTransparent, 0), + "vrate should not be drawn to before stable state"); + ok(h.isPixel(h.getPixel(vmanual), h.blackTransparent, 0), + "vmanual should not be drawn to before stable state"); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should become red when we get" + - " to stable state (first frame)")); + .then(() => h.waitForPixelColor(vauto, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vrate, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should become red when we get" + + " to stable state (first frame)")); } function checkDrawColorGreen() { @@ -40,11 +46,15 @@ function checkDrawColorGreen() { var drawing = h.startDrawing(() => h.drawColor(c, h.green)); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.green, 0, "should become green automatically")) - .then(() => h.waitForPixel(vrate, h.green, 0, "should become green automatically")) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should still be red")) + .then(() => h.waitForPixelColor(vauto, h.green, 0, + "should become green automatically")) + .then(() => h.waitForPixelColor(vrate, h.green, 0, + "should become green automatically")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should still be red")) .then(() => h.requestFrame(vmanual)) - .then(() => h.waitForPixel(vmanual, h.green, 0, "should become green after requstFrame()")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should become green after requstFrame()")) .catch(err => ok(false, "checkDrawColorGreen failed: ", err)) .then(() => drawing.stop()); } @@ -54,10 +64,12 @@ function checkRequestFrameOrderGuarantee() { "call results in the expected frame seen in the stream."); return Promise.resolve() - .then(() => h.waitForPixel(vmanual, h.green, 0, "should still be green")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should still be green")) .then(() => h.drawColor(c, h.red)) // 1. Draw canvas red .then(() => h.requestFrame(vmanual)) // 2. Immediately request a frame - .then(() => h.waitForPixel(vmanual, h.red, 0, "should become red after call order test")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should become red after call order test")) } function checkDrawImageNotCleanRed() { @@ -74,11 +86,14 @@ function checkDrawImageNotCleanRed() { }) .then(() => drawing = h.startDrawing(() => ctx.drawImage(notCleanRed, 0, 0, c.width, c.height))) .then(() => h.testNotClean(c)) - .then(() => h.waitForPixelToTimeout(vauto, h.red, 0, 1000, "should not become red")) - .then(() => h.waitForPixelToTimeout(vrate, h.red, 0, 0, "should not become red")) - .then(() => h.waitForPixel(vmanual, h.green, 0, "should still be green")) + .then(() => h.waitForPixelColorTimeout(vauto, h.red, 0, 1000, + "should not become red")) + .then(() => h.isPixelNot(h.getPixel(vrate), h.red, 250, + "should not have become red")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, "should still be green")) .then(() => h.requestFrame(vmanual)) - .then(() => h.waitForPixelToTimeout(vmanual, h.red, 0, 1000, "should not become red")) + .then(() => h.waitForPixelColorTimeout(vmanual, h.red, 0, 1000, + "should not become red")) .catch(err => ok(false, "checkDrawImageNotCleanRed failed: ", err)) .then(() => drawing.stop()); } diff --git a/dom/canvas/test/webgl-mochitest/test_capture.html b/dom/canvas/test/webgl-mochitest/test_capture.html index 489d67872361..fc27b35e7341 100644 --- a/dom/canvas/test/webgl-mochitest/test_capture.html +++ b/dom/canvas/test/webgl-mochitest/test_capture.html @@ -54,14 +54,21 @@ function checkClearColorInitialRed() { vmanual.srcObject = c.captureStream(0); vrate.srcObject = c.captureStream(10); - ok(h.testPixel(vauto, [0, 0, 0, 0], 0), "Should not be drawn to before stable state"); - ok(h.testPixel(vrate, [0, 0, 0, 0], 0), "Should not be drawn to before stable state"); - ok(h.testPixel(vmanual, [0, 0, 0, 0], 0), "Should not be drawn to before stable state"); + ok(h.isPixel(h.getPixel(vauto), h.blackTransparent, 0, + "vauto should not be drawn to before stable state")); + ok(h.isPixel(h.getPixel(vrate), h.blackTransparent, 0, + "vrate should not be drawn to before stable state")); + ok(h.isPixel(h.getPixel(vmanual), h.blackTransparent, 0, + "vmanual should not be drawn to before stable state")); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should become red when we get to stable state (first frame)")) + .then(() => h.waitForPixelColor(vauto, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vrate, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should become red when we get to stable " + + "state (first frame)")) } function checkDrawColorGreen() { @@ -69,11 +76,15 @@ function checkDrawColorGreen() { var drawing = h.startDrawing(h.drawColor.bind(h, c, h.green)); checkGLError('after DrawColor'); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.green, 0, "should become green automatically")) - .then(() => h.waitForPixel(vrate, h.green, 0, "should become green automatically")) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should still be red")) + .then(() => h.waitForPixelColor(vauto, h.green, 0, + "should become green automatically")) + .then(() => h.waitForPixelColor(vrate, h.green, 0, + "should become green automatically")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should still be red")) .then(() => h.requestFrame(vmanual)) - .then(() => h.waitForPixel(vmanual, h.green, 0, "should become green after requstFrame()")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should become green after requstFrame()")) .then(() => drawing.stop()); } @@ -81,11 +92,15 @@ function checkClearColorRed() { info("Checking that clearing to red works."); var drawing = h.startDrawing(h.clearColor.bind(h, c, h.red)); return Promise.resolve() - .then(() => h.waitForPixel(vauto, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vrate, h.red, 0, "should become red automatically")) - .then(() => h.waitForPixel(vmanual, h.green, 0, "should still be green")) + .then(() => h.waitForPixelColor(vauto, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vrate, h.red, 0, + "should become red automatically")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should still be green")) .then(() => h.requestFrame(vmanual)) - .then(() => h.waitForPixel(vmanual, h.red, 0, "should become red after requestFrame()")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, + "should become red after requestFrame()")) .then(() => drawing.stop()); } @@ -93,10 +108,11 @@ function checkRequestFrameOrderGuarantee() { info("Checking that requestFrame() immediately after a draw " + "call results in the expected frame seen in the stream."); return Promise.resolve() - .then(() => h.waitForPixel(vmanual, h.red, 0, "should still be red")) + .then(() => h.waitForPixelColor(vmanual, h.red, 0, "should still be red")) .then(() => h.drawColor(c, h.green)) // 1. Draw canvas green .then(() => h.requestFrame(vmanual)) // 2. Immediately request a frame - .then(() => h.waitForPixel(vmanual, h.green, 0, "should become green after call order test")) + .then(() => h.waitForPixelColor(vmanual, h.green, 0, + "should become green after call order test")) } function finish() { diff --git a/dom/media/test/test_mediarecorder_record_canvas_captureStream.html b/dom/media/test/test_mediarecorder_record_canvas_captureStream.html index 21456b93539d..7f440fba9ddd 100644 --- a/dom/media/test/test_mediarecorder_record_canvas_captureStream.html +++ b/dom/media/test/test_mediarecorder_record_canvas_captureStream.html @@ -56,7 +56,7 @@ function startTest() { SimpleTest.finish(); }; document.getElementById("content").appendChild(video); - helper.waitForPixel(video, helper.red, 128, "Should become red") + helper.waitForPixelColor(video, helper.red, 128, "Should become red") .then(SimpleTest.finish); }; diff --git a/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_2d.html b/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_2d.html index cf82d4e28a7e..624d641d11c1 100644 --- a/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_2d.html +++ b/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_2d.html @@ -39,7 +39,8 @@ runNetworkTest(() => { ok(!!vremote, "Should have remote video element for pcRemote"); }, function WAIT_FOR_REMOTE_GREEN() { - return h.waitForPixel(vremote, h.green, 128, "pcRemote's remote should become green"); + return h.waitForPixelColor(vremote, h.green, 128, + "pcRemote's remote should become green"); }, function DRAW_LOCAL_RED() { // After requesting a frame it will be captured at the time of next render. @@ -49,7 +50,8 @@ runNetworkTest(() => { h.drawColor(canvas, h.red); }, function WAIT_FOR_REMOTE_RED() { - return h.waitForPixel(vremote, h.red, 128, "pcRemote's remote should become red"); + return h.waitForPixelColor(vremote, h.red, 128, + "pcRemote's remote should become red"); } ]); test.run(); diff --git a/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_webgl.html b/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_webgl.html index 3b9377563db4..02e4018d4595 100644 --- a/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_webgl.html +++ b/dom/media/tests/mochitest/test_peerConnection_captureStream_canvas_webgl.html @@ -89,7 +89,8 @@ runNetworkTest(() => { ok(!!vremote, "Should have remote video element for pcRemote"); }, function WAIT_FOR_REMOTE_GREEN() { - return h.waitForPixel(vremote, h.green, 128, "pcRemote's remote should become green"); + return h.waitForPixelColor(vremote, h.green, 128, + "pcRemote's remote should become green"); }, function REQUEST_FRAME(test) { // After requesting a frame it will be captured at the time of next render. @@ -101,7 +102,8 @@ runNetworkTest(() => { h.drawColor(canvas, h.red); }, function WAIT_FOR_REMOTE_RED() { - return h.waitForPixel(vremote, h.red, 128, "pcRemote's remote should become red"); + return h.waitForPixelColor(vremote, h.red, 128, + "pcRemote's remote should become red"); } ]); test.run();