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 3 // found in the LICENSE file. 4 5 package org.chromium.content.browser; 6 7 import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; 8 import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; 9 import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; 10 11 import android.app.Activity; 12 import android.app.Application; 13 import android.content.ComponentName; 14 import android.util.Pair; 15 16 import org.junit.After; 17 import org.junit.Assert; 18 import org.junit.Before; 19 import org.junit.Test; 20 import org.junit.runner.RunWith; 21 import org.robolectric.Robolectric; 22 import org.robolectric.annotation.Config; 23 import org.robolectric.shadows.ShadowLooper; 24 25 import org.chromium.base.process_launcher.ChildProcessConnection; 26 import org.chromium.base.test.BaseRobolectricTestRunner; 27 import org.chromium.base.test.TestChildProcessConnection; 28 import org.chromium.base.test.util.Feature; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** 34 * Unit tests for BindingManager and ChildProcessConnection. 35 * 36 * Default property of being low-end device is overriden, so that both low-end and high-end policies 37 * are tested. 38 */ 39 @RunWith(BaseRobolectricTestRunner.class) 40 @Config(manifest = Config.NONE) 41 public class BindingManagerTest { 42 // Creates a mocked ChildProcessConnection that is optionally added to a BindingManager. createTestChildProcessConnection( int pid, BindingManager manager, List<ChildProcessConnection> iterable)43 private static ChildProcessConnection createTestChildProcessConnection( 44 int pid, BindingManager manager, List<ChildProcessConnection> iterable) { 45 TestChildProcessConnection connection = new TestChildProcessConnection( 46 new ComponentName("org.chromium.test", "TestService"), 47 false /* bindToCallerCheck */, false /* bindAsExternalService */, 48 null /* serviceBundle */); 49 connection.setPid(pid); 50 connection.start(false /* useStrongBinding */, null /* serviceCallback */); 51 manager.addConnection(connection); 52 iterable.add(connection); 53 connection.removeModerateBinding(); // Remove initial binding. 54 return connection; 55 } 56 57 Activity mActivity; 58 59 // Created in setUp() for convenience. 60 BindingManager mManager; 61 BindingManager mVariableManager; 62 63 List<ChildProcessConnection> mIterable; 64 65 @Before setUp()66 public void setUp() { 67 // The tests run on only one thread. Pretend that is the launcher thread so LauncherThread 68 // asserts are not triggered. 69 LauncherThread.setCurrentThreadAsLauncherThread(); 70 mActivity = Robolectric.buildActivity(Activity.class).setup().get(); 71 mIterable = new ArrayList<>(); 72 mManager = new BindingManager(mActivity, 4, mIterable); 73 mVariableManager = new BindingManager(mActivity, mIterable); 74 } 75 76 @After tearDown()77 public void tearDown() { 78 LauncherThread.setLauncherThreadAsLauncherThread(); 79 } 80 81 /** 82 * Verifies that onSentToBackground() drops all the moderate bindings after some delay, and 83 * onBroughtToForeground() doesn't recover them. 84 */ 85 @Test 86 @Feature({"ProcessManagement"}) testModerateBindingDropOnBackground()87 public void testModerateBindingDropOnBackground() { 88 doTestModerateBindingDropOnBackground(mManager); 89 } 90 91 @Test 92 @Feature({"ProcessManagement"}) testModerateBindingDropOnBackgroundWithVariableSize()93 public void testModerateBindingDropOnBackgroundWithVariableSize() { 94 doTestModerateBindingDropOnBackground(mVariableManager); 95 } 96 doTestModerateBindingDropOnBackground(BindingManager manager)97 private void doTestModerateBindingDropOnBackground(BindingManager manager) { 98 ChildProcessConnection[] connections = new ChildProcessConnection[3]; 99 for (int i = 0; i < connections.length; i++) { 100 connections[i] = createTestChildProcessConnection(i + 1 /* pid */, manager, mIterable); 101 } 102 103 // Verify that each connection has a moderate binding after binding and releasing a strong 104 // binding. 105 for (ChildProcessConnection connection : connections) { 106 Assert.assertTrue(connection.isModerateBindingBound()); 107 } 108 109 ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); 110 111 // Verify that leaving the application for a short time doesn't clear the moderate bindings. 112 manager.onSentToBackground(); 113 for (ChildProcessConnection connection : connections) { 114 Assert.assertTrue(connection.isModerateBindingBound()); 115 } 116 manager.onBroughtToForeground(); 117 ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); 118 for (ChildProcessConnection connection : connections) { 119 Assert.assertTrue(connection.isModerateBindingBound()); 120 } 121 122 // Call onSentToBackground() and verify that all the moderate bindings drop after some 123 // delay. 124 manager.onSentToBackground(); 125 for (ChildProcessConnection connection : connections) { 126 Assert.assertTrue(connection.isModerateBindingBound()); 127 } 128 ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); 129 for (ChildProcessConnection connection : connections) { 130 Assert.assertFalse(connection.isModerateBindingBound()); 131 } 132 133 // Call onBroughtToForeground() and verify that the previous moderate bindings aren't 134 // recovered. 135 manager.onBroughtToForeground(); 136 for (ChildProcessConnection connection : connections) { 137 Assert.assertFalse(connection.isModerateBindingBound()); 138 } 139 } 140 141 /** 142 * Verifies that onLowMemory() drops all the moderate bindings. 143 */ 144 @Test 145 @Feature({"ProcessManagement"}) testModerateBindingDropOnLowMemory()146 public void testModerateBindingDropOnLowMemory() { 147 doTestModerateBindingDropOnLowMemory(mManager); 148 } 149 150 @Test 151 @Feature({"ProcessManagement"}) testModerateBindingDropOnLowMemoryVariableSize()152 public void testModerateBindingDropOnLowMemoryVariableSize() { 153 doTestModerateBindingDropOnLowMemory(mVariableManager); 154 } 155 doTestModerateBindingDropOnLowMemory(BindingManager manager)156 private void doTestModerateBindingDropOnLowMemory(BindingManager manager) { 157 final Application app = mActivity.getApplication(); 158 159 ChildProcessConnection[] connections = new ChildProcessConnection[4]; 160 for (int i = 0; i < connections.length; i++) { 161 connections[i] = createTestChildProcessConnection(i + 1 /* pid */, manager, mIterable); 162 } 163 164 for (ChildProcessConnection connection : connections) { 165 Assert.assertTrue(connection.isModerateBindingBound()); 166 } 167 168 // Call onLowMemory() and verify that all the moderate bindings drop. 169 app.onLowMemory(); 170 for (ChildProcessConnection connection : connections) { 171 Assert.assertFalse(connection.isModerateBindingBound()); 172 } 173 } 174 175 /** 176 * Verifies that onTrimMemory() drops moderate bindings properly. 177 */ 178 @Test 179 @Feature({"ProcessManagement"}) testModerateBindingDropOnTrimMemory()180 public void testModerateBindingDropOnTrimMemory() { 181 doTestModerateBindingDropOnTrimMemory(mManager); 182 } 183 184 @Test 185 @Feature({"ProcessManagement"}) testModerateBindingDropOnTrimMemoryWithVariableSize()186 public void testModerateBindingDropOnTrimMemoryWithVariableSize() { 187 doTestModerateBindingDropOnTrimMemory(mVariableManager); 188 } 189 doTestModerateBindingDropOnTrimMemory(BindingManager manager)190 private void doTestModerateBindingDropOnTrimMemory(BindingManager manager) { 191 final Application app = mActivity.getApplication(); 192 // This test applies only to the moderate-binding manager. 193 194 ArrayList<Pair<Integer, Integer>> levelAndExpectedVictimCountList = new ArrayList<>(); 195 levelAndExpectedVictimCountList.add( 196 new Pair<Integer, Integer>(TRIM_MEMORY_RUNNING_MODERATE, 1)); 197 levelAndExpectedVictimCountList.add(new Pair<Integer, Integer>(TRIM_MEMORY_RUNNING_LOW, 2)); 198 levelAndExpectedVictimCountList.add( 199 new Pair<Integer, Integer>(TRIM_MEMORY_RUNNING_CRITICAL, 4)); 200 201 ChildProcessConnection[] connections = new ChildProcessConnection[4]; 202 for (int i = 0; i < connections.length; i++) { 203 connections[i] = createTestChildProcessConnection(i + 1 /* pid */, manager, mIterable); 204 } 205 206 for (Pair<Integer, Integer> pair : levelAndExpectedVictimCountList) { 207 String message = "Failed for the level=" + pair.first; 208 mIterable.clear(); 209 // Verify that each connection has a moderate binding after binding and releasing a 210 // strong binding. 211 for (ChildProcessConnection connection : connections) { 212 manager.addConnection(connection); 213 mIterable.add(connection); 214 Assert.assertTrue(message, connection.isModerateBindingBound()); 215 } 216 217 app.onTrimMemory(pair.first); 218 // Verify that some of the moderate bindings have been dropped. 219 for (int i = 0; i < connections.length; i++) { 220 Assert.assertEquals( 221 message, i >= pair.second, connections[i].isModerateBindingBound()); 222 } 223 } 224 } 225 226 /* 227 * Test that Chrome is sent to the background, that the initially added moderate bindings are 228 * removed and are not re-added when Chrome is brought back to the foreground. 229 */ 230 @Test 231 @Feature({"ProcessManagement"}) testModerateBindingTillBackgroundedSentToBackground()232 public void testModerateBindingTillBackgroundedSentToBackground() { 233 doTestModerateBindingTillBackgroundedSentToBackground(mManager); 234 } 235 236 @Test 237 @Feature({"ProcessManagement"}) testModerateBindingTillBackgroundedSentToBackgroundWithVariableSize()238 public void testModerateBindingTillBackgroundedSentToBackgroundWithVariableSize() { 239 doTestModerateBindingTillBackgroundedSentToBackground(mVariableManager); 240 } 241 doTestModerateBindingTillBackgroundedSentToBackground(BindingManager manager)242 private void doTestModerateBindingTillBackgroundedSentToBackground(BindingManager manager) { 243 ChildProcessConnection connection = createTestChildProcessConnection(0, manager, mIterable); 244 Assert.assertTrue(connection.isModerateBindingBound()); 245 246 manager.onSentToBackground(); 247 ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); 248 Assert.assertFalse(connection.isModerateBindingBound()); 249 250 // Bringing Chrome to the foreground should not re-add the moderate bindings. 251 manager.onBroughtToForeground(); 252 Assert.assertFalse(connection.isModerateBindingBound()); 253 } 254 255 @Test 256 @Feature({"ProcessManagement"}) testOneWaivedConnection()257 public void testOneWaivedConnection() { 258 testOneWaivedConnection(mManager); 259 } 260 261 @Test 262 @Feature({"ProcessManagement"}) testOneWaivedConnectionWithVariableSize()263 public void testOneWaivedConnectionWithVariableSize() { 264 testOneWaivedConnection(mVariableManager); 265 } 266 testOneWaivedConnection(BindingManager manager)267 private void testOneWaivedConnection(BindingManager manager) { 268 ChildProcessConnection[] connections = new ChildProcessConnection[3]; 269 for (int i = 0; i < connections.length; i++) { 270 connections[i] = createTestChildProcessConnection(i + 1 /* pid */, manager, mIterable); 271 } 272 273 // Make sure binding is added for all connections. 274 for (ChildProcessConnection c : connections) { 275 Assert.assertTrue(c.isModerateBindingBound()); 276 } 277 278 // Move middle connection to be the first (ie lowest ranked). 279 mIterable.set(0, connections[1]); 280 mIterable.set(1, connections[0]); 281 manager.rankingChanged(); 282 Assert.assertTrue(connections[0].isModerateBindingBound()); 283 Assert.assertFalse(connections[1].isModerateBindingBound()); 284 Assert.assertTrue(connections[2].isModerateBindingBound()); 285 286 // Swap back. 287 mIterable.set(0, connections[0]); 288 mIterable.set(1, connections[1]); 289 manager.rankingChanged(); 290 Assert.assertFalse(connections[0].isModerateBindingBound()); 291 Assert.assertTrue(connections[1].isModerateBindingBound()); 292 Assert.assertTrue(connections[2].isModerateBindingBound()); 293 294 manager.removeConnection(connections[1]); 295 Assert.assertFalse(connections[0].isModerateBindingBound()); 296 Assert.assertFalse(connections[1].isModerateBindingBound()); 297 Assert.assertTrue(connections[2].isModerateBindingBound()); 298 299 manager.removeConnection(connections[0]); 300 Assert.assertFalse(connections[0].isModerateBindingBound()); 301 Assert.assertFalse(connections[1].isModerateBindingBound()); 302 Assert.assertTrue(connections[2].isModerateBindingBound()); 303 } 304 } 305