1 // Copyright 2018 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 
5 package org.chromium.content.browser;
6 
7 import android.content.ComponentName;
8 
9 import org.junit.Assert;
10 import org.junit.Test;
11 import org.junit.runner.RunWith;
12 import org.robolectric.annotation.Config;
13 import org.robolectric.shadows.ShadowLooper;
14 
15 import org.chromium.base.process_launcher.ChildProcessConnection;
16 import org.chromium.base.test.BaseRobolectricTestRunner;
17 import org.chromium.base.test.TestChildProcessConnection;
18 import org.chromium.content_public.browser.ChildProcessImportance;
19 
20 /** Unit tests for ChildProessRanking */
21 @RunWith(BaseRobolectricTestRunner.class)
22 @Config(manifest = Config.NONE)
23 public class ChildProcessRankingTest {
createConnection()24     private TestChildProcessConnection createConnection() {
25         TestChildProcessConnection connection = new TestChildProcessConnection(
26                 new ComponentName("pkg", "cls"), false /* bindToCallerCheck */,
27                 false /* bindAsExternalService */, null /* serviceBundle */);
28         connection.start(false /* useStrongBinding */, null /* serviceCallback */);
29         return connection;
30     }
31 
assertRankingAndRemoveAll( ChildProcessRanking ranking, ChildProcessConnection[] connections)32     private void assertRankingAndRemoveAll(
33             ChildProcessRanking ranking, ChildProcessConnection[] connections) {
34         int index = connections.length;
35         ChildProcessConnection reverseIterationArray[] =
36                 new ChildProcessConnection[connections.length];
37         for (ChildProcessConnection c : ranking) {
38             reverseIterationArray[--index] = c;
39         }
40         Assert.assertArrayEquals(connections, reverseIterationArray);
41         Assert.assertEquals(0, index);
42 
43         index = connections.length;
44         ChildProcessConnection reverseRemoveArray[] =
45                 new ChildProcessConnection[connections.length];
46         for (int i = 0; i < connections.length; ++i) {
47             ChildProcessConnection c = ranking.getLowestRankedConnection();
48             reverseRemoveArray[--index] = c;
49             ranking.removeConnection(c);
50         }
51         Assert.assertArrayEquals(connections, reverseRemoveArray);
52         Assert.assertNull(ranking.getLowestRankedConnection());
53     }
54 
assertNotInGroup(ChildProcessConnection[] connections)55     private void assertNotInGroup(ChildProcessConnection[] connections) {
56         for (ChildProcessConnection c : connections) {
57             Assert.assertEquals(0, c.getGroup());
58         }
59     }
60 
assertInGroupOrderedByImportance(ChildProcessConnection[] connections)61     private void assertInGroupOrderedByImportance(ChildProcessConnection[] connections) {
62         int importanceSoFar = -1;
63         for (ChildProcessConnection c : connections) {
64             Assert.assertTrue(c.getGroup() > 0);
65             Assert.assertTrue(c.getImportanceInGroup() > importanceSoFar);
66             importanceSoFar = c.getImportanceInGroup();
67         }
68     }
69 
70     @Test
testRanking()71     public void testRanking() {
72         ChildProcessRanking ranking = new ChildProcessRanking(10);
73         doTestRanking(ranking, false);
74     }
75 
76     @Test
testRankingWithoutLimit()77     public void testRankingWithoutLimit() {
78         ChildProcessRanking ranking = new ChildProcessRanking();
79         doTestRanking(ranking, false);
80     }
81 
82     @Test
testEnableGroupAfter()83     public void testEnableGroupAfter() {
84         ChildProcessRanking ranking = new ChildProcessRanking();
85         doTestRanking(ranking, true);
86     }
87 
doTestRanking(ChildProcessRanking ranking, boolean enableGroupImportanceAfter)88     private void doTestRanking(ChildProcessRanking ranking, boolean enableGroupImportanceAfter) {
89         if (!enableGroupImportanceAfter) ranking.enableServiceGroupImportance();
90 
91         ChildProcessConnection c1 = createConnection();
92         ChildProcessConnection c2 = createConnection();
93         ChildProcessConnection c3 = createConnection();
94         ChildProcessConnection c4 = createConnection();
95         ChildProcessConnection c5 = createConnection();
96         ChildProcessConnection c6 = createConnection();
97         ChildProcessConnection c7 = createConnection();
98         ChildProcessConnection c8 = createConnection();
99         ChildProcessConnection c9 = createConnection();
100         ChildProcessConnection c10 = createConnection();
101 
102 
103         // Insert in lowest ranked to highest ranked order.
104 
105         // Invisible frame.
106         ranking.addConnection(c1, false /* foreground */, 0 /* frameDepth */,
107                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
108 
109         // Visible subframe outside viewport.
110         ranking.addConnection(c2, true /* foreground */, 2 /* frameDepth */,
111                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
112         ranking.addConnection(c3, true /* foreground */, 1 /* frameDepth */,
113                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
114 
115         // Visible subframe inside viewport.
116         ranking.addConnection(c4, true /* foreground */, 2 /* frameDepth */,
117                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
118         ranking.addConnection(c5, true /* foreground */, 1 /* frameDepth */,
119                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
120 
121         // Visible main frame.
122         ranking.addConnection(c6, true /* foreground */, 0 /* frameDepth */,
123                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
124 
125         if (enableGroupImportanceAfter) {
126             assertNotInGroup(new ChildProcessConnection[] {c6, c5, c4, c3, c2, c1});
127             ranking.enableServiceGroupImportance();
128         }
129 
130         assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c6, c5, c4, c3, c2, c1});
131 
132         assertNotInGroup(new ChildProcessConnection[] {c6, c5, c4});
133         assertInGroupOrderedByImportance(new ChildProcessConnection[] {c3, c2, c1});
134     }
135 
136     @Test
testRankingWithImportance()137     public void testRankingWithImportance() {
138         ChildProcessConnection c1 = createConnection();
139         ChildProcessConnection c2 = createConnection();
140         ChildProcessConnection c3 = createConnection();
141         ChildProcessConnection c4 = createConnection();
142 
143         ChildProcessRanking ranking = new ChildProcessRanking(4);
144         ranking.enableServiceGroupImportance();
145 
146         // Insert in lowest ranked to highest ranked order.
147         ranking.addConnection(c1, false /* foreground */, 0 /* frameDepth */,
148                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
149         ranking.addConnection(c2, false /* foreground */, 0 /* frameDepth */,
150                 false /* intersectsViewport */, ChildProcessImportance.MODERATE);
151         ranking.addConnection(c3, false /* foreground */, 1 /* frameDepth */,
152                 false /* intersectsViewport */, ChildProcessImportance.IMPORTANT);
153         ranking.addConnection(c4, false /* foreground */, 0 /* frameDepth */,
154                 false /* intersectsViewport */, ChildProcessImportance.IMPORTANT);
155 
156         assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c4, c3, c2, c1});
157         assertNotInGroup(new ChildProcessConnection[] {c4, c3, c2});
158         assertInGroupOrderedByImportance(new ChildProcessConnection[] {c1});
159     }
160 
161     @Test
testUpdate()162     public void testUpdate() {
163         ChildProcessConnection c1 = createConnection();
164         ChildProcessConnection c2 = createConnection();
165         ChildProcessConnection c3 = createConnection();
166         ChildProcessConnection c4 = createConnection();
167 
168         ChildProcessRanking ranking = new ChildProcessRanking(4);
169         ranking.enableServiceGroupImportance();
170 
171         // c1,2 are in one tab, and c3,4 are in second tab.
172         ranking.addConnection(c1, true /* foreground */, 1 /* frameDepth */,
173                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
174         ranking.addConnection(c2, true /* foreground */, 0 /* frameDepth */,
175                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
176         ranking.addConnection(c3, false /* foreground */, 1 /* frameDepth */,
177                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
178         ranking.addConnection(c4, false /* foreground */, 0 /* frameDepth */,
179                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
180         Assert.assertEquals(c3, ranking.getLowestRankedConnection());
181 
182         // Switch from tab c1,2 to tab c3,c4.
183         ranking.updateConnection(c1, false /* foreground */, 1 /* frameDepth */,
184                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
185         ranking.updateConnection(c2, false /* foreground */, 0 /* frameDepth */,
186                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
187         ranking.updateConnection(c3, true /* foreground */, 1 /* frameDepth */,
188                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
189         ranking.updateConnection(c4, true /* foreground */, 0 /* frameDepth */,
190                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
191 
192         assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c4, c3, c2, c1});
193         assertNotInGroup(new ChildProcessConnection[] {c4, c3});
194         assertInGroupOrderedByImportance(new ChildProcessConnection[] {c2, c1});
195     }
196 
197     @Test
testIntersectsViewport()198     public void testIntersectsViewport() {
199         ChildProcessConnection c1 = createConnection();
200         ChildProcessConnection c2 = createConnection();
201         ChildProcessConnection c3 = createConnection();
202 
203         ChildProcessRanking ranking = new ChildProcessRanking(4);
204         ranking.enableServiceGroupImportance();
205 
206         // Insert in lowest ranked to highest ranked order.
207         ranking.addConnection(c1, true /* foreground */, 1 /* frameDepth */,
208                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
209         ranking.addConnection(c2, true /* foreground */, 1 /* frameDepth */,
210                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
211         ranking.addConnection(c3, true /* foreground */, 0 /* frameDepth */,
212                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
213 
214         assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c3, c2, c1});
215         assertNotInGroup(new ChildProcessConnection[] {c3, c2});
216         assertInGroupOrderedByImportance(new ChildProcessConnection[] {c1});
217     }
218 
219     @Test
testFrameDepthIntOverflow()220     public void testFrameDepthIntOverflow() {
221         ChildProcessConnection c1 = createConnection();
222         ChildProcessConnection c2 = createConnection();
223         ChildProcessConnection c3 = createConnection();
224         ChildProcessRanking ranking = new ChildProcessRanking();
225 
226         // Native can pass up the maximum value of unsigned int.
227         long intOverflow = ((long) Integer.MAX_VALUE) * 2;
228         ranking.addConnection(c3, true /* foreground */, intOverflow - 1 /* frameDepth */,
229                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
230         ranking.addConnection(c2, true /* foreground */, 10 /* frameDepth */,
231                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
232         ranking.addConnection(c1, true /* foreground */, intOverflow /* frameDepth */,
233                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
234 
235         assertRankingAndRemoveAll(ranking, new ChildProcessConnection[] {c2, c3, c1});
236     }
237 
238     @Test
testThrowExceptionWhenGoingOverLimit()239     public void testThrowExceptionWhenGoingOverLimit() {
240         ChildProcessRanking ranking = new ChildProcessRanking(2);
241 
242         ChildProcessConnection c1 = createConnection();
243         ChildProcessConnection c2 = createConnection();
244         ChildProcessConnection c3 = createConnection();
245 
246         ranking.addConnection(c1, true /* foreground */, 1 /* frameDepth */,
247                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
248         ranking.addConnection(c2, true /* foreground */, 1 /* frameDepth */,
249                 true /* intersectsViewport */, ChildProcessImportance.NORMAL);
250         boolean exceptionThrown = false;
251         try {
252             ranking.addConnection(c3, true /* foreground */, 1 /* frameDepth */,
253                     true /* intersectsViewport */, ChildProcessImportance.NORMAL);
254         } catch (Throwable e) {
255             exceptionThrown = true;
256         }
257         Assert.assertTrue(exceptionThrown);
258     }
259 
260     @Test
testRebindHighRankConnection()261     public void testRebindHighRankConnection() {
262         ChildProcessRanking ranking = new ChildProcessRanking();
263         ranking.enableServiceGroupImportance();
264 
265         TestChildProcessConnection c1 = createConnection();
266         TestChildProcessConnection c2 = createConnection();
267         TestChildProcessConnection c3 = createConnection();
268 
269         ranking.addConnection(c1, true /* foreground */, 0 /* frameDepth */,
270                 false /* intersectsViewport */, ChildProcessImportance.IMPORTANT);
271         ranking.addConnection(c2, true /* foreground */, 2 /* frameDepth */,
272                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
273         ranking.addConnection(c3, true /* foreground */, 3 /* frameDepth */,
274                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
275 
276         assertNotInGroup(new ChildProcessConnection[] {c1});
277         assertInGroupOrderedByImportance(new ChildProcessConnection[] {c2, c3});
278 
279         c1.getAndResetRebindCalled();
280         ranking.updateConnection(c3, true /* foreground */, 1 /* frameDepth */,
281                 false /* intersectsViewport */, ChildProcessImportance.NORMAL);
282         assertNotInGroup(new ChildProcessConnection[] {c1});
283         assertInGroupOrderedByImportance(new ChildProcessConnection[] {c3, c2});
284         Assert.assertFalse(c1.getAndResetRebindCalled());
285 
286         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
287         Assert.assertTrue(c1.getAndResetRebindCalled());
288     }
289 }
290