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