OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // The tests here cover the end-to-end functionality of tab capturing and | 5 // The tests here cover the end-to-end functionality of tab capturing and |
6 // playback as video. The page generates video test patterns that rotate | 6 // playback as video. The page generates video test patterns that rotate |
7 // cyclicly, and the rendering output of the tab is captured into a | 7 // cyclicly, and the rendering output of the tab is captured into a |
8 // LocalMediaStream. This stream is then piped into a video element for | 8 // local MediaStream. This stream is then piped into a video element for |
9 // playback, and a canvas is used to examine the frames of the video for | 9 // playback, and a canvas is used to examine the frames of the video for |
10 // expected content. The stream may be plumbed one of two ways, depending on | 10 // expected content. The stream may be plumbed one of two ways, depending on |
11 // the 'method' query param: | 11 // the 'method' query param: |
12 // | 12 // |
13 // local: LocalMediaStream --> DOM Video Element | 13 // local: LocalMediaStream --> DOM Video Element |
14 // webrtc: LocalMediaStream --> PeerConnection (sender) | 14 // webrtc: LocalMediaStream --> PeerConnection (sender) |
15 // --> PeerConnection (receiver) --> DOM Video Element | 15 // --> PeerConnection (receiver) --> DOM Video Element |
| 16 // |
| 17 // There are two rounds of testing: In each round, video frames are received and |
| 18 // monitored, and the round successfully completes once each of the expected |
| 19 // patterns is observed. Between the two rounds, the MediaStream is disconnected |
| 20 // from its consumer, which places the tab capture "device" into a suspend mode. |
| 21 var testRoundNumber = 0; |
16 | 22 |
17 // The test pattern cycles as a color fill of red, then green, then blue. | 23 // The test pattern cycles as a color fill of red, then green, then blue. |
18 var colors = [ [ 255, 0, 0 ], [ 0, 255, 0 ], [ 0, 0, 255 ] ]; | 24 var colors = [ [ 255, 0, 0 ], [ 0, 255, 0 ], [ 0, 0, 255 ] ]; |
19 var curIdx = 0; | 25 var curIdx = 0; |
20 | 26 |
21 // Capture parameters. | 27 // Capture parameters. |
22 var width = 64; | 28 var width = 64; |
23 var height = 48; | 29 var height = 48; |
24 var frameRate = 15; | 30 var frameRate = 15; |
25 | 31 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 this.stepTimeMillis = 100; | 77 this.stepTimeMillis = 100; |
72 } | 78 } |
73 var now = new Date().getTime(); | 79 var now = new Date().getTime(); |
74 if (!this.nextSteppingAt) { | 80 if (!this.nextSteppingAt) { |
75 this.nextSteppingAt = now + this.stepTimeMillis; | 81 this.nextSteppingAt = now + this.stepTimeMillis; |
76 } else if (now >= this.nextSteppingAt) { | 82 } else if (now >= this.nextSteppingAt) { |
77 ++curIdx; | 83 ++curIdx; |
78 if (curIdx >= colors.length) { // Completed a cycle. | 84 if (curIdx >= colors.length) { // Completed a cycle. |
79 curIdx = 0; | 85 curIdx = 0; |
80 // Increase the wait time between switching test patterns for overloaded | 86 // Increase the wait time between switching test patterns for overloaded |
81 // bots that aren't capturing all the frames of video. | 87 // bots that aren't capturing all the frames of video. Only do this for |
82 this.stepTimeMillis *= 1.25; | 88 // the first test phase, since increases shouldn't be needed after that. |
| 89 if (testRoundNumber == 0) { |
| 90 this.stepTimeMillis *= 1.25; |
| 91 } |
83 } | 92 } |
84 this.nextSteppingAt = now + this.stepTimeMillis; | 93 this.nextSteppingAt = now + this.stepTimeMillis; |
85 } | 94 } |
86 } | 95 } |
87 | 96 |
88 function waitForExpectedColors(colorDeviation) { | 97 function waitForExpectedColors(colorDeviation) { |
89 // If needed, create the video and canvas elements, but no need to append them | 98 // At the start of a round of testing, create the video and canvas elements, |
90 // to the DOM. | 99 // but no need to append them to the DOM. |
91 if (!this.video) { | 100 if (!this.video) { |
92 this.video = document.createElement("video"); | 101 this.video = document.createElement("video"); |
93 this.video.width = width; | 102 this.video.width = width; |
94 this.video.height = height; | 103 this.video.height = height; |
95 this.video.addEventListener("error", chrome.test.fail); | 104 this.video.addEventListener("error", chrome.test.fail); |
96 this.video.src = URL.createObjectURL(receiveStream); | 105 this.video.src = URL.createObjectURL(receiveStream); |
97 this.video.play(); | 106 this.video.play(); |
98 | 107 |
99 this.readbackCanvas = document.createElement("canvas"); | 108 this.readbackCanvas = document.createElement("canvas"); |
100 this.readbackCanvas.width = width; | 109 this.readbackCanvas.width = width; |
(...skipping 11 matching lines...) Expand all Loading... |
112 ctx.drawImage(video, 0, 0, width, height); | 121 ctx.drawImage(video, 0, 0, width, height); |
113 var imageData = ctx.getImageData(width / 2, height / 2, 1, 1); | 122 var imageData = ctx.getImageData(width / 2, height / 2, 1, 1); |
114 var pixel = [ imageData.data[0], imageData.data[1], imageData.data[2] ]; | 123 var pixel = [ imageData.data[0], imageData.data[1], imageData.data[2] ]; |
115 | 124 |
116 // Does the pixel match one of the expected colors? | 125 // Does the pixel match one of the expected colors? |
117 for (var i = 0; i < expectedColors.length; ++i) { | 126 for (var i = 0; i < expectedColors.length; ++i) { |
118 var curColor = expectedColors[i]; | 127 var curColor = expectedColors[i]; |
119 if (Math.abs(pixel[0] - curColor[0]) <= colorDeviation && | 128 if (Math.abs(pixel[0] - curColor[0]) <= colorDeviation && |
120 Math.abs(pixel[1] - curColor[1]) <= colorDeviation && | 129 Math.abs(pixel[1] - curColor[1]) <= colorDeviation && |
121 Math.abs(pixel[2] - curColor[2]) <= colorDeviation) { | 130 Math.abs(pixel[2] - curColor[2]) <= colorDeviation) { |
122 console.debug("Observed expected color RGB(" + curColor + | 131 console.debug(`${testRoundNumber == 0 ? 'First' : 'Second'} round: ` + |
123 ") in the video as RGB(" + pixel + ")"); | 132 `Observed expected color RGB(${curColor}) in the video as ` + |
| 133 `RGB(${pixel})`); |
124 expectedColors.splice(i, 1); | 134 expectedColors.splice(i, 1); |
125 } | 135 } |
126 } | 136 } |
127 } | 137 } |
128 | 138 |
129 if (expectedColors.length == 0) { | 139 if (expectedColors.length == 0) { |
130 chrome.test.succeed(); | 140 // Successful end of the current test round. If the first round, sleep, then |
| 141 // execute the second round. If the second round, then the whole test has |
| 142 // succeeded. |
| 143 if (testRoundNumber == 0) { |
| 144 // Destroy the video, which will disconnect the consumer of the |
| 145 // MediaStream. |
| 146 this.video.removeEventListener("error", chrome.test.fail); |
| 147 this.video.src = ''; |
| 148 this.video = null; |
| 149 |
| 150 // Wait one second, then execute the second round of testing. This tests |
| 151 // the suspend/resume functionality of tab capture (w.r.t. a MediaStream |
| 152 // having no consumers, and then being re-used with a new consumer). |
| 153 console.debug('First round succeeded! Now testing suspend/resume...'); |
| 154 setTimeout(function() { |
| 155 ++testRoundNumber; |
| 156 for (const color of colors) { |
| 157 expectedColors.push(color); |
| 158 } |
| 159 waitForExpectedColors(colorDeviation); |
| 160 }, 1000); |
| 161 } else { |
| 162 chrome.test.succeed(); |
| 163 } |
131 } else { | 164 } else { |
132 setTimeout(function () { waitForExpectedColors(colorDeviation); }, | 165 setTimeout(function () { waitForExpectedColors(colorDeviation); }, |
133 1000 / frameRate); | 166 1000 / frameRate); |
134 } | 167 } |
135 } | 168 } |
136 | 169 |
137 chrome.test.runTests([ | 170 chrome.test.runTests([ |
138 function endToEndTest() { | 171 function endToEndTest() { |
139 // The receive port changes between browser_test invocations, and is passed | 172 // The receive port changes between browser_test invocations, and is passed |
140 // as an query parameter in the URL. | 173 // as an query parameter in the URL. |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
204 } else { | 237 } else { |
205 chrome.test.fail("Unknown transport method: " + transportMethod); | 238 chrome.test.fail("Unknown transport method: " + transportMethod); |
206 } | 239 } |
207 }); | 240 }); |
208 } | 241 } |
209 ]); | 242 ]); |
210 | 243 |
211 // TODO(miu): Once the WebAudio API is finalized, we should add a test to emit a | 244 // TODO(miu): Once the WebAudio API is finalized, we should add a test to emit a |
212 // tone from the sender page, and have the receiver page check for the audio | 245 // tone from the sender page, and have the receiver page check for the audio |
213 // tone. | 246 // tone. |
OLD | NEW |