blob: 7a316056429aa00b4d8e1788226e42c9e1eb10e1 [file] [log] [blame]
/*
* 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);
modify.countDown();
} catch (Throwable e) {
errs.add(e);
}
}, 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();
}
}