1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.content.browser; 6 7 import android.content.ComponentName; 8 9 import org.junit.Assert; 10 import org.junit.Test; 11 import org.junit.runner.RunWith; 12 import org.robolectric.annotation.Config; 13 import org.robolectric.shadows.ShadowLooper; 14 15 import org.chromium.base.process_launcher.ChildProcessConnection; 16 import org.chromium.base.test.BaseRobolectricTestRunner; 17 import org.chromium.base.test.TestChildProcessConnection; 18 import org.chromium.content_public.browser.ChildProcessImportance; 19 20 /** Unit tests for ChildProessRanking */ 21 @RunWith(BaseRobolectricTestRunner.class) 22 @Config(manifest = Config.NONE) 23 public class ChildProcessRankingTest { createConnection()24 private TestChildProcessConnection createConnection() { 25 TestChildProcessConnection connection = new TestChildProcessConnection( 26 new ComponentName("pkg", "cls"), false /* bindToCallerCheck */, 27 false /* bindAsExternalService */, null /* serviceBundle */); 28 connection.start(false /* useStrongBinding */, null /* serviceCallback */); 29 return connection; 30 } 31 assertRankingAndRemoveAll( ChildProcessRanking ranking, ChildProcessConnection[] connections)32 private void assertRankingAndRemoveAll( 33 ChildProcessRanking ranking, ChildProcessConnection[] connections) { 34 int index = connections.length; 35 ChildProcessConnection reverseIterationArray[] = 36 new ChildProcessConnection[connections.length]; 37 for (ChildProcessConnection c : ranking) { 38 reverseIterationArray[--index] = c; 39 } 40 Assert.assertArrayEquals(connections, reverseIterationArray); 41 Assert.assertEquals(0, index); 42 43 index = connections.length; 44 ChildProcessConnection reverseRemoveArray[] = 45 new ChildProcessConnection[connections.length]; 46 for (int i = 0; i < connections.length; ++i) { 47 ChildProcessConnection c = ranking.getLowestRankedConnection(); 48 reverseRemoveArray[--index] = c; 49 ranking.removeConnection(c); 50 } 51 Assert.assertArrayEquals(connections, reverseRemoveArray); 52 Assert.assertNull(ranking.getLowestRankedConnection()); 53 } 54 assertNotInGroup(ChildProcessConnection[] connections)55 private void assertNotInGroup(ChildProcessConnection[] connections) { 56 for (ChildProcessConnection c : connections) { 57 Assert.assertEquals(0, c.getGroup()); 58 } 59 } 60 assertInGroupOrderedByImportance(ChildProcessConnection[] connections)61 private void assertInGroupOrderedByImportance(ChildProcessConnection[] connections) { 62 int importanceSoFar = -1; 63 for (ChildProcessConnection c : connections) { 64 Assert.assertTrue(c.getGroup() > 0); 65 Assert.assertTrue(c.getImportanceInGroup() > importanceSoFar); 66 importanceSoFar = c.getImportanceInGroup(); 67 } 68 } 69 70 @Test testRanking()71 public void testRanking() { 72 ChildProcessRanking ranking = new ChildProcessRanking(10); 73 doTestRanking(ranking, false); 74 } 75 76 @Test testRankingWithoutLimit()77 public void testRankingWithoutLimit() { 78 ChildProcessRanking ranking = new ChildProcessRanking(); 79 doTestRanking(ranking, false); 80 } 81 82 @Test testEnableGroupAfter()83 public void testEnableGroupAfter() { 84 ChildProcessRanking ranking = new ChildProcessRanking(); 85 doTestRanking(ranking, true); 86 } 87 doTestRanking(ChildProcessRanking ranking, boolean enableGroupImportanceAfter)88 private void doTestRanking(ChildProcessRanking ranking, boolean enableGroupImportanceAfter) { 89 if (!enableGroupImportanceAfter) ranking.enableServiceGroupImportance(); 90 91 ChildProcessConnection c1 = createConnection(); 92 ChildProcessConnection c2 = createConnection(); 93 ChildProcessConnection c3 = createConnection(); 94 ChildProcessConnection c4 = createConnection(); 95 ChildProcessConnection c5 = createConnection(); 96 ChildProcessConnection c6 = createConnection(); 97 ChildProcessConnection c7 = createConnection(); 98 ChildProcessConnection c8 = createConnection(); 99 ChildProcessConnection c9 = createConnection(); 100 ChildProcessConnection c10 = createConnection(); 101 102 103 // Insert in lowest ranked to highest ranked order. 104 105 // Invisible frame. 106 ranking.addConnection(c1, false /* foreground */, 0 /* frameDepth */, 107 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 108 109 // Visible subframe outside viewport. 110 ranking.addConnection(c2, true /* foreground */, 2 /* frameDepth */, 111 false /* intersectsViewport */, ChildProcessImportance.NORMAL); 112 ranking.addConnection(c3, true /* foreground */, 1 /* frameDepth */, 113 false /* intersectsViewport */, ChildProcessImportance.NORMAL); 114 115 // Visible subframe inside viewport. 116 ranking.addConnection(c4, true /* foreground */, 2 /* frameDepth */, 117 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 118 ranking.addConnection(c5, true /* foreground */, 1 /* frameDepth */, 119 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 120 121 // Visible main frame. 122 ranking.addConnection(c6, true /* foreground */, 0 /* frameDepth */, 123 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 124 125 if (enableGroupImportanceAfter) { 126 assertNotInGroup(new ChildProcessConnection[] {c6, c5, c4, c3, c2, c1}); 127 ranking.enableServiceGroupImportance(); 128 } 129 130 assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c6, c5, c4, c3, c2, c1}); 131 132 assertNotInGroup(new ChildProcessConnection[] {c6, c5, c4}); 133 assertInGroupOrderedByImportance(new ChildProcessConnection[] {c3, c2, c1}); 134 } 135 136 @Test testRankingWithImportance()137 public void testRankingWithImportance() { 138 ChildProcessConnection c1 = createConnection(); 139 ChildProcessConnection c2 = createConnection(); 140 ChildProcessConnection c3 = createConnection(); 141 ChildProcessConnection c4 = createConnection(); 142 143 ChildProcessRanking ranking = new ChildProcessRanking(4); 144 ranking.enableServiceGroupImportance(); 145 146 // Insert in lowest ranked to highest ranked order. 147 ranking.addConnection(c1, false /* foreground */, 0 /* frameDepth */, 148 false /* intersectsViewport */, ChildProcessImportance.NORMAL); 149 ranking.addConnection(c2, false /* foreground */, 0 /* frameDepth */, 150 false /* intersectsViewport */, ChildProcessImportance.MODERATE); 151 ranking.addConnection(c3, false /* foreground */, 1 /* frameDepth */, 152 false /* intersectsViewport */, ChildProcessImportance.IMPORTANT); 153 ranking.addConnection(c4, false /* foreground */, 0 /* frameDepth */, 154 false /* intersectsViewport */, ChildProcessImportance.IMPORTANT); 155 156 assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c4, c3, c2, c1}); 157 assertNotInGroup(new ChildProcessConnection[] {c4, c3, c2}); 158 assertInGroupOrderedByImportance(new ChildProcessConnection[] {c1}); 159 } 160 161 @Test testUpdate()162 public void testUpdate() { 163 ChildProcessConnection c1 = createConnection(); 164 ChildProcessConnection c2 = createConnection(); 165 ChildProcessConnection c3 = createConnection(); 166 ChildProcessConnection c4 = createConnection(); 167 168 ChildProcessRanking ranking = new ChildProcessRanking(4); 169 ranking.enableServiceGroupImportance(); 170 171 // c1,2 are in one tab, and c3,4 are in second tab. 172 ranking.addConnection(c1, true /* foreground */, 1 /* frameDepth */, 173 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 174 ranking.addConnection(c2, true /* foreground */, 0 /* frameDepth */, 175 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 176 ranking.addConnection(c3, false /* foreground */, 1 /* frameDepth */, 177 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 178 ranking.addConnection(c4, false /* foreground */, 0 /* frameDepth */, 179 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 180 Assert.assertEquals(c3, ranking.getLowestRankedConnection()); 181 182 // Switch from tab c1,2 to tab c3,c4. 183 ranking.updateConnection(c1, false /* foreground */, 1 /* frameDepth */, 184 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 185 ranking.updateConnection(c2, false /* foreground */, 0 /* frameDepth */, 186 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 187 ranking.updateConnection(c3, true /* foreground */, 1 /* frameDepth */, 188 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 189 ranking.updateConnection(c4, true /* foreground */, 0 /* frameDepth */, 190 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 191 192 assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c4, c3, c2, c1}); 193 assertNotInGroup(new ChildProcessConnection[] {c4, c3}); 194 assertInGroupOrderedByImportance(new ChildProcessConnection[] {c2, c1}); 195 } 196 197 @Test testIntersectsViewport()198 public void testIntersectsViewport() { 199 ChildProcessConnection c1 = createConnection(); 200 ChildProcessConnection c2 = createConnection(); 201 ChildProcessConnection c3 = createConnection(); 202 203 ChildProcessRanking ranking = new ChildProcessRanking(4); 204 ranking.enableServiceGroupImportance(); 205 206 // Insert in lowest ranked to highest ranked order. 207 ranking.addConnection(c1, true /* foreground */, 1 /* frameDepth */, 208 false /* intersectsViewport */, ChildProcessImportance.NORMAL); 209 ranking.addConnection(c2, true /* foreground */, 1 /* frameDepth */, 210 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 211 ranking.addConnection(c3, true /* foreground */, 0 /* frameDepth */, 212 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 213 214 assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c3, c2, c1}); 215 assertNotInGroup(new ChildProcessConnection[] {c3, c2}); 216 assertInGroupOrderedByImportance(new ChildProcessConnection[] {c1}); 217 } 218 219 @Test testFrameDepthIntOverflow()220 public void testFrameDepthIntOverflow() { 221 ChildProcessConnection c1 = createConnection(); 222 ChildProcessConnection c2 = createConnection(); 223 ChildProcessConnection c3 = createConnection(); 224 ChildProcessRanking ranking = new ChildProcessRanking(); 225 226 // Native can pass up the maximum value of unsigned int. 227 long intOverflow = ((long) Integer.MAX_VALUE) * 2; 228 ranking.addConnection(c3, true /* foreground */, intOverflow - 1 /* frameDepth */, 229 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 230 ranking.addConnection(c2, true /* foreground */, 10 /* frameDepth */, 231 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 232 ranking.addConnection(c1, true /* foreground */, intOverflow /* frameDepth */, 233 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 234 235 assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c2, c3, c1}); 236 } 237 238 @Test testThrowExceptionWhenGoingOverLimit()239 public void testThrowExceptionWhenGoingOverLimit() { 240 ChildProcessRanking ranking = new ChildProcessRanking(2); 241 242 ChildProcessConnection c1 = createConnection(); 243 ChildProcessConnection c2 = createConnection(); 244 ChildProcessConnection c3 = createConnection(); 245 246 ranking.addConnection(c1, true /* foreground */, 1 /* frameDepth */, 247 false /* intersectsViewport */, ChildProcessImportance.NORMAL); 248 ranking.addConnection(c2, true /* foreground */, 1 /* frameDepth */, 249 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 250 boolean exceptionThrown = false; 251 try { 252 ranking.addConnection(c3, true /* foreground */, 1 /* frameDepth */, 253 true /* intersectsViewport */, ChildProcessImportance.NORMAL); 254 } catch (Throwable e) { 255 exceptionThrown = true; 256 } 257 Assert.assertTrue(exceptionThrown); 258 } 259 260 @Test testRebindHighRankConnection()261 public void testRebindHighRankConnection() { 262 ChildProcessRanking ranking = new ChildProcessRanking(); 263 ranking.enableServiceGroupImportance(); 264 265 TestChildProcessConnection c1 = createConnection(); 266 TestChildProcessConnection c2 = createConnection(); 267 TestChildProcessConnection c3 = createConnection(); 268 269 ranking.addConnection(c1, true /* foreground */, 0 /* frameDepth */, 270 false /* intersectsViewport */, ChildProcessImportance.IMPORTANT); 271 ranking.addConnection(c2, true /* foreground */, 2 /* frameDepth */, 272 false /* intersectsViewport */, ChildProcessImportance.NORMAL); 273 ranking.addConnection(c3, true /* foreground */, 3 /* frameDepth */, 274 false /* intersectsViewport */, ChildProcessImportance.NORMAL); 275 276 assertNotInGroup(new ChildProcessConnection[] {c1}); 277 assertInGroupOrderedByImportance(new ChildProcessConnection[] {c2, c3}); 278 279 c1.getAndResetRebindCalled(); 280 ranking.updateConnection(c3, true /* foreground */, 1 /* frameDepth */, 281 false /* intersectsViewport */, ChildProcessImportance.NORMAL); 282 assertNotInGroup(new ChildProcessConnection[] {c1}); 283 assertInGroupOrderedByImportance(new ChildProcessConnection[] {c3, c2}); 284 Assert.assertFalse(c1.getAndResetRebindCalled()); 285 286 ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); 287 Assert.assertTrue(c1.getAndResetRebindCalled()); 288 } 289 } 290