1 /*
2  * Copyright (C) 2022 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.server.cpu;
18 
19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
20 
21 import static com.android.server.cpu.CpuInfoReader.CpuInfo.MISSING_FREQUENCY;
22 import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_BACKGROUND;
23 import static com.android.server.cpu.CpuInfoReader.FLAG_CPUSET_CATEGORY_TOP_APP;
24 
25 import static com.google.common.truth.Truth.assertWithMessage;
26 
27 import android.content.Context;
28 import android.content.res.AssetManager;
29 import android.util.Log;
30 import android.util.SparseArray;
31 
32 import com.android.server.ExpectableTestCase;
33 
34 import libcore.io.Streams;
35 
36 import org.junit.After;
37 import org.junit.Before;
38 import org.junit.Test;
39 
40 import java.io.File;
41 import java.io.FileOutputStream;
42 import java.io.InputStream;
43 import java.io.OutputStream;
44 import java.util.Objects;
45 
46 /** This class contains unit tests for the {@link CpuInfoReader}. */
47 public final class CpuInfoReaderTest extends ExpectableTestCase {
48     private static final String TAG = CpuInfoReaderTest.class.getSimpleName();
49     private static final String ROOT_DIR_NAME = "CpuInfoReaderTest";
50     private static final String VALID_CPUSET_DIR = "valid_cpuset";
51     private static final String VALID_CPUSET_WITH_EMPTY_CPUS = "valid_cpuset_with_empty_cpus";
52     private static final String VALID_CPUFREQ_WITH_EMPTY_AFFECTED_CPUS =
53             "valid_cpufreq_with_empty_affected_cpus";
54     private static final String VALID_CPUFREQ_WITH_EMPTY_RELATED_CPUS =
55             "valid_cpufreq_with_empty_related_cpus";
56     private static final String VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR =
57             "valid_cpufreq_with_time_in_state";
58     private static final String VALID_CPUFREQ_WITH_TIME_IN_STATE_2_DIR =
59             "valid_cpufreq_with_time_in_state_2";
60     private static final String VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_DIR =
61             "valid_cpufreq_without_time_in_state";
62     private static final String VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_2_DIR =
63             "valid_cpufreq_without_time_in_state_2";
64     private static final String VALID_PROC_STAT = "valid_proc_stat";
65     private static final String VALID_PROC_STAT_2 = "valid_proc_stat_2";
66     private static final String CORRUPTED_CPUFREQ_DIR = "corrupted_cpufreq";
67     private static final String CORRUPTED_CPUSET_DIR = "corrupted_cpuset";
68     private static final String CORRUPTED_PROC_STAT = "corrupted_proc_stat";
69     private static final String EMPTY_DIR = "empty_dir";
70     private static final String EMPTY_FILE = "empty_file";
71 
72     private final Context mContext = getInstrumentation().getTargetContext();
73     private final File mCacheRoot = new File(mContext.getCacheDir(), ROOT_DIR_NAME);
74     private final AssetManager mAssetManager = mContext.getAssets();
75 
76     @Before
setUp()77     public void setUp() throws Exception {
78         copyAssets(ROOT_DIR_NAME, mContext.getCacheDir());
79         assertWithMessage("Cache root dir %s exists", mCacheRoot.getAbsolutePath())
80                 .that(mCacheRoot.exists()).isTrue();
81     }
82 
83     @After
tearDown()84     public void tearDown() throws Exception {
85         if (!deleteDirectory(mCacheRoot)) {
86             Log.e(TAG, "Failed to delete cache root directory " + mCacheRoot.getAbsolutePath());
87         }
88     }
89 
90     @Test
testReadCpuInfoWithTimeInState()91     public void testReadCpuInfoWithTimeInState() throws Exception {
92         CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
93                 getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(VALID_PROC_STAT));
94 
95         SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
96         SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
97         expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
98                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
99                 /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095,
100                 /* normalizedAvailableCpuFreqKHz= */ 2_402_267,
101                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
102                         /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
103                         /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
104                         /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970,
105                         /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0,
106                         /* guestNiceTimeMillis= */ 0)));
107         expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
108                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
109                 /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
110                 /* normalizedAvailableCpuFreqKHz= */ 2_693_525,
111                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
112                         /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
113                         /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
114                         /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
115                         /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
116                         /* guestNiceTimeMillis= */ 0)));
117         expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
118                 FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
119                 /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
120                 /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
121                 /* normalizedAvailableCpuFreqKHz= */ 1_901_608,
122                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
123                         /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
124                         /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
125                         /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130,
126                         /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0,
127                         /* guestNiceTimeMillis= */ 0)));
128         expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
129                 FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
130                 /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
131                 /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
132                 /* normalizedAvailableCpuFreqKHz= */ 1_907_125,
133                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
134                         /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
135                         /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810,
136                         /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970,
137                         /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0,
138                         /* guestNiceTimeMillis= */ 0)));
139 
140         compareCpuInfos("CPU infos first snapshot", expectedCpuInfos, actualCpuInfos);
141 
142         cpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_2_DIR));
143         cpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
144 
145         actualCpuInfos = cpuInfoReader.readCpuInfos();
146 
147         expectedCpuInfos.clear();
148         expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
149                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
150                 /* maxCpuFreqKHz= */ 2_600_000, /* avgTimeInStateCpuFreqKHz= */ 419_354,
151                 /* normalizedAvailableCpuFreqKHz= */ 2_525_919,
152                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
153                         /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
154                         /* idleTimeMillis= */ 110_000_000, /* iowaitTimeMillis= */ 1_100_000,
155                         /* irqTimeMillis= */ 1_400_000, /* softirqTimeMillis= */ 80_000,
156                         /* stealTimeMillis= */ 21_000, /* guestTimeMillis= */ 0,
157                         /* guestNiceTimeMillis= */ 0)));
158         expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
159                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_800_000,
160                 /* maxCpuFreqKHz= */ 2_900_000, /* avgTimeInStateCpuFreqKHz= */ 429_032,
161                 /* normalizedAvailableCpuFreqKHz= */ 2_503_009,
162                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000,
163                         /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
164                         /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000,
165                         /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000,
166                         /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
167                         /* guestNiceTimeMillis= */ 0)));
168         expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
169                 FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
170                 /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000,
171                 /* maxCpuFreqKHz= */ 2_100_000, /* avgTimeInStateCpuFreqKHz= */ 403_225,
172                 /* normalizedAvailableCpuFreqKHz= */ 1_788_209,
173                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
174                         /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0,
175                         /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000,
176                         /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000,
177                         /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
178                         /* guestNiceTimeMillis= */ 0)));
179         expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
180                 FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
181                 /* isOnline= */ false, /* curCpuFreqKHz= */ MISSING_FREQUENCY,
182                 /* maxCpuFreqKHz= */ 2_100_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
183                 /* normalizedAvailableCpuFreqKHz= */ MISSING_FREQUENCY,
184                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000,
185                         /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000,
186                         /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000,
187                         /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000,
188                         /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0,
189                         /* guestNiceTimeMillis= */ 0)));
190 
191         compareCpuInfos("CPU infos second snapshot", expectedCpuInfos, actualCpuInfos);
192     }
193 
194     @Test
testReadCpuInfoWithoutTimeInState()195     public void testReadCpuInfoWithoutTimeInState() throws Exception {
196         CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
197                 getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_DIR),
198                 getCacheFile(VALID_PROC_STAT));
199 
200         SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
201         SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
202         expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
203                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
204                 /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
205                 /* normalizedAvailableCpuFreqKHz= */ 2_253_713,
206                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
207                         /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
208                         /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
209                         /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970,
210                         /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0,
211                         /* guestNiceTimeMillis= */ 0)));
212         expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
213                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
214                 /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
215                 /* normalizedAvailableCpuFreqKHz= */ 2_492_687,
216                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
217                         /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
218                         /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
219                         /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
220                         /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
221                         /* guestNiceTimeMillis= */ 0)));
222         expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
223                 FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
224                 /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
225                 /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
226                 /* normalizedAvailableCpuFreqKHz= */ 1_788_079,
227                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
228                         /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
229                         /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
230                         /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130,
231                         /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0,
232                         /* guestNiceTimeMillis= */ 0)));
233         expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
234                 FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
235                 /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
236                 /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
237                 /* normalizedAvailableCpuFreqKHz= */ 1_799_962,
238                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
239                         /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
240                         /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810,
241                         /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970,
242                         /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0,
243                         /* guestNiceTimeMillis= */ 0)));
244 
245         compareCpuInfos("CPU infos first snapshot without time_in_state file", expectedCpuInfos,
246                 actualCpuInfos);
247 
248         cpuInfoReader.setCpuFreqDir(getCacheFile(VALID_CPUFREQ_WITHOUT_TIME_IN_STATE_2_DIR));
249         cpuInfoReader.setProcStatFile(getCacheFile(VALID_PROC_STAT_2));
250 
251         actualCpuInfos = cpuInfoReader.readCpuInfos();
252 
253         expectedCpuInfos.clear();
254         expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
255                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
256                 /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
257                 /* normalizedAvailableCpuFreqKHz= */ 2323347,
258                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
259                         /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
260                         /* idleTimeMillis= */ 110_000_000, /* iowaitTimeMillis= */ 1_100_000,
261                         /* irqTimeMillis= */ 1_400_000, /* softirqTimeMillis= */ 80_000,
262                         /* stealTimeMillis= */ 21_000, /* guestTimeMillis= */ 0,
263                         /* guestNiceTimeMillis= */ 0)));
264         expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
265                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_800_000,
266                 /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
267                 /* normalizedAvailableCpuFreqKHz= */ 209111,
268                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000,
269                         /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000,
270                         /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000,
271                         /* irqTimeMillis= */ 200_000, /* softirqTimeMillis= */ 100_000,
272                         /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
273                         /* guestNiceTimeMillis= */ 0)));
274         expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
275                 FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
276                 /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000,
277                 /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
278                 /* normalizedAvailableCpuFreqKHz= */ 453514,
279                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000,
280                         /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0,
281                         /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000,
282                         /* irqTimeMillis= */ 20_000_000, /* softirqTimeMillis= */ 1_000_000,
283                         /* stealTimeMillis= */ 100_000, /* guestTimeMillis= */ 0,
284                         /* guestNiceTimeMillis= */ 0)));
285         expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
286                 FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND,
287                 /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000,
288                 /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY,
289                 /* normalizedAvailableCpuFreqKHz= */ 37728,
290                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000,
291                         /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000,
292                         /* idleTimeMillis= */ 100_000, /* iowaitTimeMillis= */ 100_000,
293                         /* irqTimeMillis= */ 100_000, /* softirqTimeMillis= */ 1_000_000,
294                         /* stealTimeMillis= */ 1_000, /* guestTimeMillis= */ 0,
295                         /* guestNiceTimeMillis= */ 0)));
296 
297         compareCpuInfos("CPU infos second snapshot without time_in_state file", expectedCpuInfos,
298                 actualCpuInfos);
299     }
300 
301     @Test
testReadCpuInfoWithCorruptedCpuset()302     public void testReadCpuInfoWithCorruptedCpuset() throws Exception {
303         CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(CORRUPTED_CPUSET_DIR),
304                 getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR),
305                 getCacheFile(VALID_PROC_STAT));
306 
307         SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
308         SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
309         expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
310                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
311                 /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095,
312                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
313                         /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
314                         /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
315                         /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970,
316                         /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0,
317                         /* guestNiceTimeMillis= */ 0)));
318         expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
319                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
320                 /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
321                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
322                         /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
323                         /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
324                         /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
325                         /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
326                         /* guestNiceTimeMillis= */ 0)));
327         expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
328                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
329                 /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
330                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
331                         /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
332                         /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
333                         /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130,
334                         /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0,
335                         /* guestNiceTimeMillis= */ 0)));
336 
337         compareCpuInfos("CPU infos with corrupted cpuset", expectedCpuInfos, actualCpuInfos);
338     }
339 
340     @Test
testReadCpuInfoWithCorruptedCpufreq()341     public void testReadCpuInfoWithCorruptedCpufreq() throws Exception {
342         CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
343                 getCacheFile(CORRUPTED_CPUFREQ_DIR), getCacheFile(VALID_PROC_STAT));
344 
345         SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
346         SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
347 
348         compareCpuInfos("CPU infos with corrupted CPU frequency", expectedCpuInfos, actualCpuInfos);
349     }
350 
351     @Test
testReadCpuInfoWithCorruptedProcStat()352     public void testReadCpuInfoWithCorruptedProcStat() throws Exception {
353         CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
354                 getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR),
355                 getCacheFile(CORRUPTED_PROC_STAT));
356 
357         SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
358         SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
359         expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
360                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
361                 /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095,
362                 /* normalizedAvailableCpuFreqKHz= */ 2_402_267,
363                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
364                         /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
365                         /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
366                         /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970,
367                         /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0,
368                         /* guestNiceTimeMillis= */ 0)));
369         expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
370                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
371                 /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
372                 /* normalizedAvailableCpuFreqKHz= */ 2_693_525,
373                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
374                         /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
375                         /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
376                         /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
377                         /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
378                         /* guestNiceTimeMillis= */ 0)));
379 
380         compareCpuInfos("CPU infos with corrupted proc stat", expectedCpuInfos, actualCpuInfos);
381     }
382 
383     @Test
testReadCpuInfoWithEmptyCpuset()384     public void testReadCpuInfoWithEmptyCpuset() throws Exception {
385         File emptyDir = getCacheFile(EMPTY_DIR);
386         assertWithMessage("Make empty dir %s", emptyDir).that(emptyDir.mkdir()).isTrue();
387         CpuInfoReader cpuInfoReader = new CpuInfoReader(emptyDir, getCacheFile(
388                 VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR),
389                 getCacheFile(VALID_PROC_STAT), /* minReadIntervalMillis= */0);
390 
391         assertWithMessage("Init CPU reader info").that(cpuInfoReader.init()).isFalse();
392 
393         assertWithMessage("Cpu infos with empty cpuset").that(cpuInfoReader.readCpuInfos())
394                 .isNull();
395     }
396 
397     @Test
testReadCpuInfoWithEmptyCpufreq()398     public void testReadCpuInfoWithEmptyCpufreq() throws Exception {
399         File emptyDir = getCacheFile(EMPTY_DIR);
400         assertWithMessage("Make empty dir %s", emptyDir).that(emptyDir.mkdir()).isTrue();
401         CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR), emptyDir,
402                 getCacheFile(VALID_PROC_STAT), /* minReadIntervalMillis= */0);
403 
404         assertWithMessage("Init CPU reader info").that(cpuInfoReader.init()).isFalse();
405 
406         assertWithMessage("Cpu infos with empty CPU frequency").that(cpuInfoReader.readCpuInfos())
407                 .isNull();
408     }
409 
410     @Test
testReadCpuInfoWithEmptyRelatedCpus()411     public void testReadCpuInfoWithEmptyRelatedCpus() throws Exception {
412         CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
413                 getCacheFile(VALID_CPUFREQ_WITH_EMPTY_RELATED_CPUS),
414                 getCacheFile(VALID_PROC_STAT));
415 
416         SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
417         SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
418 
419         expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
420                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
421                 /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
422                 /* normalizedAvailableCpuFreqKHz= */ 2_693_525,
423                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
424                         /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
425                         /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
426                         /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
427                         /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
428                         /* guestNiceTimeMillis= */ 0)));
429 
430         compareCpuInfos("CPU infos with policy 0 containing an empty related_cpus file",
431                 expectedCpuInfos, actualCpuInfos);
432     }
433 
434     @Test
testReadCpuInfoWithEmptyCpusetCpus()435     public void testReadCpuInfoWithEmptyCpusetCpus() throws Exception {
436         CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_WITH_EMPTY_CPUS),
437                 getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR),
438                 getCacheFile(VALID_PROC_STAT));
439 
440         SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
441         SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
442         expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0,
443                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000,
444                 /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095,
445                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610,
446                         /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050,
447                         /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810,
448                         /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970,
449                         /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0,
450                         /* guestNiceTimeMillis= */ 0)));
451         expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
452                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
453                 /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
454                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
455                         /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
456                         /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
457                         /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
458                         /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
459                         /* guestNiceTimeMillis= */ 0)));
460         expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2,
461                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
462                 /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
463                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280,
464                         /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020,
465                         /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960,
466                         /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130,
467                         /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0,
468                         /* guestNiceTimeMillis= */ 0)));
469         expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3,
470                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000,
471                 /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285,
472                 /* normalizedAvailableCpuFreqKHz= */ 1_907_125,
473                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610,
474                         /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050,
475                         /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810,
476                         /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970,
477                         /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0,
478                         /* guestNiceTimeMillis= */ 0)));
479 
480         compareCpuInfos("CPU infos with empty background cpu set", expectedCpuInfos,
481                 actualCpuInfos);
482     }
483 
484     @Test
testReadCpuInfoWithEmptyAffectedCpus()485     public void testReadCpuInfoWithEmptyAffectedCpus() throws Exception {
486         CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
487                 getCacheFile(VALID_CPUFREQ_WITH_EMPTY_AFFECTED_CPUS),
488                 getCacheFile(VALID_PROC_STAT));
489 
490         SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos();
491         SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>();
492 
493         expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1,
494                 FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000,
495                 /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380,
496                 /* normalizedAvailableCpuFreqKHz= */ 2_693_525,
497                 new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280,
498                         /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020,
499                         /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960,
500                         /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130,
501                         /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0,
502                         /* guestNiceTimeMillis= */ 0)));
503 
504         compareCpuInfos("CPU infos with policy 0 containing an empty affected_cpus file",
505                 expectedCpuInfos, actualCpuInfos);
506     }
507 
508     @Test
testReadCpuInfoWithEmptyProcStat()509     public void testReadCpuInfoWithEmptyProcStat() throws Exception {
510         File emptyFile = getCacheFile(EMPTY_FILE);
511         assertWithMessage("Create empty file %s", emptyFile).that(emptyFile.createNewFile())
512                 .isTrue();
513         CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
514                 getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(EMPTY_FILE),
515                 /* minReadIntervalMillis= */0);
516 
517         assertWithMessage("Cpu infos with empty proc stat").that(cpuInfoReader.readCpuInfos())
518                 .isNull();
519     }
520 
521     @Test
testReadingTooFrequentlyReturnsLastReadCpuInfos()522     public void testReadingTooFrequentlyReturnsLastReadCpuInfos() throws Exception {
523         CpuInfoReader cpuInfoReader = new CpuInfoReader(getCacheFile(VALID_CPUSET_DIR),
524                 getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), getCacheFile(VALID_PROC_STAT),
525                 /* minReadIntervalMillis= */ 60_000);
526         assertWithMessage("Initialize CPU info reader").that(cpuInfoReader.init()).isTrue();
527 
528         SparseArray<CpuInfoReader.CpuInfo> firstCpuInfos = cpuInfoReader.readCpuInfos();
529         assertWithMessage("CPU infos first snapshot").that(firstCpuInfos).isNotNull();
530         assertWithMessage("CPU infos first snapshot size").that(firstCpuInfos.size())
531                 .isGreaterThan(0);
532 
533         SparseArray<CpuInfoReader.CpuInfo> secondCpuInfos = cpuInfoReader.readCpuInfos();
534         compareCpuInfos("CPU infos second snapshot", firstCpuInfos, secondCpuInfos);
535 
536         SparseArray<CpuInfoReader.CpuInfo> thirdCpuInfos = cpuInfoReader.readCpuInfos();
537         compareCpuInfos("CPU infos third snapshot", firstCpuInfos, thirdCpuInfos);
538     }
539 
compareCpuInfos(String message, SparseArray<CpuInfoReader.CpuInfo> expected, SparseArray<CpuInfoReader.CpuInfo> actual)540     private void compareCpuInfos(String message,
541             SparseArray<CpuInfoReader.CpuInfo> expected,
542             SparseArray<CpuInfoReader.CpuInfo> actual) {
543         assertWithMessage("%s. Total CPU infos", message).that(actual.size())
544                 .isEqualTo(expected.size());
545         for (int i = 0; i < expected.size(); i++) {
546             int cpuCoreId = expected.keyAt(i);
547             CpuInfoReader.CpuInfo expectedCpuInfo = expected.valueAt(i);
548             CpuInfoReader.CpuInfo actualCpuInfo = actual.get(cpuCoreId);
549             expectWithMessage("%s. Core %s's CPU info", message, cpuCoreId).that(actualCpuInfo)
550                     .isEqualTo(expectedCpuInfo);
551         }
552     }
553 
getCacheFile(String assetName)554     private File getCacheFile(String assetName) {
555         return new File(mCacheRoot, assetName);
556     }
557 
copyAssets(String assetPath, File targetRoot)558     private void copyAssets(String assetPath, File targetRoot) throws Exception {
559         File target = new File(targetRoot, assetPath);
560         String[] assets = mAssetManager.list(assetPath);
561         if (assets == null || assets.length == 0) {
562             try (InputStream in = mAssetManager.open(assetPath);
563                  OutputStream out = new FileOutputStream(target)) {
564                 Streams.copy(in, out);
565             }
566             return;
567         }
568         assertWithMessage("Make target directory %s", target).that(target.mkdir()).isTrue();
569         for (String assetName : assets) {
570             copyAssets(String.format("%s%s%s", assetPath, File.separator, assetName), targetRoot);
571         }
572     }
573 
newCpuInfoReader(File cpusetDir, File cpuFreqDir, File procStatFile)574     private static CpuInfoReader newCpuInfoReader(File cpusetDir, File cpuFreqDir,
575             File procStatFile) {
576         CpuInfoReader cpuInfoReader = new CpuInfoReader(cpusetDir, cpuFreqDir, procStatFile,
577                 /* minReadIntervalMillis= */ 0);
578         assertWithMessage("Initialize CPU info reader").that(cpuInfoReader.init()).isTrue();
579         return cpuInfoReader;
580     }
581 
deleteDirectory(File rootDir)582     private static boolean deleteDirectory(File rootDir) {
583         if (!rootDir.exists() || !rootDir.isDirectory()) {
584             return false;
585         }
586         for (File file : Objects.requireNonNull(rootDir.listFiles())) {
587             if (file.isDirectory()) {
588                 deleteDirectory(file);
589             } else if (!file.delete()) {
590                 return false;
591             }
592         }
593         return rootDir.delete();
594     }
595 }
596