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