Mike Ma | e45b9b1 | 2018-11-13 17:49:43 -0800 | [diff] [blame] | 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 | package com.android.internal.os; |
| 17 | |
| 18 | import static org.junit.Assert.assertEquals; |
| 19 | import static org.junit.Assert.assertTrue; |
| 20 | |
| 21 | import android.content.Context; |
| 22 | import android.os.FileUtils; |
| 23 | import android.support.test.InstrumentationRegistry; |
| 24 | import android.support.test.filters.SmallTest; |
| 25 | import android.support.test.runner.AndroidJUnit4; |
| 26 | import android.util.SparseLongArray; |
| 27 | |
| 28 | import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader; |
| 29 | |
| 30 | import org.junit.After; |
| 31 | import org.junit.Before; |
| 32 | import org.junit.Test; |
| 33 | import org.junit.runner.RunWith; |
| 34 | |
| 35 | import java.io.BufferedWriter; |
| 36 | import java.io.File; |
| 37 | import java.io.IOException; |
| 38 | import java.nio.file.Files; |
| 39 | import java.util.Random; |
| 40 | |
| 41 | /** |
| 42 | * Test class for {@link KernelCpuUidActiveTimeReader}. |
| 43 | * |
| 44 | * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest |
| 45 | */ |
| 46 | @SmallTest |
| 47 | @RunWith(AndroidJUnit4.class) |
| 48 | public class KernelCpuUidActiveTimeReaderTest { |
| 49 | private File mTestDir; |
| 50 | private File mTestFile; |
| 51 | private KernelCpuUidActiveTimeReader mReader; |
| 52 | private VerifiableCallback mCallback; |
| 53 | |
| 54 | private Random mRand = new Random(12345); |
| 55 | private final int mCpus = 4; |
| 56 | private final String mHeadline = "cpus: 4\n"; |
| 57 | private final int[] mUids = {0, 1, 22, 333, 4444, 55555}; |
| 58 | |
| 59 | private Context getContext() { |
| 60 | return InstrumentationRegistry.getContext(); |
| 61 | } |
| 62 | |
| 63 | @Before |
| 64 | public void setUp() { |
| 65 | mTestDir = getContext().getDir("test", Context.MODE_PRIVATE); |
| 66 | mTestFile = new File(mTestDir, "test.file"); |
| 67 | mReader = new KernelCpuUidActiveTimeReader( |
| 68 | new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false); |
| 69 | mCallback = new VerifiableCallback(); |
| 70 | } |
| 71 | |
| 72 | @After |
| 73 | public void tearDown() throws Exception { |
| 74 | FileUtils.deleteContents(mTestDir); |
| 75 | FileUtils.deleteContents(getContext().getFilesDir()); |
| 76 | } |
| 77 | |
| 78 | @Test |
| 79 | public void testReadDelta() throws Exception { |
| 80 | final long[][] times = increaseTime(new long[mUids.length][mCpus]); |
| 81 | writeToFile(mHeadline + uidLines(mUids, times)); |
| 82 | mReader.readDelta(mCallback); |
| 83 | for (int i = 0; i < mUids.length; ++i) { |
| 84 | mCallback.verify(mUids[i], getActiveTime(times[i])); |
| 85 | } |
| 86 | mCallback.verifyNoMoreInteractions(); |
| 87 | |
| 88 | // Verify that a second call will only return deltas. |
| 89 | mCallback.clear(); |
| 90 | final long[][] newTimes1 = increaseTime(times); |
| 91 | writeToFile(mHeadline + uidLines(mUids, newTimes1)); |
| 92 | mReader.readDelta(mCallback); |
| 93 | for (int i = 0; i < mUids.length; ++i) { |
| 94 | mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i])); |
| 95 | } |
| 96 | mCallback.verifyNoMoreInteractions(); |
| 97 | |
| 98 | // Verify that there won't be a callback if the proc file values didn't change. |
| 99 | mCallback.clear(); |
| 100 | mReader.readDelta(mCallback); |
| 101 | mCallback.verifyNoMoreInteractions(); |
| 102 | |
| 103 | // Verify that calling with a null callback doesn't result in any crashes |
| 104 | mCallback.clear(); |
| 105 | final long[][] newTimes2 = increaseTime(newTimes1); |
| 106 | writeToFile(mHeadline + uidLines(mUids, newTimes2)); |
| 107 | mReader.readDelta(null); |
| 108 | mCallback.verifyNoMoreInteractions(); |
| 109 | |
| 110 | // Verify that the readDelta call will only return deltas when |
| 111 | // the previous call had null callback. |
| 112 | mCallback.clear(); |
| 113 | final long[][] newTimes3 = increaseTime(newTimes2); |
| 114 | writeToFile(mHeadline + uidLines(mUids, newTimes3)); |
| 115 | mReader.readDelta(mCallback); |
| 116 | for (int i = 0; i < mUids.length; ++i) { |
| 117 | mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i])); |
| 118 | } |
| 119 | mCallback.verifyNoMoreInteractions(); |
| 120 | assertTrue(mTestFile.delete()); |
| 121 | } |
| 122 | |
| 123 | @Test |
| 124 | public void testReadAbsolute() throws Exception { |
| 125 | final long[][] times1 = increaseTime(new long[mUids.length][mCpus]); |
| 126 | writeToFile(mHeadline + uidLines(mUids, times1)); |
| 127 | mReader.readAbsolute(mCallback); |
| 128 | for (int i = 0; i < mUids.length; i++) { |
| 129 | mCallback.verify(mUids[i], getActiveTime(times1[i])); |
| 130 | } |
| 131 | mCallback.verifyNoMoreInteractions(); |
| 132 | |
| 133 | // Verify that a second call should still return absolute values |
| 134 | mCallback.clear(); |
| 135 | final long[][] times2 = increaseTime(times1); |
| 136 | writeToFile(mHeadline + uidLines(mUids, times2)); |
| 137 | mReader.readAbsolute(mCallback); |
| 138 | for (int i = 0; i < mUids.length; i++) { |
| 139 | mCallback.verify(mUids[i], getActiveTime(times2[i])); |
| 140 | } |
| 141 | mCallback.verifyNoMoreInteractions(); |
| 142 | assertTrue(mTestFile.delete()); |
| 143 | } |
| 144 | |
| 145 | @Test |
| 146 | public void testReadDeltaDecreasedTime() throws Exception { |
| 147 | final long[][] times1 = increaseTime(new long[mUids.length][mCpus]); |
| 148 | writeToFile(mHeadline + uidLines(mUids, times1)); |
| 149 | mReader.readDelta(mCallback); |
| 150 | |
| 151 | // Verify that there should not be a callback for a particular UID if its time decreases. |
| 152 | mCallback.clear(); |
| 153 | final long[][] times2 = increaseTime(times1); |
| 154 | System.arraycopy(times1[0], 0, times2[0], 0, mCpus); |
| 155 | times2[0][0] = 100; |
| 156 | writeToFile(mHeadline + uidLines(mUids, times2)); |
| 157 | mReader.readDelta(mCallback); |
| 158 | for (int i = 1; i < mUids.length; i++) { |
| 159 | mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i])); |
| 160 | } |
| 161 | mCallback.verifyNoMoreInteractions(); |
| 162 | assertTrue(mTestFile.delete()); |
| 163 | |
| 164 | // Verify that the internal state was not modified. |
| 165 | mCallback.clear(); |
| 166 | final long[][] times3 = increaseTime(times2); |
| 167 | times3[0] = increaseTime(times1)[0]; |
| 168 | writeToFile(mHeadline + uidLines(mUids, times3)); |
| 169 | mReader.readDelta(mCallback); |
| 170 | mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0])); |
| 171 | for (int i = 1; i < mUids.length; i++) { |
| 172 | mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i])); |
| 173 | } |
| 174 | mCallback.verifyNoMoreInteractions(); |
| 175 | } |
| 176 | |
| 177 | @Test |
| 178 | public void testReadDeltaNegativeTime() throws Exception { |
| 179 | final long[][] times1 = increaseTime(new long[mUids.length][mCpus]); |
| 180 | writeToFile(mHeadline + uidLines(mUids, times1)); |
| 181 | mReader.readDelta(mCallback); |
| 182 | |
| 183 | // Verify that there should not be a callback for a particular UID if its time is -ve. |
| 184 | mCallback.clear(); |
| 185 | final long[][] times2 = increaseTime(times1); |
| 186 | times2[0][0] *= -1; |
| 187 | writeToFile(mHeadline + uidLines(mUids, times2)); |
| 188 | mReader.readDelta(mCallback); |
| 189 | for (int i = 1; i < mUids.length; i++) { |
| 190 | mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i])); |
| 191 | } |
| 192 | mCallback.verifyNoMoreInteractions(); |
| 193 | assertTrue(mTestFile.delete()); |
| 194 | |
| 195 | // Verify that the internal state was not modified. |
| 196 | mCallback.clear(); |
| 197 | final long[][] times3 = increaseTime(times2); |
| 198 | times3[0] = increaseTime(times1)[0]; |
| 199 | writeToFile(mHeadline + uidLines(mUids, times3)); |
| 200 | mReader.readDelta(mCallback); |
| 201 | mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0])); |
| 202 | for (int i = 1; i < mUids.length; i++) { |
| 203 | mCallback.verify(mUids[i], getActiveTime(times3[i]) - getActiveTime(times2[i])); |
| 204 | } |
| 205 | mCallback.verifyNoMoreInteractions(); |
| 206 | } |
| 207 | |
| 208 | private String uidLines(int[] uids, long[][] times) { |
| 209 | StringBuffer sb = new StringBuffer(); |
| 210 | for (int i = 0; i < uids.length; i++) { |
| 211 | sb.append(uids[i]).append(':'); |
| 212 | for (int j = 0; j < times[i].length; j++) { |
| 213 | sb.append(' ').append(times[i][j] / 10); |
| 214 | } |
| 215 | sb.append('\n'); |
| 216 | } |
| 217 | return sb.toString(); |
| 218 | } |
| 219 | |
| 220 | private void writeToFile(String s) throws IOException { |
| 221 | try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) { |
| 222 | w.write(s); |
| 223 | w.flush(); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | private long[][] increaseTime(long[][] original) { |
| 228 | long[][] newTime = new long[original.length][original[0].length]; |
| 229 | for (int i = 0; i < original.length; i++) { |
| 230 | for (int j = 0; j < original[0].length; j++) { |
| 231 | newTime[i][j] = original[i][j] + mRand.nextInt(10000) * 1000 + 1000; |
| 232 | } |
| 233 | } |
| 234 | return newTime; |
| 235 | } |
| 236 | |
| 237 | private long getActiveTime(long[] times) { |
| 238 | return times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4; |
| 239 | } |
| 240 | |
| 241 | private class VerifiableCallback implements KernelCpuUidTimeReader.Callback<Long> { |
| 242 | SparseLongArray mData = new SparseLongArray(); |
| 243 | |
| 244 | public void verify(int uid, long time) { |
| 245 | assertEquals(time, mData.get(uid)); |
| 246 | mData.delete(uid); |
| 247 | } |
| 248 | |
| 249 | public void clear() { |
| 250 | mData.clear(); |
| 251 | } |
| 252 | |
| 253 | @Override |
| 254 | public void onUidCpuTime(int uid, Long time) { |
| 255 | mData.put(uid, time); |
| 256 | } |
| 257 | |
| 258 | public void verifyNoMoreInteractions() { |
| 259 | assertEquals(0, mData.size()); |
| 260 | } |
| 261 | } |
| 262 | } |