BrowserStartupControllerTest.java revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
1// Copyright 2013 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 5package org.chromium.content.browser; 6 7import android.content.Context; 8import android.test.InstrumentationTestCase; 9import android.test.suitebuilder.annotation.SmallTest; 10 11import org.chromium.base.ThreadUtils; 12import org.chromium.base.test.util.AdvancedMockContext; 13import org.chromium.content.common.ProcessInitException; 14import org.chromium.content.common.ResultCodes; 15 16public class BrowserStartupControllerTest extends InstrumentationTestCase { 17 18 private TestBrowserStartupController mController; 19 20 private static class TestBrowserStartupController extends BrowserStartupController { 21 22 private int mStartupResult; 23 private boolean mLibraryLoadSucceeds; 24 private int mInitializedCounter = 0; 25 26 @Override 27 void prepareToStartBrowserProcess(int numRenderers) throws ProcessInitException { 28 if (!mLibraryLoadSucceeds) { 29 throw new ProcessInitException(ResultCodes.RESULT_CODE_NATIVE_LIBRARY_LOAD_FAILED); 30 } 31 } 32 33 private TestBrowserStartupController(Context context) { 34 super(context); 35 } 36 37 @Override 38 int contentStart() { 39 mInitializedCounter++; 40 if(BrowserStartupController.browserMayStartAsynchonously()) { 41 // Post to the UI thread to emulate what would happen in a real scenario. 42 ThreadUtils.postOnUiThread(new Runnable() { 43 @Override 44 public void run() { 45 BrowserStartupController.browserStartupComplete(mStartupResult); 46 } 47 }); 48 } else { 49 BrowserStartupController.browserStartupComplete(mStartupResult); 50 } 51 return mStartupResult; 52 } 53 54 private int initializedCounter() { 55 return mInitializedCounter; 56 } 57 } 58 59 private static class TestStartupCallback implements BrowserStartupController.StartupCallback { 60 private boolean mWasSuccess; 61 private boolean mWasFailure; 62 private boolean mHasStartupResult; 63 private boolean mAlreadyStarted; 64 65 @Override 66 public void onSuccess(boolean alreadyStarted) { 67 assert !mHasStartupResult; 68 mWasSuccess = true; 69 mAlreadyStarted = alreadyStarted; 70 mHasStartupResult = true; 71 } 72 73 @Override 74 public void onFailure() { 75 assert !mHasStartupResult; 76 mWasFailure = true; 77 mHasStartupResult = true; 78 } 79 } 80 81 @Override 82 protected void setUp() throws Exception { 83 super.setUp(); 84 Context context = new AdvancedMockContext(getInstrumentation().getTargetContext()); 85 mController = new TestBrowserStartupController(context); 86 // Setting the static singleton instance field enables more correct testing, since it is 87 // is possible to call {@link BrowserStartupController#browserStartupComplete(int)} instead 88 // of {@link BrowserStartupController#executeEnqueuedCallbacks(int, boolean)} directly. 89 BrowserStartupController.overrideInstanceForTest(mController); 90 } 91 92 @SmallTest 93 public void testSingleAsynchronousStartupRequest() { 94 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 95 mController.mLibraryLoadSucceeds = true; 96 final TestStartupCallback callback = new TestStartupCallback(); 97 98 // Kick off the asynchronous startup request. 99 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 100 @Override 101 public void run() { 102 mController.startBrowserProcessesAsync(callback); 103 } 104 }); 105 106 assertTrue("Asynchronous mode should have been set.", 107 BrowserStartupController.browserMayStartAsynchonously()); 108 assertEquals("The browser process should have been initialized one time.", 109 1, mController.initializedCounter()); 110 111 // Wait for callbacks to complete. 112 getInstrumentation().waitForIdleSync(); 113 114 assertTrue("Callback should have been executed.", callback.mHasStartupResult); 115 assertTrue("Callback should have been a success.", callback.mWasSuccess); 116 assertFalse("Callback should be told that the browser process was not already started.", 117 callback.mAlreadyStarted); 118 } 119 120 @SmallTest 121 public void testMultipleAsynchronousStartupRequests() { 122 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 123 mController.mLibraryLoadSucceeds = true; 124 final TestStartupCallback callback1 = new TestStartupCallback(); 125 final TestStartupCallback callback2 = new TestStartupCallback(); 126 final TestStartupCallback callback3 = new TestStartupCallback(); 127 128 // Kick off the asynchronous startup requests. 129 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 130 @Override 131 public void run() { 132 mController.startBrowserProcessesAsync(callback1); 133 } 134 }); 135 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 136 @Override 137 public void run() { 138 mController.startBrowserProcessesAsync(callback2); 139 } 140 }); 141 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 142 @Override 143 public void run() { 144 mController.addStartupCompletedObserver(callback3); 145 } 146 }); 147 148 assertTrue("Asynchronous mode should have been set.", 149 BrowserStartupController.browserMayStartAsynchonously()); 150 assertEquals("The browser process should have been initialized one time.", 151 1, mController.initializedCounter()); 152 153 // Wait for callbacks to complete. 154 getInstrumentation().waitForIdleSync(); 155 156 assertTrue("Callback 1 should have been executed.", callback1.mHasStartupResult); 157 assertTrue("Callback 1 should have been a success.", callback1.mWasSuccess); 158 assertTrue("Callback 2 should have been executed.", callback2.mHasStartupResult); 159 assertTrue("Callback 2 should have been a success.", callback2.mWasSuccess); 160 assertTrue("Callback 3 should have been executed.", callback3.mHasStartupResult); 161 assertTrue("Callback 3 should have been a success.", callback3.mWasSuccess); 162 // Some startup tasks might have been enqueued after the browser process was started, but 163 // not the first one which kicked of the startup. 164 assertFalse("Callback 1 should be told that the browser process was not already started.", 165 callback1.mAlreadyStarted); 166 } 167 168 @SmallTest 169 public void testConsecutiveAsynchronousStartupRequests() { 170 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 171 mController.mLibraryLoadSucceeds = true; 172 final TestStartupCallback callback1 = new TestStartupCallback(); 173 final TestStartupCallback callback2 = new TestStartupCallback(); 174 175 // Kick off the asynchronous startup requests. 176 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 177 @Override 178 public void run() { 179 mController.startBrowserProcessesAsync(callback1); 180 } 181 }); 182 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 183 @Override 184 public void run() { 185 mController.addStartupCompletedObserver(callback2); 186 } 187 }); 188 189 assertTrue("Asynchronous mode should have been set.", 190 BrowserStartupController.browserMayStartAsynchonously()); 191 assertEquals("The browser process should have been initialized one time.", 192 1, mController.initializedCounter()); 193 194 // Wait for callbacks to complete. 195 getInstrumentation().waitForIdleSync(); 196 197 assertTrue("Callback 1 should have been executed.", callback1.mHasStartupResult); 198 assertTrue("Callback 1 should have been a success.", callback1.mWasSuccess); 199 assertTrue("Callback 2 should have been executed.", callback2.mHasStartupResult); 200 assertTrue("Callback 2 should have been a success.", callback2.mWasSuccess); 201 202 final TestStartupCallback callback3 = new TestStartupCallback(); 203 final TestStartupCallback callback4 = new TestStartupCallback(); 204 205 // Kick off more asynchronous startup requests. 206 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 207 @Override 208 public void run() { 209 mController.startBrowserProcessesAsync(callback3); 210 } 211 }); 212 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 213 @Override 214 public void run() { 215 mController.addStartupCompletedObserver(callback4); 216 } 217 }); 218 219 // Wait for callbacks to complete. 220 getInstrumentation().waitForIdleSync(); 221 222 assertTrue("Callback 3 should have been executed.", callback3.mHasStartupResult); 223 assertTrue("Callback 3 should have been a success.", callback3.mWasSuccess); 224 assertTrue("Callback 3 should be told that the browser process was already started.", 225 callback3.mAlreadyStarted); 226 assertTrue("Callback 4 should have been executed.", callback4.mHasStartupResult); 227 assertTrue("Callback 4 should have been a success.", callback4.mWasSuccess); 228 assertTrue("Callback 4 should be told that the browser process was already started.", 229 callback4.mAlreadyStarted); 230 } 231 232 @SmallTest 233 public void testSingleFailedAsynchronousStartupRequest() { 234 mController.mStartupResult = BrowserStartupController.STARTUP_FAILURE; 235 mController.mLibraryLoadSucceeds = true; 236 final TestStartupCallback callback = new TestStartupCallback(); 237 238 // Kick off the asynchronous startup request. 239 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 240 @Override 241 public void run() { 242 mController.startBrowserProcessesAsync(callback); 243 } 244 }); 245 246 assertTrue("Asynchronous mode should have been set.", 247 BrowserStartupController.browserMayStartAsynchonously()); 248 assertEquals("The browser process should have been initialized one time.", 249 1, mController.initializedCounter()); 250 251 // Wait for callbacks to complete. 252 getInstrumentation().waitForIdleSync(); 253 254 assertTrue("Callback should have been executed.", callback.mHasStartupResult); 255 assertTrue("Callback should have been a failure.", callback.mWasFailure); 256 } 257 258 @SmallTest 259 public void testConsecutiveFailedAsynchronousStartupRequests() { 260 mController.mStartupResult = BrowserStartupController.STARTUP_FAILURE; 261 mController.mLibraryLoadSucceeds = true; 262 final TestStartupCallback callback1 = new TestStartupCallback(); 263 final TestStartupCallback callback2 = new TestStartupCallback(); 264 265 // Kick off the asynchronous startup requests. 266 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 267 @Override 268 public void run() { 269 mController.startBrowserProcessesAsync(callback1); 270 } 271 }); 272 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 273 @Override 274 public void run() { 275 mController.addStartupCompletedObserver(callback2); 276 } 277 }); 278 279 assertTrue("Asynchronous mode should have been set.", 280 BrowserStartupController.browserMayStartAsynchonously()); 281 assertEquals("The browser process should have been initialized one time.", 282 1, mController.initializedCounter()); 283 284 // Wait for callbacks to complete. 285 getInstrumentation().waitForIdleSync(); 286 287 assertTrue("Callback 1 should have been executed.", callback1.mHasStartupResult); 288 assertTrue("Callback 1 should have been a failure.", callback1.mWasFailure); 289 assertTrue("Callback 2 should have been executed.", callback2.mHasStartupResult); 290 assertTrue("Callback 2 should have been a failure.", callback2.mWasFailure); 291 292 final TestStartupCallback callback3 = new TestStartupCallback(); 293 final TestStartupCallback callback4 = new TestStartupCallback(); 294 295 // Kick off more asynchronous startup requests. 296 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 297 @Override 298 public void run() { 299 mController.startBrowserProcessesAsync(callback3); 300 } 301 }); 302 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 303 @Override 304 public void run() { 305 mController.addStartupCompletedObserver(callback4); 306 } 307 }); 308 309 // Wait for callbacks to complete. 310 getInstrumentation().waitForIdleSync(); 311 312 assertTrue("Callback 3 should have been executed.", callback3.mHasStartupResult); 313 assertTrue("Callback 3 should have been a failure.", callback3.mWasFailure); 314 assertTrue("Callback 4 should have been executed.", callback4.mHasStartupResult); 315 assertTrue("Callback 4 should have been a failure.", callback4.mWasFailure); 316 } 317 318 @SmallTest 319 public void testSingleSynchronousRequest() { 320 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 321 mController.mLibraryLoadSucceeds = true; 322 // Kick off the synchronous startup. 323 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 324 @Override 325 public void run() { 326 assertTrue("Browser should have started successfully", 327 mController.startBrowserProcessesSync(1)); 328 } 329 }); 330 assertFalse("Synchronous mode should have been set", 331 BrowserStartupController.browserMayStartAsynchonously()); 332 333 assertEquals("The browser process should have been initialized one time.", 334 1, mController.initializedCounter()); 335 } 336 337 @SmallTest 338 public void testAsyncThenSyncRequests() { 339 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 340 mController.mLibraryLoadSucceeds = true; 341 final TestStartupCallback callback = new TestStartupCallback(); 342 343 // Kick off the startups. 344 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 345 @Override 346 public void run() { 347 mController.startBrowserProcessesAsync(callback); 348 // To ensure that the async startup doesn't complete too soon we have 349 // to do both these in a since Runnable instance. This avoids the 350 // unpredictable race that happens in real situations. 351 assertTrue("Browser should have started successfully", 352 mController.startBrowserProcessesSync(1)); 353 } 354 }); 355 assertFalse("Synchronous mode should have been set", 356 BrowserStartupController.browserMayStartAsynchonously()); 357 358 assertEquals("The browser process should have been initialized twice.", 359 2, mController.initializedCounter()); 360 361 assertTrue("Callback should have been executed.", callback.mHasStartupResult); 362 assertTrue("Callback should have been a success.", callback.mWasSuccess); 363 assertFalse("Callback should be told that the browser process was not already started.", 364 callback.mAlreadyStarted); 365 } 366 367 @SmallTest 368 public void testSyncThenAsyncRequests() { 369 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 370 mController.mLibraryLoadSucceeds = true; 371 final TestStartupCallback callback = new TestStartupCallback(); 372 373 // Do a synchronous startup first. 374 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 375 @Override 376 public void run() { 377 assertTrue("Browser should have started successfully", 378 mController.startBrowserProcessesSync(1)); 379 } 380 }); 381 382 assertEquals("The browser process should have been initialized once.", 383 1, mController.initializedCounter()); 384 385 assertFalse("Synchronous mode should have been set", 386 BrowserStartupController.browserMayStartAsynchonously()); 387 388 // Kick off the asynchronous startup request. This should just queue the callback. 389 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 390 @Override 391 public void run() { 392 mController.startBrowserProcessesAsync(callback); 393 } 394 }); 395 396 assertEquals("The browser process should not have been initialized a second time.", 397 1, mController.initializedCounter()); 398 399 // Wait for callbacks to complete. 400 getInstrumentation().waitForIdleSync(); 401 402 assertTrue("Callback should have been executed.", callback.mHasStartupResult); 403 assertTrue("Callback should have been a success.", callback.mWasSuccess); 404 assertTrue("Callback should be told that the browser process was already started.", 405 callback.mAlreadyStarted); 406 } 407 408 @SmallTest 409 public void testLibraryLoadFails() { 410 mController.mLibraryLoadSucceeds = false; 411 final TestStartupCallback callback = new TestStartupCallback(); 412 413 // Kick off the asynchronous startup request. 414 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 415 @Override 416 public void run() { 417 mController.startBrowserProcessesAsync(callback); 418 } 419 }); 420 421 assertEquals("The browser process should not have been initialized.", 422 0, mController.initializedCounter()); 423 424 // Wait for callbacks to complete. 425 getInstrumentation().waitForIdleSync(); 426 427 assertTrue("Callback should have been executed.", callback.mHasStartupResult); 428 assertFalse("Callback should have been a failure.", callback.mWasSuccess); 429 assertFalse("Callback should be told that the browser process was not already started.", 430 callback.mAlreadyStarted); 431 } 432 433} 434