| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.os; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import android.content.Context; |
| import android.os.FileUtils; |
| import android.os.SystemClock; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.filters.SmallTest; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.nio.CharBuffer; |
| import java.nio.file.Files; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Random; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.TimeUnit; |
| import java.util.stream.IntStream; |
| |
| /** |
| * Test class for {@link KernelCpuProcStringReader}. |
| * |
| * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcStringReaderTest |
| */ |
| @SmallTest |
| @RunWith(AndroidJUnit4.class) |
| public class KernelCpuProcStringReaderTest { |
| private File mRoot; |
| private File mTestDir; |
| private File mTestFile; |
| private Random mRand = new Random(12345); |
| private KernelCpuProcStringReader mReader; |
| |
| private Context getContext() { |
| return InstrumentationRegistry.getContext(); |
| } |
| |
| @Before |
| public void setUp() { |
| mTestDir = getContext().getDir("test", Context.MODE_PRIVATE); |
| mRoot = getContext().getFilesDir(); |
| mTestFile = new File(mTestDir, "test.file"); |
| mReader = new KernelCpuProcStringReader(mTestFile.getAbsolutePath()); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| FileUtils.deleteContents(mTestDir); |
| FileUtils.deleteContents(mRoot); |
| } |
| |
| |
| /** |
| * Tests that reading will return null if the file does not exist. |
| */ |
| @Test |
| public void testReadInvalidFile() throws Exception { |
| assertEquals(null, mReader.open()); |
| } |
| |
| /** |
| * Tests that reading will always return null after 5 failures. |
| */ |
| @Test |
| public void testReadErrorsLimit() throws Exception { |
| for (int i = 0; i < 3; i++) { |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) { |
| assertNull(iter); |
| } |
| SystemClock.sleep(50); |
| } |
| final String data = "018n9x134yrm9sry01298yMF1X980Ym908u98weruwe983^(*)0N)&tu09281my\n"; |
| try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { |
| w.write(data); |
| } |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) { |
| assertEquals(data.length(), iter.size()); |
| assertEquals(data, iter.nextLine().toString() + '\n'); |
| } |
| assertTrue(mTestFile.delete()); |
| for (int i = 0; i < 3; i++) { |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open(true)) { |
| assertNull(iter); |
| } |
| SystemClock.sleep(50); |
| } |
| try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { |
| w.write(data); |
| } |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open(true)) { |
| assertNull(iter); |
| } |
| } |
| |
| /** Tests nextLine functionality. */ |
| @Test |
| public void testReadLine() throws Exception { |
| 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" |
| + " 0 0 0 0 0 0 0 0 0 0 0 13\n" |
| + "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 " |
| + "0 1 1 1 0 2 0 221\n" |
| + "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" |
| + " 0 2 0 0 0 2 721\n" |
| + "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" |
| + " 1 2 10 0 0 0 1 58\n" |
| + "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" |
| + " 0 0 2 0 0 7 707\n"; |
| try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { |
| w.write(data); |
| } |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) { |
| assertEquals( |
| "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" |
| + " 0 0 0 0 0 0 0 13", |
| iter.nextLine().toString()); |
| assertEquals( |
| "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 0 1 1 1 0 2 0 221", |
| iter.nextLine().toString()); |
| long[] actual = new long[43]; |
| KernelCpuProcStringReader.asLongs(iter.nextLine(), actual); |
| assertArrayEquals( |
| new long[]{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, 0, 2, 0, 0, 0, 2, 721}, |
| actual); |
| assertEquals( |
| "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 1 2 10 0 0 0 1 58", |
| iter.nextLine().toString()); |
| assertEquals( |
| "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 0 0 2 0 0 7 707", |
| iter.nextLine().toString()); |
| } |
| } |
| |
| /** Stress tests read functionality. */ |
| @Test |
| public void testMultipleRead() throws Exception { |
| for (int i = 0; i < 100; i++) { |
| final String data = getTestString(600, 150); |
| try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { |
| w.write(data); |
| } |
| String[] lines = data.split("\n"); |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open(true)) { |
| for (String line : lines) { |
| assertEquals(line, iter.nextLine().toString()); |
| } |
| } |
| assertTrue(mTestFile.delete()); |
| } |
| } |
| |
| /** Tests reading lines, then converting to long[]. */ |
| @Test |
| public void testReadLineToArray() throws Exception { |
| final long[][] data = getTestArray(800, 50); |
| try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { |
| w.write(arrayToString(data)); |
| } |
| long[] actual = new long[50]; |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) { |
| for (long[] expected : data) { |
| CharBuffer cb = iter.nextLine(); |
| String before = cb.toString(); |
| assertEquals(50, KernelCpuProcStringReader.asLongs(cb, actual)); |
| assertArrayEquals(expected, actual); |
| assertEquals("Buffer not reset to the pos before reading", before, cb.toString()); |
| } |
| } |
| } |
| |
| /** Tests error handling of converting to long[]. */ |
| @Test |
| public void testLineToArrayErrorHandling() { |
| long[] actual = new long[100]; |
| String invalidChar = "123: -1234 456"; |
| String overflow = "123: 999999999999999999999999999999999999999999999999999999999 123"; |
| CharBuffer cb = CharBuffer.wrap("----" + invalidChar + "+++", 4, 4 + invalidChar.length()); |
| assertEquals("Failed to report err for: " + invalidChar, -2, |
| KernelCpuProcStringReader.asLongs(cb, actual)); |
| assertEquals("Buffer not reset to the same pos before reading", invalidChar, cb.toString()); |
| |
| cb = CharBuffer.wrap("----" + overflow + "+++", 4, 4 + overflow.length()); |
| assertEquals("Failed to report err for: " + overflow, -3, |
| KernelCpuProcStringReader.asLongs(cb, actual)); |
| assertEquals("Buffer not reset to the pos before reading", overflow, cb.toString()); |
| } |
| |
| /** |
| * Tests that reading a file over the limit (1MB) will return null. |
| */ |
| @Test |
| public void testReadOverLimit() throws Exception { |
| final String data = getTestString(1, 1024 * 1024 + 1); |
| try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { |
| w.write(data); |
| } |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) { |
| assertNull(iter); |
| } |
| } |
| |
| /** |
| * Tests concurrent reading with 5 threads. |
| */ |
| @Test |
| public void testConcurrent() throws Exception { |
| final String data = getTestString(200, 150); |
| final String data1 = getTestString(180, 120); |
| final String[] lines = data.split("\n"); |
| final String[] lines1 = data1.split("\n"); |
| final List<Throwable> errs = Collections.synchronizedList(new ArrayList<>()); |
| try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { |
| w.write(data); |
| } |
| // An additional thread for modifying the file content. |
| ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(11); |
| final CountDownLatch ready = new CountDownLatch(10); |
| final CountDownLatch start = new CountDownLatch(1); |
| final CountDownLatch modify = new CountDownLatch(1); |
| final CountDownLatch done = new CountDownLatch(10); |
| |
| // Schedules 5 threads to be executed together now, and 5 to be executed after file is |
| // modified. |
| for (int i = 0; i < 5; i++) { |
| threadPool.submit(() -> { |
| ready.countDown(); |
| try { |
| start.await(); |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) { |
| for (String line : lines) { |
| assertEquals(line, iter.nextLine().toString()); |
| } |
| } |
| } catch (Throwable e) { |
| errs.add(e); |
| } finally { |
| done.countDown(); |
| } |
| }); |
| threadPool.submit(() -> { |
| ready.countDown(); |
| try { |
| start.await(); |
| // Wait for file modification. |
| modify.await(); |
| try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) { |
| for (String line : lines1) { |
| assertEquals(line, iter.nextLine().toString()); |
| } |
| } |
| } catch (Throwable e) { |
| errs.add(e); |
| } finally { |
| done.countDown(); |
| } |
| }); |
| } |
| |
| assertTrue("Prep timed out", ready.await(100, TimeUnit.MILLISECONDS)); |
| start.countDown(); |
| |
| threadPool.schedule(() -> { |
| assertTrue(mTestFile.delete()); |
| try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { |
| w.write(data1); |
| } catch (Throwable e) { |
| errs.add(e); |
| } finally { |
| modify.countDown(); |
| } |
| }, 600, TimeUnit.MILLISECONDS); |
| |
| assertTrue("Execution timed out", done.await(3, TimeUnit.SECONDS)); |
| threadPool.shutdownNow(); |
| |
| StringWriter sw = new StringWriter(); |
| PrintWriter pw = new PrintWriter(sw); |
| errs.forEach(e -> e.printStackTrace(pw)); |
| |
| assertTrue("All Exceptions:\n" + sw.toString(), errs.isEmpty()); |
| } |
| |
| private String getTestString(int lines, int charsPerLine) { |
| StringBuffer sb = new StringBuffer(); |
| for (int i = 0; i < lines; i++) { |
| for (int j = 0; j < charsPerLine; j++) { |
| sb.append((char) (mRand.nextInt(93) + 32)); |
| } |
| sb.append('\n'); |
| } |
| return sb.toString(); |
| } |
| |
| private long[][] getTestArray(int lines, int numPerLine) { |
| return IntStream.range(0, lines).mapToObj( |
| (i) -> mRand.longs(numPerLine, 0, Long.MAX_VALUE).toArray()).toArray(long[][]::new); |
| } |
| |
| private String arrayToString(long[][] array) { |
| StringBuffer sb = new StringBuffer(); |
| for (int i = 0; i < array.length; i++) { |
| sb.append(array[i][0]).append(':'); |
| for (int j = 1; j < array[0].length; j++) { |
| sb.append(' ').append(array[i][j]); |
| } |
| sb.append('\n'); |
| } |
| return sb.toString(); |
| } |
| } |