1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.os;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.Mockito.mockitoSession;
22 import static org.mockito.Mockito.when;
23 import static org.testng.Assert.assertThrows;
24 
25 import static java.util.stream.Collectors.toList;
26 
27 import android.platform.test.annotations.Presubmit;
28 
29 import androidx.test.filters.SmallTest;
30 import androidx.test.runner.AndroidJUnit4;
31 
32 import org.junit.After;
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 import org.mockito.Mock;
37 import org.mockito.MockitoSession;
38 
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.Collections;
43 
44 @Presubmit
45 @SmallTest
46 @RunWith(AndroidJUnit4.class)
47 public class KernelCpuThreadReaderDiffTest {
48 
49     private MockitoSession mMockingSessions;
50     @Mock KernelCpuThreadReader mMockReader;
51 
52     @Before
setUp()53     public void setUp() {
54         mMockingSessions = mockitoSession().initMocks(this).startMocking();
55     }
56 
57     @After
tearDown()58     public void tearDown() {
59         if (mMockingSessions != null) {
60             mMockingSessions.finishMocking();
61         }
62     }
63 
64     @Test
test_empty()65     public void test_empty() {
66         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
67                 new KernelCpuThreadReaderDiff(mMockReader, 0);
68         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
69         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isEmpty();
70     }
71 
72     @Test
test_simple()73     public void test_simple() {
74         when(mMockReader.getProcessCpuUsage())
75                 .thenReturn(createProcess(new int[] {100, 100, 100}))
76                 .thenReturn(createProcess(new int[] {150, 160, 170}));
77         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
78                 new KernelCpuThreadReaderDiff(mMockReader, 0);
79         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
80         assertThat(cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()))
81                 .containsExactly(Arrays.asList(50, 60, 70));
82     }
83 
84     @Test
test_failure()85     public void test_failure() {
86         when(mMockReader.getProcessCpuUsage())
87                 .thenReturn(createProcess(new int[] {1}))
88                 .thenReturn(createProcess(new int[] {2}))
89                 .thenThrow(new RuntimeException())
90                 .thenReturn(createProcess(new int[] {4}))
91                 .thenReturn(createProcess(new int[] {6}));
92         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
93                 new KernelCpuThreadReaderDiff(mMockReader, 0);
94         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
95         assertThat(cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()))
96                 .containsExactly(Collections.singletonList(1));
97         assertThrows(
98                 RuntimeException.class,
99                 () -> cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()));
100         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
101         assertThat(cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()))
102                 .containsExactly(Collections.singletonList(2));
103     }
104 
105     @Test
test_twoFailures()106     public void test_twoFailures() {
107         when(mMockReader.getProcessCpuUsage())
108                 .thenReturn(createProcess(new int[] {1}))
109                 .thenReturn(createProcess(new int[] {2}))
110                 .thenThrow(new RuntimeException())
111                 .thenThrow(new RuntimeException())
112                 .thenReturn(createProcess(new int[] {4}))
113                 .thenReturn(createProcess(new int[] {6}));
114         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
115                 new KernelCpuThreadReaderDiff(mMockReader, 0);
116         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
117         assertThat(cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()))
118                 .containsExactly(Collections.singletonList(1));
119         assertThrows(
120                 RuntimeException.class,
121                 () -> cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()));
122         assertThrows(
123                 RuntimeException.class,
124                 () -> cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()));
125         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
126         assertThat(cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()))
127                 .containsExactly(Collections.singletonList(2));
128     }
129 
130     @Test
test_negativeDiff()131     public void test_negativeDiff() {
132         when(mMockReader.getProcessCpuUsage())
133                 .thenReturn(createProcess(new int[] {2}))
134                 .thenReturn(createProcess(new int[] {1}));
135         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
136                 new KernelCpuThreadReaderDiff(mMockReader, 0);
137         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
138         assertThat(cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()))
139                 .containsExactly(Collections.singletonList(-1));
140     }
141 
142     @Test
test_threshold()143     public void test_threshold() {
144         when(mMockReader.getProcessCpuUsage())
145                 .thenReturn(createProcess(new int[] {1}))
146                 .thenReturn(createProcess(new int[] {10}))
147                 .thenReturn(createProcess(new int[] {12}))
148                 .thenReturn(createProcess(new int[] {20}));
149         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
150                 new KernelCpuThreadReaderDiff(mMockReader, 5);
151         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
152 
153         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes1 =
154                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
155         assertThat(cpuUsages(processes1)).containsExactly(Collections.singletonList(9));
156         assertThat(threadNames(processes1)).containsExactly("thread0");
157 
158         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes2 =
159                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
160         assertThat(cpuUsages(processes2)).containsExactly(Collections.singletonList(2));
161         assertThat(threadNames(processes2)).containsExactly("__OTHER_THREADS");
162 
163         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes3 =
164                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
165         assertThat(cpuUsages(processes3)).containsExactly(Collections.singletonList(8));
166         assertThat(threadNames(processes3)).containsExactly("thread0");
167     }
168 
169     @Test
test_newThread()170     public void test_newThread() {
171         when(mMockReader.getProcessCpuUsage())
172                 .thenReturn(createProcess(new int[] {1}))
173                 .thenReturn(createProcess(new int[] {2}))
174                 .thenReturn(createProcess(new int[] {4}, new int[] {5}));
175         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
176                 new KernelCpuThreadReaderDiff(mMockReader, 0);
177         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
178 
179         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes1 =
180                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
181         assertThat(cpuUsages(processes1)).containsExactly(Collections.singletonList(1));
182         assertThat(threadNames(processes1)).containsExactly("thread0");
183 
184         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes2 =
185                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
186         assertThat(cpuUsages(processes2))
187                 .containsExactly(Collections.singletonList(2), Collections.singletonList(5));
188         assertThat(threadNames(processes2)).containsExactly("thread0", "thread1");
189     }
190 
191     @Test
test_stoppedThread()192     public void test_stoppedThread() {
193         when(mMockReader.getProcessCpuUsage())
194                 .thenReturn(createProcess(new int[] {1}, new int[] {1}))
195                 .thenReturn(createProcess(new int[] {2}, new int[] {3}))
196                 .thenReturn(createProcess(new int[] {4}));
197         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
198                 new KernelCpuThreadReaderDiff(mMockReader, 0);
199         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
200 
201         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes1 =
202                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
203         assertThat(cpuUsages(processes1))
204                 .containsExactly(Collections.singletonList(1), Collections.singletonList(2));
205         assertThat(threadNames(processes1)).containsExactly("thread0", "thread1");
206 
207         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes2 =
208                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
209         assertThat(cpuUsages(processes2)).containsExactly(Collections.singletonList(2));
210         assertThat(threadNames(processes2)).containsExactly("thread0");
211     }
212 
213     @Test
test_nonNegativeOtherThreads()214     public void test_nonNegativeOtherThreads() {
215         when(mMockReader.getProcessCpuUsage())
216                 .thenReturn(createProcess(new int[] {0}, new int[] {0}))
217                 .thenReturn(createProcess(new int[] {4}, new int[] {4}))
218                 .thenReturn(createProcess(new int[] {10}, new int[] {7}))
219                 .thenReturn(createProcess(new int[] {20}, new int[] {15}));
220         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
221                 new KernelCpuThreadReaderDiff(mMockReader, 5);
222         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
223 
224         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes1 =
225                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
226         assertThat(cpuUsages(processes1)).containsExactly(Collections.singletonList(8));
227         assertThat(threadNames(processes1)).containsExactly("__OTHER_THREADS");
228 
229         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes2 =
230                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
231         assertThat(cpuUsages(processes2))
232                 .containsExactly(Collections.singletonList(6), Collections.singletonList(3));
233         assertThat(threadNames(processes2)).containsExactly("thread0", "__OTHER_THREADS");
234 
235         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes3 =
236                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
237         assertThat(cpuUsages(processes3))
238                 .containsExactly(Collections.singletonList(10), Collections.singletonList(8));
239         assertThat(threadNames(processes3)).containsExactly("thread0", "thread1");
240     }
241 
242     @Test
test_otherThreadsOnZeroDiff()243     public void test_otherThreadsOnZeroDiff() {
244         when(mMockReader.getProcessCpuUsage())
245                 .thenReturn(createProcess(new int[] {0}))
246                 .thenReturn(createProcess(new int[] {0}));
247         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
248                 new KernelCpuThreadReaderDiff(mMockReader, 5);
249         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
250 
251         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes1 =
252                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
253         assertThat(cpuUsages(processes1)).containsExactly(Collections.singletonList(0));
254         assertThat(threadNames(processes1)).containsExactly("__OTHER_THREADS");
255     }
256 
257     @Test
test_failureAndNewThread()258     public void test_failureAndNewThread() {
259         when(mMockReader.getProcessCpuUsage())
260                 .thenReturn(createProcess(new int[] {0}))
261                 .thenThrow(new RuntimeException())
262                 .thenReturn(createProcess(new int[] {1}, new int[] {10}))
263                 .thenReturn(createProcess(new int[] {2}, new int[] {12}));
264         KernelCpuThreadReaderDiff kernelCpuThreadReaderDiff =
265                 new KernelCpuThreadReaderDiff(mMockReader, 0);
266         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
267 
268         assertThrows(
269                 RuntimeException.class,
270                 () -> cpuUsages(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()));
271         assertThat(kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed()).isNull();
272 
273         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processes1 =
274                 kernelCpuThreadReaderDiff.getProcessCpuUsageDiffed();
275         assertThat(cpuUsages(processes1))
276                 .containsExactly(Collections.singletonList(1), Collections.singletonList(2));
277         assertThat(threadNames(processes1)).containsExactly("thread0", "thread1");
278     }
279 
createProcess( int[]... cpuUsageMillis)280     private ArrayList<KernelCpuThreadReader.ProcessCpuUsage> createProcess(
281             int[]... cpuUsageMillis) {
282         ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = new ArrayList<>();
283         for (int i = 0; i < cpuUsageMillis.length; i++) {
284             int[] cpuUsage = cpuUsageMillis[i];
285             threadCpuUsages.add(
286                     new KernelCpuThreadReader.ThreadCpuUsage(0, "thread" + i, cpuUsage));
287         }
288         return new ArrayList<>(
289                 Collections.singletonList(
290                         new KernelCpuThreadReader.ProcessCpuUsage(
291                                 0, "process", 0, threadCpuUsages)));
292     }
293 
cpuUsages( Collection<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages)294     private Collection<Collection<Integer>> cpuUsages(
295             Collection<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages) {
296         return processCpuUsages.stream()
297                 .flatMap(p -> p.threadCpuUsages.stream())
298                 .map(t -> Arrays.stream(t.usageTimesMillis).boxed().collect(toList()))
299                 .collect(toList());
300     }
301 
threadNames( Collection<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages)302     private Collection<String> threadNames(
303             Collection<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages) {
304         return processCpuUsages.stream()
305                 .flatMap(p -> p.threadCpuUsages.stream())
306                 .map(t -> t.threadName)
307                 .collect(toList());
308     }
309 }
310