1/*
2 * Copyright (C) 2017 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
17package com.android.internal.os;
18
19import static org.junit.Assert.assertArrayEquals;
20import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertFalse;
22import static org.junit.Assert.assertTrue;
23
24import android.support.test.filters.SmallTest;
25import android.support.test.runner.AndroidJUnit4;
26import android.util.SparseArray;
27
28import com.android.internal.os.KernelSingleUidTimeReader.Injector;
29
30import org.junit.Before;
31import org.junit.Test;
32import org.junit.runner.RunWith;
33
34import java.io.IOException;
35import java.nio.ByteBuffer;
36import java.nio.ByteOrder;
37import java.util.Arrays;
38
39@SmallTest
40@RunWith(AndroidJUnit4.class)
41public class KernelSingleUidTimeReaderTest {
42    private final static int TEST_UID = 2222;
43    private final static int TEST_FREQ_COUNT = 5;
44
45    private KernelSingleUidTimeReader mReader;
46    private TestInjector mInjector;
47
48    @Before
49    public void setUp() {
50        mInjector = new TestInjector();
51        mReader = new KernelSingleUidTimeReader(TEST_FREQ_COUNT, mInjector);
52    }
53
54    @Test
55    public void readDelta() {
56        final SparseArray<long[]> allLastCpuTimes = mReader.getLastUidCpuTimeMs();
57        long[] latestCpuTimes = new long[] {120, 130, 140, 150, 160};
58        mInjector.setData(latestCpuTimes);
59        long[] deltaCpuTimes = mReader.readDeltaMs(TEST_UID);
60        assertCpuTimesEqual(latestCpuTimes, deltaCpuTimes);
61        assertCpuTimesEqual(latestCpuTimes, allLastCpuTimes.get(TEST_UID));
62
63        long[] expectedDeltaTimes = new long[] {200, 340, 1230, 490, 4890};
64        for (int i = 0; i < latestCpuTimes.length; ++i) {
65            latestCpuTimes[i] += expectedDeltaTimes[i];
66        }
67        mInjector.setData(latestCpuTimes);
68        deltaCpuTimes = mReader.readDeltaMs(TEST_UID);
69        assertCpuTimesEqual(expectedDeltaTimes, deltaCpuTimes);
70        assertCpuTimesEqual(latestCpuTimes, allLastCpuTimes.get(TEST_UID));
71
72        // delta should be null if cpu times haven't changed
73        deltaCpuTimes = mReader.readDeltaMs(TEST_UID);
74        assertCpuTimesEqual(null, deltaCpuTimes);
75        assertCpuTimesEqual(latestCpuTimes, allLastCpuTimes.get(TEST_UID));
76
77        // Malformed data (-ve)
78        long[] malformedLatestTimes = new long[latestCpuTimes.length];
79        for (int i = 0; i < latestCpuTimes.length; ++i) {
80            if (i == 1) {
81                malformedLatestTimes[i] = -4;
82            } else {
83                malformedLatestTimes[i] = latestCpuTimes[i] + i * 42;
84            }
85        }
86        mInjector.setData(malformedLatestTimes);
87        deltaCpuTimes = mReader.readDeltaMs(TEST_UID);
88        assertCpuTimesEqual(null, deltaCpuTimes);
89        assertCpuTimesEqual(latestCpuTimes, allLastCpuTimes.get(TEST_UID));
90
91        // Malformed data (decreased)
92        malformedLatestTimes = new long[latestCpuTimes.length];
93        for (int i = 0; i < latestCpuTimes.length; ++i) {
94            if (i == 1) {
95                malformedLatestTimes[i] = latestCpuTimes[i] - 4;
96            } else {
97                malformedLatestTimes[i] = latestCpuTimes[i] + i * 42;
98            }
99        }
100        mInjector.setData(malformedLatestTimes);
101        deltaCpuTimes = mReader.readDeltaMs(TEST_UID);
102        assertCpuTimesEqual(null, deltaCpuTimes);
103        assertCpuTimesEqual(latestCpuTimes, allLastCpuTimes.get(TEST_UID));
104    }
105
106    @Test
107    public void readDelta_fileNotAvailable() {
108        mInjector.letReadDataThrowException(true);
109
110        for (int i = 0; i < KernelSingleUidTimeReader.TOTAL_READ_ERROR_COUNT; ++i) {
111            assertTrue(mReader.singleUidCpuTimesAvailable());
112            mReader.readDeltaMs(TEST_UID);
113        }
114        assertFalse(mReader.singleUidCpuTimesAvailable());
115    }
116
117    @Test
118    public void readDelta_incorrectCount() {
119        assertTrue(mReader.singleUidCpuTimesAvailable());
120
121        long[] cpuTimes = new long[TEST_FREQ_COUNT - 1];
122        for (int i = 0; i < cpuTimes.length; ++i) {
123            cpuTimes[i] = 111 + i;
124        }
125        mInjector.setData(cpuTimes);
126        assertCpuTimesEqual(null, mReader.readDeltaMs(TEST_UID));
127        assertFalse(mReader.singleUidCpuTimesAvailable());
128
129        // Reset
130        mReader.setSingleUidCpuTimesAvailable(true);
131
132        cpuTimes = new long[TEST_FREQ_COUNT + 1];
133        for (int i = 0; i < cpuTimes.length; ++i) {
134            cpuTimes[i] = 222 + i;
135        }
136        mInjector.setData(cpuTimes);
137        assertCpuTimesEqual(null, mReader.readDeltaMs(TEST_UID));
138        assertFalse(mReader.singleUidCpuTimesAvailable());
139    }
140
141    @Test
142    public void testComputeDelta() {
143        // proc file not available
144        mReader.setSingleUidCpuTimesAvailable(false);
145        long[] latestCpuTimes = new long[] {12, 13, 14, 15, 16};
146        long[] deltaCpuTimes = mReader.computeDelta(TEST_UID, latestCpuTimes);
147        assertCpuTimesEqual(null, deltaCpuTimes);
148
149        // cpu times have changed
150        mReader.setSingleUidCpuTimesAvailable(true);
151        SparseArray<long[]> allLastCpuTimes = mReader.getLastUidCpuTimeMs();
152        long[] lastCpuTimes = new long[] {12, 13, 14, 15, 16};
153        allLastCpuTimes.put(TEST_UID, lastCpuTimes);
154        long[] expectedDeltaTimes = new long[] {123, 324, 43, 989, 80};
155        for (int i = 0; i < latestCpuTimes.length; ++i) {
156            latestCpuTimes[i] = lastCpuTimes[i] + expectedDeltaTimes[i];
157        }
158        deltaCpuTimes = mReader.computeDelta(TEST_UID, latestCpuTimes);
159        assertCpuTimesEqual(expectedDeltaTimes, deltaCpuTimes);
160        assertCpuTimesEqual(latestCpuTimes, allLastCpuTimes.get(TEST_UID));
161
162        // no change in cpu times
163        deltaCpuTimes = mReader.computeDelta(TEST_UID, latestCpuTimes);
164        assertCpuTimesEqual(null, deltaCpuTimes);
165        assertCpuTimesEqual(latestCpuTimes, allLastCpuTimes.get(TEST_UID));
166
167        // Malformed cpu times (-ve)
168        long[] malformedLatestTimes = new long[latestCpuTimes.length];
169        for (int i = 0; i < latestCpuTimes.length; ++i) {
170            if (i == 1) {
171                malformedLatestTimes[i] = -4;
172            } else {
173                malformedLatestTimes[i] = latestCpuTimes[i] + i * 42;
174            }
175        }
176        deltaCpuTimes = mReader.computeDelta(TEST_UID, malformedLatestTimes);
177        assertCpuTimesEqual(null, deltaCpuTimes);
178        assertCpuTimesEqual(latestCpuTimes, allLastCpuTimes.get(TEST_UID));
179
180        // Malformed cpu times (decreased)
181        for (int i = 0; i < latestCpuTimes.length; ++i) {
182            if (i == 1) {
183                malformedLatestTimes[i] = latestCpuTimes[i] - 4;
184            } else {
185                malformedLatestTimes[i] = latestCpuTimes[i] + i * 42;
186            }
187        }
188        deltaCpuTimes = mReader.computeDelta(TEST_UID, malformedLatestTimes);
189        assertCpuTimesEqual(null, deltaCpuTimes);
190        assertCpuTimesEqual(latestCpuTimes, allLastCpuTimes.get(TEST_UID));
191    }
192
193    @Test
194    public void testGetDelta() {
195        // No last cpu times
196        long[] lastCpuTimes = null;
197        long[] latestCpuTimes = new long[] {12, 13, 14, 15, 16};
198        long[] deltaCpuTimes = mReader.getDeltaLocked(lastCpuTimes, latestCpuTimes);
199        assertCpuTimesEqual(latestCpuTimes, deltaCpuTimes);
200
201        // Latest cpu times are -ve
202        lastCpuTimes = new long[] {12, 13, 14, 15, 16};
203        latestCpuTimes = new long[] {15, -10, 19, 21, 23};
204        deltaCpuTimes = mReader.getDeltaLocked(lastCpuTimes, latestCpuTimes);
205        assertCpuTimesEqual(null, deltaCpuTimes);
206
207        // Latest cpu times are less than last cpu times
208        lastCpuTimes = new long[] {12, 13, 14, 15, 16};
209        latestCpuTimes = new long[] {15, 11, 21, 34, 171};
210        deltaCpuTimes = mReader.getDeltaLocked(lastCpuTimes, latestCpuTimes);
211        assertCpuTimesEqual(null, deltaCpuTimes);
212
213        lastCpuTimes = new long[] {12, 13, 14, 15, 16};
214        latestCpuTimes = new long[] {112, 213, 314, 415, 516};
215        deltaCpuTimes = mReader.getDeltaLocked(lastCpuTimes, latestCpuTimes);
216        assertCpuTimesEqual(new long[] {100, 200, 300, 400, 500}, deltaCpuTimes);
217    }
218
219    @Test
220    public void testRemoveUid() {
221        final SparseArray<long[]> lastUidCpuTimes = mReader.getLastUidCpuTimeMs();
222        lastUidCpuTimes.put(12, new long[] {});
223        lastUidCpuTimes.put(16, new long[] {});
224
225        mReader.removeUid(12);
226        assertFalse("Removal failed, cpuTimes=" + lastUidCpuTimes,
227                lastUidCpuTimes.indexOfKey(12) >= 0);
228        mReader.removeUid(16);
229        assertFalse("Removal failed, cpuTimes=" + lastUidCpuTimes,
230                lastUidCpuTimes.indexOfKey(16) >= 0);
231    }
232
233    @Test
234    public void testRemoveUidsRange() {
235        final SparseArray<long[]> lastUidCpuTimes = mReader.getLastUidCpuTimeMs();
236        final int startUid = 12;
237        final int endUid = 24;
238
239        for (int i = startUid; i <= endUid; ++i) {
240            lastUidCpuTimes.put(startUid, new long[] {});
241        }
242        mReader.removeUidsInRange(startUid, endUid);
243        assertEquals("There shouldn't be any items left, cpuTimes=" + lastUidCpuTimes,
244                0, lastUidCpuTimes.size());
245
246        for (int i = startUid; i <= endUid; ++i) {
247            lastUidCpuTimes.put(startUid, new long[] {});
248        }
249        mReader.removeUidsInRange(startUid - 1, endUid);
250        assertEquals("There shouldn't be any items left, cpuTimes=" + lastUidCpuTimes,
251                0, lastUidCpuTimes.size());
252
253        for (int i = startUid; i <= endUid; ++i) {
254            lastUidCpuTimes.put(startUid, new long[] {});
255        }
256        mReader.removeUidsInRange(startUid, endUid + 1);
257        assertEquals("There shouldn't be any items left, cpuTimes=" + lastUidCpuTimes,
258                0, lastUidCpuTimes.size());
259
260        for (int i = startUid; i <= endUid; ++i) {
261            lastUidCpuTimes.put(startUid, new long[] {});
262        }
263        mReader.removeUidsInRange(startUid - 1, endUid + 1);
264        assertEquals("There shouldn't be any items left, cpuTimes=" + lastUidCpuTimes,
265                0, lastUidCpuTimes.size());
266    }
267
268    private void assertCpuTimesEqual(long[] expected, long[] actual) {
269        assertArrayEquals("Expected=" + Arrays.toString(expected)
270                + ", Actual=" + Arrays.toString(actual), expected, actual);
271    }
272
273    class TestInjector extends Injector {
274        private byte[] mData;
275        private boolean mThrowExcpetion;
276
277        @Override
278        public byte[] readData(String procFile) throws IOException {
279            if (mThrowExcpetion) {
280                throw new IOException("In the test");
281            } else {
282                return mData;
283            }
284        }
285
286        public void setData(long[] cpuTimes) {
287            final ByteBuffer buffer = ByteBuffer.allocate(cpuTimes.length * Long.BYTES);
288            buffer.order(ByteOrder.nativeOrder());
289            for (long time : cpuTimes) {
290                buffer.putLong(time / 10);
291            }
292            mData = buffer.array();
293        }
294
295        public void letReadDataThrowException(boolean throwException) {
296            mThrowExcpetion = throwException;
297        }
298    }
299}
300