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