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
5package org.chromium.content.browser;
6
7import android.content.Context;
8import android.os.RemoteException;
9import android.test.InstrumentationTestCase;
10import android.test.suitebuilder.annotation.MediumTest;
11
12import org.chromium.base.library_loader.LibraryLoader;
13import org.chromium.base.test.util.Feature;
14import org.chromium.content.browser.test.util.Criteria;
15import org.chromium.content.browser.test.util.CriteriaHelper;
16
17/**
18 * Instrumentation tests for ChildProcessLauncher.
19 */
20public class ChildProcessLauncherTest extends InstrumentationTestCase {
21    /**
22     *  Tests cleanup for a connection that fails to connect in the first place.
23     */
24    @MediumTest
25    @Feature({"ProcessManagement"})
26    public void testServiceFailedToBind() throws InterruptedException, RemoteException {
27        assertEquals(0, ChildProcessLauncher.allocatedConnectionsCountForTesting());
28        assertEquals(0, ChildProcessLauncher.connectedServicesCountForTesting());
29
30        // Try to allocate a connection to service class in incorrect package. We can do that by
31        // using the instrumentation context (getContext()) instead of the app context
32        // (getTargetContext()).
33        Context context = getInstrumentation().getContext();
34        ChildProcessLauncher.allocateBoundConnectionForTesting(context);
35
36        // Verify that the connection is not considered as allocated.
37        assertEquals(0, ChildProcessLauncher.allocatedConnectionsCountForTesting());
38        assertEquals(0, ChildProcessLauncher.connectedServicesCountForTesting());
39    }
40
41    /**
42     * Tests cleanup for a connection that terminates before setup.
43     */
44    @MediumTest
45    @Feature({"ProcessManagement"})
46    public void testServiceCrashedBeforeSetup() throws InterruptedException, RemoteException {
47        assertEquals(0, ChildProcessLauncher.allocatedConnectionsCountForTesting());
48        assertEquals(0, ChildProcessLauncher.connectedServicesCountForTesting());
49
50        // Start and connect to a new service.
51        final ChildProcessConnectionImpl connection = startConnection();
52        assertEquals(1, ChildProcessLauncher.allocatedConnectionsCountForTesting());
53
54        // Verify that the service is not yet set up.
55        assertEquals(0, connection.getPid());
56        assertEquals(0, ChildProcessLauncher.connectedServicesCountForTesting());
57
58        // Crash the service.
59        assertTrue(connection.crashServiceForTesting());
60
61        // Verify that the connection gets cleaned-up.
62        assertTrue("Crashed connection wasn't released from the allocator.",
63                CriteriaHelper.pollForCriteria(new Criteria() {
64            @Override
65            public boolean isSatisfied() {
66                return ChildProcessLauncher.allocatedConnectionsCountForTesting() == 0;
67            }
68        }));
69
70        assertTrue("Crashed connection wasn't released from ChildProcessLauncher.",
71                CriteriaHelper.pollForCriteria(new Criteria() {
72            @Override
73            public boolean isSatisfied() {
74                return ChildProcessLauncher.connectedServicesCountForTesting() == 0;
75            }
76        }));
77    }
78
79    /**
80     * Tests cleanup for a connection that terminates after setup.
81     */
82    @MediumTest
83    @Feature({"ProcessManagement"})
84    public void testServiceCrashedAfterSetup() throws InterruptedException, RemoteException {
85        assertEquals(0, ChildProcessLauncher.allocatedConnectionsCountForTesting());
86
87        // Start and connect to a new service.
88        final ChildProcessConnectionImpl connection = startConnection();
89        assertEquals(1, ChildProcessLauncher.allocatedConnectionsCountForTesting());
90
91        // Initiate the connection setup.
92        ChildProcessLauncher.triggerConnectionSetup(connection, new String[0], 1,
93                new FileDescriptorInfo[0], ChildProcessLauncher.CALLBACK_FOR_RENDERER_PROCESS, 0);
94
95        // Verify that the connection completes the setup.
96        assertTrue("The connection wasn't registered in ChildProcessLauncher after setup.",
97                CriteriaHelper.pollForCriteria(new Criteria() {
98            @Override
99            public boolean isSatisfied() {
100                return ChildProcessLauncher.connectedServicesCountForTesting() == 1;
101            }
102        }));
103
104        assertTrue("The connection failed to get a pid in setup.",
105                CriteriaHelper.pollForCriteria(new Criteria() {
106            @Override
107            public boolean isSatisfied() {
108                return connection.getPid() != 0;
109            }
110        }));
111
112        // Crash the service.
113        assertTrue(connection.crashServiceForTesting());
114
115        // Verify that the connection gets cleaned-up.
116        assertTrue("Crashed connection wasn't released from the allocator.",
117                CriteriaHelper.pollForCriteria(new Criteria() {
118            @Override
119            public boolean isSatisfied() {
120                return ChildProcessLauncher.allocatedConnectionsCountForTesting() == 0;
121            }
122        }));
123
124        assertTrue("Crashed connection wasn't released from ChildProcessLauncher.",
125                CriteriaHelper.pollForCriteria(new Criteria() {
126            @Override
127            public boolean isSatisfied() {
128                return ChildProcessLauncher.connectedServicesCountForTesting() == 0;
129            }
130        }));
131
132        // Verify that the connection pid remains set after termination.
133        assertTrue(connection.getPid() != 0);
134    }
135
136    private ChildProcessConnectionImpl startConnection() throws InterruptedException {
137        // Allocate a new connection.
138        Context context = getInstrumentation().getTargetContext();
139        final ChildProcessConnectionImpl connection = (ChildProcessConnectionImpl)
140                ChildProcessLauncher.allocateBoundConnectionForTesting(context);
141
142        // Wait for the service to connect.
143        assertTrue("The connection wasn't established.",
144                CriteriaHelper.pollForCriteria(new Criteria() {
145            @Override
146            public boolean isSatisfied() {
147                return connection.isConnected();
148            }
149        }));
150        return connection;
151    }
152
153    @Override
154    protected void setUp() throws Exception {
155        super.setUp();
156        LibraryLoader.ensureInitialized();
157    }
158}
159