BrowserStartupControllerTest.java revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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 try { 103 mController.startBrowserProcessesAsync(callback); 104 } catch (Exception e) { 105 fail("Browser should have started successfully"); 106 } 107 } 108 }); 109 110 assertTrue("Asynchronous mode should have been set.", 111 BrowserStartupController.browserMayStartAsynchonously()); 112 assertEquals("The browser process should have been initialized one time.", 1, 113 mController.initializedCounter()); 114 115 // Wait for callbacks to complete. 116 getInstrumentation().waitForIdleSync(); 117 118 assertTrue("Callback should have been executed.", callback.mHasStartupResult); 119 assertTrue("Callback should have been a success.", callback.mWasSuccess); 120 assertFalse("Callback should be told that the browser process was not already started.", 121 callback.mAlreadyStarted); 122 } 123 124 @SmallTest 125 public void testMultipleAsynchronousStartupRequests() { 126 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 127 mController.mLibraryLoadSucceeds = true; 128 final TestStartupCallback callback1 = new TestStartupCallback(); 129 final TestStartupCallback callback2 = new TestStartupCallback(); 130 final TestStartupCallback callback3 = new TestStartupCallback(); 131 132 // Kick off the asynchronous startup requests. 133 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 134 @Override 135 public void run() { 136 try { 137 mController.startBrowserProcessesAsync(callback1); 138 } catch (Exception e) { 139 fail("Browser should have started successfully"); 140 } 141 } 142 }); 143 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 144 @Override 145 public void run() { 146 try { 147 mController.startBrowserProcessesAsync(callback2); 148 } catch (Exception e) { 149 fail("Browser should have started successfully"); 150 } 151 } 152 }); 153 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 154 @Override 155 public void run() { 156 mController.addStartupCompletedObserver(callback3); 157 } 158 }); 159 160 assertTrue("Asynchronous mode should have been set.", 161 BrowserStartupController.browserMayStartAsynchonously()); 162 assertEquals("The browser process should have been initialized one time.", 1, 163 mController.initializedCounter()); 164 165 // Wait for callbacks to complete. 166 getInstrumentation().waitForIdleSync(); 167 168 assertTrue("Callback 1 should have been executed.", callback1.mHasStartupResult); 169 assertTrue("Callback 1 should have been a success.", callback1.mWasSuccess); 170 assertTrue("Callback 2 should have been executed.", callback2.mHasStartupResult); 171 assertTrue("Callback 2 should have been a success.", callback2.mWasSuccess); 172 assertTrue("Callback 3 should have been executed.", callback3.mHasStartupResult); 173 assertTrue("Callback 3 should have been a success.", callback3.mWasSuccess); 174 // Some startup tasks might have been enqueued after the browser process was started, but 175 // not the first one which kicked of the startup. 176 assertFalse("Callback 1 should be told that the browser process was not already started.", 177 callback1.mAlreadyStarted); 178 } 179 180 @SmallTest 181 public void testConsecutiveAsynchronousStartupRequests() { 182 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 183 mController.mLibraryLoadSucceeds = true; 184 final TestStartupCallback callback1 = new TestStartupCallback(); 185 final TestStartupCallback callback2 = new TestStartupCallback(); 186 187 // Kick off the asynchronous startup requests. 188 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 189 @Override 190 public void run() { 191 try { 192 mController.startBrowserProcessesAsync(callback1); 193 } catch (Exception e) { 194 fail("Browser should have started successfully"); 195 } 196 } 197 }); 198 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 199 @Override 200 public void run() { 201 mController.addStartupCompletedObserver(callback2); 202 } 203 }); 204 205 assertTrue("Asynchronous mode should have been set.", 206 BrowserStartupController.browserMayStartAsynchonously()); 207 assertEquals("The browser process should have been initialized one time.", 1, 208 mController.initializedCounter()); 209 210 // Wait for callbacks to complete. 211 getInstrumentation().waitForIdleSync(); 212 213 assertTrue("Callback 1 should have been executed.", callback1.mHasStartupResult); 214 assertTrue("Callback 1 should have been a success.", callback1.mWasSuccess); 215 assertTrue("Callback 2 should have been executed.", callback2.mHasStartupResult); 216 assertTrue("Callback 2 should have been a success.", callback2.mWasSuccess); 217 218 final TestStartupCallback callback3 = new TestStartupCallback(); 219 final TestStartupCallback callback4 = new TestStartupCallback(); 220 221 // Kick off more asynchronous startup requests. 222 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 223 @Override 224 public void run() { 225 try { 226 mController.startBrowserProcessesAsync(callback3); 227 } catch (Exception e) { 228 fail("Browser should have started successfully"); 229 } 230 } 231 }); 232 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 233 @Override 234 public void run() { 235 mController.addStartupCompletedObserver(callback4); 236 } 237 }); 238 239 // Wait for callbacks to complete. 240 getInstrumentation().waitForIdleSync(); 241 242 assertTrue("Callback 3 should have been executed.", callback3.mHasStartupResult); 243 assertTrue("Callback 3 should have been a success.", callback3.mWasSuccess); 244 assertTrue("Callback 3 should be told that the browser process was already started.", 245 callback3.mAlreadyStarted); 246 assertTrue("Callback 4 should have been executed.", callback4.mHasStartupResult); 247 assertTrue("Callback 4 should have been a success.", callback4.mWasSuccess); 248 assertTrue("Callback 4 should be told that the browser process was already started.", 249 callback4.mAlreadyStarted); 250 } 251 252 @SmallTest 253 public void testSingleFailedAsynchronousStartupRequest() { 254 mController.mStartupResult = BrowserStartupController.STARTUP_FAILURE; 255 mController.mLibraryLoadSucceeds = true; 256 final TestStartupCallback callback = new TestStartupCallback(); 257 258 // Kick off the asynchronous startup request. 259 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 260 @Override 261 public void run() { 262 try { 263 mController.startBrowserProcessesAsync(callback); 264 } catch (Exception e) { 265 fail("Browser should have started successfully"); 266 } 267 } 268 }); 269 270 assertTrue("Asynchronous mode should have been set.", 271 BrowserStartupController.browserMayStartAsynchonously()); 272 assertEquals("The browser process should have been initialized one time.", 1, 273 mController.initializedCounter()); 274 275 // Wait for callbacks to complete. 276 getInstrumentation().waitForIdleSync(); 277 278 assertTrue("Callback should have been executed.", callback.mHasStartupResult); 279 assertTrue("Callback should have been a failure.", callback.mWasFailure); 280 } 281 282 @SmallTest 283 public void testConsecutiveFailedAsynchronousStartupRequests() { 284 mController.mStartupResult = BrowserStartupController.STARTUP_FAILURE; 285 mController.mLibraryLoadSucceeds = true; 286 final TestStartupCallback callback1 = new TestStartupCallback(); 287 final TestStartupCallback callback2 = new TestStartupCallback(); 288 289 // Kick off the asynchronous startup requests. 290 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 291 @Override 292 public void run() { 293 try { 294 mController.startBrowserProcessesAsync(callback1); 295 } catch (Exception e) { 296 fail("Browser should have started successfully"); 297 } 298 } 299 }); 300 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 301 @Override 302 public void run() { 303 mController.addStartupCompletedObserver(callback2); 304 } 305 }); 306 307 assertTrue("Asynchronous mode should have been set.", 308 BrowserStartupController.browserMayStartAsynchonously()); 309 assertEquals("The browser process should have been initialized one time.", 1, 310 mController.initializedCounter()); 311 312 // Wait for callbacks to complete. 313 getInstrumentation().waitForIdleSync(); 314 315 assertTrue("Callback 1 should have been executed.", callback1.mHasStartupResult); 316 assertTrue("Callback 1 should have been a failure.", callback1.mWasFailure); 317 assertTrue("Callback 2 should have been executed.", callback2.mHasStartupResult); 318 assertTrue("Callback 2 should have been a failure.", callback2.mWasFailure); 319 320 final TestStartupCallback callback3 = new TestStartupCallback(); 321 final TestStartupCallback callback4 = new TestStartupCallback(); 322 323 // Kick off more asynchronous startup requests. 324 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 325 @Override 326 public void run() { 327 try { 328 mController.startBrowserProcessesAsync(callback3); 329 } catch (Exception e) { 330 fail("Browser should have started successfully"); 331 } 332 } 333 }); 334 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 335 @Override 336 public void run() { 337 mController.addStartupCompletedObserver(callback4); 338 } 339 }); 340 341 // Wait for callbacks to complete. 342 getInstrumentation().waitForIdleSync(); 343 344 assertTrue("Callback 3 should have been executed.", callback3.mHasStartupResult); 345 assertTrue("Callback 3 should have been a failure.", callback3.mWasFailure); 346 assertTrue("Callback 4 should have been executed.", callback4.mHasStartupResult); 347 assertTrue("Callback 4 should have been a failure.", callback4.mWasFailure); 348 } 349 350 @SmallTest 351 public void testSingleSynchronousRequest() { 352 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 353 mController.mLibraryLoadSucceeds = true; 354 // Kick off the synchronous startup. 355 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 356 @Override 357 public void run() { 358 try { 359 mController.startBrowserProcessesSync(1); 360 } catch (Exception e) { 361 fail("Browser should have started successfully"); 362 } 363 } 364 }); 365 assertFalse("Synchronous mode should have been set", 366 BrowserStartupController.browserMayStartAsynchonously()); 367 368 assertEquals("The browser process should have been initialized one time.", 1, 369 mController.initializedCounter()); 370 } 371 372 @SmallTest 373 public void testAsyncThenSyncRequests() { 374 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 375 mController.mLibraryLoadSucceeds = true; 376 final TestStartupCallback callback = new TestStartupCallback(); 377 378 // Kick off the startups. 379 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 380 @Override 381 public void run() { 382 try { 383 mController.startBrowserProcessesAsync(callback); 384 } catch (Exception e) { 385 fail("Browser should have started successfully"); 386 } 387 // To ensure that the async startup doesn't complete too soon we have 388 // to do both these in a since Runnable instance. This avoids the 389 // unpredictable race that happens in real situations. 390 try { 391 mController.startBrowserProcessesSync(1); 392 } catch (Exception e) { 393 fail("Browser should have started successfully"); 394 } 395 } 396 }); 397 assertFalse("Synchronous mode should have been set", 398 BrowserStartupController.browserMayStartAsynchonously()); 399 400 assertEquals("The browser process should have been initialized twice.", 2, 401 mController.initializedCounter()); 402 403 assertTrue("Callback should have been executed.", callback.mHasStartupResult); 404 assertTrue("Callback should have been a success.", callback.mWasSuccess); 405 assertFalse("Callback should be told that the browser process was not already started.", 406 callback.mAlreadyStarted); 407 } 408 409 @SmallTest 410 public void testSyncThenAsyncRequests() { 411 mController.mStartupResult = BrowserStartupController.STARTUP_SUCCESS; 412 mController.mLibraryLoadSucceeds = true; 413 final TestStartupCallback callback = new TestStartupCallback(); 414 415 // Do a synchronous startup first. 416 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 417 @Override 418 public void run() { 419 try { 420 mController.startBrowserProcessesSync(1); 421 } catch (Exception e) { 422 fail("Browser should have started successfully"); 423 } 424 } 425 }); 426 427 assertEquals("The browser process should have been initialized once.", 1, 428 mController.initializedCounter()); 429 430 assertFalse("Synchronous mode should have been set", 431 BrowserStartupController.browserMayStartAsynchonously()); 432 433 // Kick off the asynchronous startup request. This should just queue the callback. 434 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 435 @Override 436 public void run() { 437 try { 438 mController.startBrowserProcessesAsync(callback); 439 } catch (Exception e) { 440 fail("Browser should have started successfully"); 441 } 442 } 443 }); 444 445 assertEquals("The browser process should not have been initialized a second time.", 1, 446 mController.initializedCounter()); 447 448 // Wait for callbacks to complete. 449 getInstrumentation().waitForIdleSync(); 450 451 assertTrue("Callback should have been executed.", callback.mHasStartupResult); 452 assertTrue("Callback should have been a success.", callback.mWasSuccess); 453 assertTrue("Callback should be told that the browser process was already started.", 454 callback.mAlreadyStarted); 455 } 456 457 @SmallTest 458 public void testLibraryLoadFails() { 459 mController.mLibraryLoadSucceeds = false; 460 final TestStartupCallback callback = new TestStartupCallback(); 461 462 // Kick off the asynchronous startup request. 463 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 464 @Override 465 public void run() { 466 try { 467 mController.startBrowserProcessesAsync(callback); 468 fail("Browser should not have started successfully"); 469 } catch (Exception e) { 470 // Exception expected, ignore. 471 } 472 } 473 }); 474 475 assertEquals("The browser process should not have been initialized.", 0, 476 mController.initializedCounter()); 477 478 // Wait for callbacks to complete. 479 getInstrumentation().waitForIdleSync(); 480 } 481 482} 483