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 org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.content.Context;
25 import android.os.FileUtils;
26 import android.os.SystemClock;
27 
28 import androidx.test.InstrumentationRegistry;
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 
37 import java.io.BufferedWriter;
38 import java.io.File;
39 import java.io.PrintWriter;
40 import java.io.StringWriter;
41 import java.nio.CharBuffer;
42 import java.nio.file.Files;
43 import java.util.ArrayList;
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.Random;
47 import java.util.concurrent.CountDownLatch;
48 import java.util.concurrent.Executors;
49 import java.util.concurrent.ScheduledExecutorService;
50 import java.util.concurrent.TimeUnit;
51 import java.util.stream.IntStream;
52 
53 /**
54  * Test class for {@link KernelCpuProcStringReader}.
55  *
56  * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcStringReaderTest
57  */
58 @SmallTest
59 @RunWith(AndroidJUnit4.class)
60 public class KernelCpuProcStringReaderTest {
61     private File mRoot;
62     private File mTestDir;
63     private File mTestFile;
64     private Random mRand = new Random(12345);
65     private KernelCpuProcStringReader mReader;
66 
getContext()67     private Context getContext() {
68         return InstrumentationRegistry.getContext();
69     }
70 
71     @Before
setUp()72     public void setUp() {
73         mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
74         mRoot = getContext().getFilesDir();
75         mTestFile = new File(mTestDir, "test.file");
76         mReader = new KernelCpuProcStringReader(mTestFile.getAbsolutePath());
77     }
78 
79     @After
tearDown()80     public void tearDown() throws Exception {
81         FileUtils.deleteContents(mTestDir);
82         FileUtils.deleteContents(mRoot);
83     }
84 
85 
86     /**
87      * Tests that reading will return null if the file does not exist.
88      */
89     @Test
testReadInvalidFile()90     public void testReadInvalidFile() throws Exception {
91         assertEquals(null, mReader.open());
92     }
93 
94     /**
95      * Tests that reading will always return null after 5 failures.
96      */
97     @Test
testReadErrorsLimit()98     public void testReadErrorsLimit() throws Exception {
99         for (int i = 0; i < 3; i++) {
100             try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
101                 assertNull(iter);
102             }
103             SystemClock.sleep(50);
104         }
105         final String data = "018n9x134yrm9sry01298yMF1X980Ym908u98weruwe983^(*)0N)&tu09281my\n";
106         try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
107             w.write(data);
108         }
109         try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
110             assertEquals(data.length(), iter.size());
111             assertEquals(data, iter.nextLine().toString() + '\n');
112         }
113         assertTrue(mTestFile.delete());
114         for (int i = 0; i < 3; i++) {
115             try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open(true)) {
116                 assertNull(iter);
117             }
118             SystemClock.sleep(50);
119         }
120         try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
121             w.write(data);
122         }
123         try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open(true)) {
124             assertNull(iter);
125         }
126     }
127 
128     /** Tests nextLine functionality. */
129     @Test
testReadLine()130     public void testReadLine() throws Exception {
131         final String data = "10103: 0 0 0 1 5 3 1 2 0 0 3 0 0 0 0 2 2 330 0 0 0 0 1 0 0 0 0 0 0 0"
132                 + " 0 0 0 0 0 0 0 0 0 0 0 13\n"
133                 + "50083: 0 0 0 29 0 13 0 4 5 0 0 0 0 0 1 0 0 15 0 0 0 0 0 0 1 0 0 0 0 1 0 1 7 0 "
134                 + "0 1 1 1 0 2 0 221\n"
135                 + "50227: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 196 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"
136                 + " 0 2 0 0 0 2 721\n"
137                 + "10158: 0 0 0 0 19 3 9 1 0 7 4 3 3 3 1 3 10 893 2 0 3 0 0 0 0 0 0 0 0 1 0 2 0 0"
138                 + " 1 2 10 0 0 0 1 58\n"
139                 + "50138: 0 0 0 8 7 0 0 0 0 0 0 0 0 0 0 0 0 322 0 0 0 3 0 5 0 0 3 0 0 0 0 1 0 0 0"
140                 + " 0 0 2 0 0 7 707\n";
141         try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
142             w.write(data);
143         }
144         try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
145             assertEquals(
146                     "10103: 0 0 0 1 5 3 1 2 0 0 3 0 0 0 0 2 2 330 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0"
147                             + " 0 0 0 0 0 0 0 13",
148                     iter.nextLine().toString());
149             assertEquals(
150                     "50083: 0 0 0 29 0 13 0 4 5 0 0 0 0 0 1 0 0 15 0 0 0 0 0 0 1 0 0 0 0 1 0 1 7 "
151                             + "0 0 1 1 1 0 2 0 221",
152                     iter.nextLine().toString());
153             long[] actual = new long[43];
154             KernelCpuProcStringReader.asLongs(iter.nextLine(), actual);
155             assertArrayEquals(
156                     new long[]{50227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 0, 0,
157                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 721},
158                     actual);
159             assertEquals(
160                     "10158: 0 0 0 0 19 3 9 1 0 7 4 3 3 3 1 3 10 893 2 0 3 0 0 0 0 0 0 0 0 1 0 2 0"
161                             + " 0 1 2 10 0 0 0 1 58",
162                     iter.nextLine().toString());
163             assertEquals(
164                     "50138: 0 0 0 8 7 0 0 0 0 0 0 0 0 0 0 0 0 322 0 0 0 3 0 5 0 0 3 0 0 0 0 1 0 0"
165                             + " 0 0 0 2 0 0 7 707",
166                     iter.nextLine().toString());
167         }
168     }
169 
170     /** Stress tests read functionality. */
171     @Test
testMultipleRead()172     public void testMultipleRead() throws Exception {
173         for (int i = 0; i < 100; i++) {
174             final String data = getTestString(600, 150);
175             try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
176                 w.write(data);
177             }
178             String[] lines = data.split("\n");
179             try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open(true)) {
180                 for (String line : lines) {
181                     assertEquals(line, iter.nextLine().toString());
182                 }
183             }
184             assertTrue(mTestFile.delete());
185         }
186     }
187 
188     /** Tests reading lines, then converting to long[]. */
189     @Test
testReadLineToArray()190     public void testReadLineToArray() throws Exception {
191         final long[][] data = getTestArray(800, 50);
192         try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
193             w.write(arrayToString(data));
194         }
195         long[] actual = new long[50];
196         try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
197             for (long[] expected : data) {
198                 CharBuffer cb = iter.nextLine();
199                 String before = cb.toString();
200                 assertEquals(50, KernelCpuProcStringReader.asLongs(cb, actual));
201                 assertArrayEquals(expected, actual);
202                 assertEquals("Buffer not reset to the pos before reading", before, cb.toString());
203             }
204         }
205     }
206 
207     /** Tests error handling of converting to long[]. */
208     @Test
testLineToArrayErrorHandling()209     public void testLineToArrayErrorHandling() {
210         long[] actual = new long[100];
211         String invalidChar = "123: -1234 456";
212         String overflow = "123: 999999999999999999999999999999999999999999999999999999999 123";
213         CharBuffer cb = CharBuffer.wrap("----" + invalidChar + "+++", 4, 4 + invalidChar.length());
214         assertEquals("Failed to report err for: " + invalidChar, -2,
215                 KernelCpuProcStringReader.asLongs(cb, actual));
216         assertEquals("Buffer not reset to the same pos before reading", invalidChar, cb.toString());
217 
218         cb = CharBuffer.wrap("----" + overflow + "+++", 4, 4 + overflow.length());
219         assertEquals("Failed to report err for: " + overflow, -3,
220                 KernelCpuProcStringReader.asLongs(cb, actual));
221         assertEquals("Buffer not reset to the pos before reading", overflow, cb.toString());
222     }
223 
224     /**
225      * Tests that reading a file over the limit (1MB) will return null.
226      */
227     @Test
testReadOverLimit()228     public void testReadOverLimit() throws Exception {
229         final String data = getTestString(1, 1024 * 1024 + 1);
230         try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
231             w.write(data);
232         }
233         try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
234             assertNull(iter);
235         }
236     }
237 
238     /**
239      * Tests concurrent reading with 5 threads.
240      */
241     @Test
testConcurrent()242     public void testConcurrent() throws Exception {
243         final String data = getTestString(200, 150);
244         final String data1 = getTestString(180, 120);
245         final String[] lines = data.split("\n");
246         final String[] lines1 = data1.split("\n");
247         final List<Throwable> errs = Collections.synchronizedList(new ArrayList<>());
248         try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
249             w.write(data);
250         }
251         // An additional thread for modifying the file content.
252         ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(11);
253         final CountDownLatch ready = new CountDownLatch(10);
254         final CountDownLatch start = new CountDownLatch(1);
255         final CountDownLatch modify = new CountDownLatch(1);
256         final CountDownLatch done = new CountDownLatch(10);
257 
258         // Schedules 5 threads to be executed together now, and 5 to be executed after file is
259         // modified.
260         for (int i = 0; i < 5; i++) {
261             threadPool.submit(() -> {
262                 ready.countDown();
263                 try {
264                     start.await();
265                     try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
266                         for (String line : lines) {
267                             assertEquals(line, iter.nextLine().toString());
268                         }
269                     }
270                 } catch (Throwable e) {
271                     errs.add(e);
272                 } finally {
273                     done.countDown();
274                 }
275             });
276             threadPool.submit(() -> {
277                 ready.countDown();
278                 try {
279                     start.await();
280                     // Wait for file modification.
281                     modify.await();
282                     try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
283                         for (String line : lines1) {
284                             assertEquals(line, iter.nextLine().toString());
285                         }
286                     }
287                 } catch (Throwable e) {
288                     errs.add(e);
289                 } finally {
290                     done.countDown();
291                 }
292             });
293         }
294 
295         assertTrue("Prep timed out", ready.await(100, TimeUnit.MILLISECONDS));
296         start.countDown();
297 
298         threadPool.schedule(() -> {
299             assertTrue(mTestFile.delete());
300             try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
301                 w.write(data1);
302             } catch (Throwable e) {
303                 errs.add(e);
304             } finally {
305                 modify.countDown();
306             }
307         }, 600, TimeUnit.MILLISECONDS);
308 
309         assertTrue("Execution timed out", done.await(3, TimeUnit.SECONDS));
310         threadPool.shutdownNow();
311 
312         StringWriter sw = new StringWriter();
313         PrintWriter pw = new PrintWriter(sw);
314         errs.forEach(e -> e.printStackTrace(pw));
315 
316         assertTrue("All Exceptions:\n" + sw.toString(), errs.isEmpty());
317     }
318 
getTestString(int lines, int charsPerLine)319     private String getTestString(int lines, int charsPerLine) {
320         StringBuffer sb = new StringBuffer();
321         for (int i = 0; i < lines; i++) {
322             for (int j = 0; j < charsPerLine; j++) {
323                 sb.append((char) (mRand.nextInt(93) + 32));
324             }
325             sb.append('\n');
326         }
327         return sb.toString();
328     }
329 
getTestArray(int lines, int numPerLine)330     private long[][] getTestArray(int lines, int numPerLine) {
331         return IntStream.range(0, lines).mapToObj(
332                 (i) -> mRand.longs(numPerLine, 0, Long.MAX_VALUE).toArray()).toArray(long[][]::new);
333     }
334 
arrayToString(long[][] array)335     private String arrayToString(long[][] array) {
336         StringBuffer sb = new StringBuffer();
337         for (int i = 0; i < array.length; i++) {
338             sb.append(array[i][0]).append(':');
339             for (int j = 1; j < array[0].length; j++) {
340                 sb.append(' ').append(array[i][j]);
341             }
342             sb.append('\n');
343         }
344         return sb.toString();
345     }
346 }
347