OLD | NEW |
1 // Asynchronous tests should manually call finishRepaintTest at the appropriate | 1 // Asynchronous tests should manually call finishRepaintTest at the appropriate |
2 // time. | 2 // time. |
3 window.testIsAsync = false; | 3 window.testIsAsync = false; |
4 window.outputRepaintRects = true; | 4 window.outputRepaintRects = true; |
| 5 window.generateMinimumRepaint = false; // See comments about 'Minimum repaint' b
elow. |
5 | 6 |
6 function runRepaintTest() | 7 function runRepaintTest() |
7 { | 8 { |
8 if (!window.testRunner || !window.internals) { | 9 if (!window.testRunner || !window.internals) { |
9 setTimeout(repaintTest, 500); | 10 setTimeout(repaintTest, 500); |
10 return; | 11 return; |
11 } | 12 } |
12 | 13 |
13 // TODO(enne): this is a workaround for multiple svg onload events. | 14 // TODO(enne): this is a workaround for multiple svg onload events. |
14 // See: http://crbug.com/372946 | 15 // See: http://crbug.com/372946 |
15 if (window.hasRunRepaintTest) | 16 if (window.hasRunRepaintTest) |
16 return; | 17 return; |
17 window.hasRunRepaintTest = true; | 18 window.hasRunRepaintTest = true; |
18 | 19 |
19 if (window.enablePixelTesting) | 20 if (window.enablePixelTesting) |
20 testRunner.dumpAsTextWithPixelResults(); | 21 testRunner.dumpAsTextWithPixelResults(); |
21 else | 22 else |
22 testRunner.dumpAsText(); | 23 testRunner.dumpAsText(); |
23 | 24 |
24 // All repaint tests are asynchronous. | 25 // All repaint tests are asynchronous. |
25 testRunner.waitUntilDone(); | 26 testRunner.waitUntilDone(); |
26 | 27 |
27 testRunner.displayAsyncThen(function() { | 28 function continueRepaintTest() |
| 29 { |
28 window.internals.startTrackingRepaints(document); | 30 window.internals.startTrackingRepaints(document); |
29 repaintTest(); | 31 repaintTest(); |
30 if (!window.testIsAsync) | 32 if (!window.testIsAsync) |
31 finishRepaintTest(); | 33 finishRepaintTest(); |
32 }); | 34 } |
| 35 |
| 36 if (window.generateMinimumRepaint) { |
| 37 testRunner.capturePixelsAsyncThen(function(width, height, snapshot) { |
| 38 window.widthBeforeRepaint = width; |
| 39 window.heightBeforeRepaint = height; |
| 40 window.snapshotBeforeRepaint = snapshot; |
| 41 continueRepaintTest(); |
| 42 }); |
| 43 } else { |
| 44 testRunner.displayAsyncThen(continueRepaintTest); |
| 45 }; |
33 } | 46 } |
34 | 47 |
35 function runRepaintAndPixelTest() | 48 function runRepaintAndPixelTest() |
36 { | 49 { |
37 window.enablePixelTesting = true; | 50 window.enablePixelTesting = true; |
38 runRepaintTest(); | 51 runRepaintTest(); |
39 } | 52 } |
40 | 53 |
41 function forceStyleRecalc() | 54 function forceStyleRecalc() |
42 { | 55 { |
43 if (document.body) | 56 if (document.body) |
44 document.body.offsetTop; | 57 document.body.offsetTop; |
45 else if (document.documentElement) | 58 else if (document.documentElement) |
46 document.documentElement.offsetTop; | 59 document.documentElement.offsetTop; |
47 } | 60 } |
48 | 61 |
49 function finishRepaintTest() | 62 function finishRepaintTest() |
50 { | 63 { |
51 if (!window.testRunner || !window.internals) | 64 if (!window.testRunner || !window.internals) |
52 return; | 65 return; |
53 | 66 |
54 // Force a style recalc. | 67 // Force a style recalc. |
55 forceStyleRecalc(); | 68 forceStyleRecalc(); |
56 | 69 |
57 var repaintRects = window.internals.layerTreeAsText(document, window.interna
ls.LAYER_TREE_INCLUDES_REPAINT_RECTS); | 70 var repaintRects = window.internals.layerTreeAsText(document, window.interna
ls.LAYER_TREE_INCLUDES_REPAINT_RECTS); |
58 | 71 |
59 internals.stopTrackingRepaints(document); | 72 internals.stopTrackingRepaints(document); |
60 | 73 |
61 // Play nice with JS tests which may want to print out assert results. | 74 function repaintTestDone() |
62 if (window.isJsTest) | 75 { |
63 window.outputRepaintRects = false; | 76 // Play nice with JS tests which may want to print out assert results. |
| 77 if (window.isJsTest) |
| 78 window.outputRepaintRects = false; |
64 | 79 |
65 if (window.outputRepaintRects) | 80 if (window.outputRepaintRects) |
66 testRunner.setCustomTextOutput(repaintRects); | 81 testRunner.setCustomTextOutput(repaintRects); |
67 | 82 |
68 if (window.afterTest) | 83 if (window.afterTest) |
69 window.afterTest(); | 84 window.afterTest(); |
70 | 85 |
71 // Play nice with async JS tests which want to notifyDone themselves. | 86 // Play nice with async JS tests which want to notifyDone themselves. |
72 if (!window.jsTestIsAsync) | 87 if (!window.jsTestIsAsync) |
73 testRunner.notifyDone(); | 88 testRunner.notifyDone(); |
| 89 } |
| 90 |
| 91 if (window.generateMinimumRepaint) { |
| 92 internals.forceFullRepaint(document); |
| 93 testRunner.capturePixelsAsyncThen(function(width, height, snapshot) { |
| 94 if (window.outputRepaintRects) { |
| 95 var minimumRepaint = computeMinimumRepaint(width, height, snapsh
ot); |
| 96 if (minimumRepaint.length) |
| 97 repaintRects += '\nMinimum repaint:\n' + JSON.stringify(mini
mumRepaint, null, 2); |
| 98 } |
| 99 repaintTestDone(); |
| 100 }); |
| 101 } else { |
| 102 repaintTestDone(); |
| 103 } |
74 } | 104 } |
| 105 |
| 106 // Minimum repaint |
| 107 // |
| 108 // Definitions |
| 109 // - minimum-repaint = region(diff(snapshot-before-repaintTest, |
| 110 // snapshot-after-repaintTest-and-full-repaint)) |
| 111 // It includes all pixels that should be changed after repaintTest. |
| 112 // - actual-repaint = repaint rects recorded during repaintTest() and |
| 113 // forceStyleRecalc(). |
| 114 // - potential-under-repaint = subtract(minimum-repaint, actual-repaint) |
| 115 // Potential-under-repaint will be shown in layout test overlay in black if |
| 116 // any minimum-repaint region is not covered by actual-repaint. |
| 117 // |
| 118 // Potential-under-repaints don't always mean bug: |
| 119 // - Some know visualization issues (crbug.com/381221) may cause false |
| 120 // under-repaint; |
| 121 // - Screen updates caused by composited layer re-compositing may not need |
| 122 // repaint. |
| 123 // |
| 124 // How to use |
| 125 // 1. Set window.generateMinimumRepaint to true in some repaint test or change |
| 126 // this script to force window.generateMinimumRepaint to true. |
| 127 // 2. Run layout tests. Repaint tests will result text diffs, which is because |
| 128 // of the minimum repaint output in the actual results and doesn't mean the |
| 129 // tests failed. |
| 130 // 3. In layout test result page, click 'overlay' link or expand the test to |
| 131 // see the 'overlay' pane. |
| 132 // 4. Click 'Highlight under-repaint' button. You'll see black region if there |
| 133 // is any under-repaint. |
| 134 // |
| 135 // Known issues |
| 136 // crbug.com/381221 |
| 137 |
| 138 function computeMinimumRepaint(width, height, snapshot) |
| 139 { |
| 140 var result = []; |
| 141 if (width > widthBeforeRepaint) { |
| 142 result.push([widthBeforeRepaint, 0, width - widthBeforeRepaint, Math.max
(height, heightBeforeRepaint)]); |
| 143 width = widthBeforeRepaint; |
| 144 } |
| 145 if (height > heightBeforeRepaint) { |
| 146 result.push([0, heightBeforeRepaint, width, height - heightBeforeRepaint
]); |
| 147 height = heightBeforeRepaint; |
| 148 } |
| 149 |
| 150 var dataBefore = new Uint32Array(snapshotBeforeRepaint); |
| 151 var dataAfter = new Uint32Array(snapshot); |
| 152 var rectsMayContinue = []; |
| 153 var index = 0; |
| 154 for (var y = 0; y < height; ++y) { |
| 155 var x = 0; |
| 156 var rectsMayContinueIndex = 0; |
| 157 var nextRectsMayContinue = []; |
| 158 while (true) { |
| 159 while (x < width && dataBefore[index] == dataAfter[index]) { |
| 160 ++x; |
| 161 ++index; |
| 162 } |
| 163 xBegin = x; |
| 164 while (x < width && dataBefore[index] != dataAfter[index]) { |
| 165 ++x; |
| 166 ++index; |
| 167 } |
| 168 xEnd = x; |
| 169 |
| 170 var xWidth = xEnd - xBegin; |
| 171 if (!xWidth) |
| 172 break; |
| 173 |
| 174 var rectMayContinue = rectsMayContinue[rectsMayContinueIndex]; |
| 175 while (rectMayContinue && rectMayContinue[0] < xBegin) |
| 176 rectMayContinue = rectsMayContinue[++rectsMayContinueIndex]; |
| 177 |
| 178 if (rectMayContinue && rectMayContinue[0] == xBegin && rectMayContin
ue[2] == xWidth) { |
| 179 ++rectMayContinue[3]; |
| 180 nextRectsMayContinue.push(rectMayContinue); |
| 181 } else { |
| 182 var newRect = [xBegin, y, xWidth, 1]; |
| 183 nextRectsMayContinue.push(newRect); |
| 184 result.push(newRect); |
| 185 } |
| 186 } |
| 187 rectsMayContinue = nextRectsMayContinue; |
| 188 } |
| 189 return result; |
| 190 } |
OLD | NEW |