NetworkStatsHistoryTest.java revision a63ba59260cd1bb3f5c16e395ace45a61f1d4461
1/*
2 * Copyright (C) 2011 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 android.net;
18
19import static android.text.format.DateUtils.DAY_IN_MILLIS;
20import static android.text.format.DateUtils.HOUR_IN_MILLIS;
21import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
22import static android.text.format.DateUtils.SECOND_IN_MILLIS;
23import static android.text.format.DateUtils.WEEK_IN_MILLIS;
24import static android.text.format.DateUtils.YEAR_IN_MILLIS;
25
26import android.test.AndroidTestCase;
27import android.test.suitebuilder.annotation.SmallTest;
28import android.test.suitebuilder.annotation.Suppress;
29import android.util.Log;
30
31import com.android.frameworks.coretests.R;
32
33import java.io.DataInputStream;
34import java.util.Random;
35
36@SmallTest
37public class NetworkStatsHistoryTest extends AndroidTestCase {
38    private static final String TAG = "NetworkStatsHistoryTest";
39
40    private static final long TEST_START = 1194220800000L;
41
42    private NetworkStatsHistory stats;
43
44    @Override
45    protected void tearDown() throws Exception {
46        super.tearDown();
47        if (stats != null) {
48            assertConsistent(stats);
49        }
50    }
51
52    public void testReadOriginalVersion() throws Exception {
53        final DataInputStream in = new DataInputStream(
54                getContext().getResources().openRawResource(R.raw.history_v1));
55
56        NetworkStatsHistory.Entry entry = null;
57        try {
58            final NetworkStatsHistory history = new NetworkStatsHistory(in);
59            assertEquals(15 * SECOND_IN_MILLIS, history.getBucketDuration());
60
61            entry = history.getValues(0, entry);
62            assertEquals(29143L, entry.rxBytes);
63            assertEquals(6223L, entry.txBytes);
64
65            entry = history.getValues(history.size() - 1, entry);
66            assertEquals(1476L, entry.rxBytes);
67            assertEquals(838L, entry.txBytes);
68
69            entry = history.getValues(Long.MIN_VALUE, Long.MAX_VALUE, entry);
70            assertEquals(332401L, entry.rxBytes);
71            assertEquals(64314L, entry.txBytes);
72
73        } finally {
74            in.close();
75        }
76    }
77
78    public void testRecordSingleBucket() throws Exception {
79        final long BUCKET_SIZE = HOUR_IN_MILLIS;
80        stats = new NetworkStatsHistory(BUCKET_SIZE);
81
82        // record data into narrow window to get single bucket
83        stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L);
84
85        assertEquals(1, stats.size());
86        assertValues(stats, 0, 1024L, 2048L);
87    }
88
89    public void testRecordEqualBuckets() throws Exception {
90        final long bucketDuration = HOUR_IN_MILLIS;
91        stats = new NetworkStatsHistory(bucketDuration);
92
93        // split equally across two buckets
94        final long recordStart = TEST_START + (bucketDuration / 2);
95        stats.recordData(recordStart, recordStart + bucketDuration, 1024L, 128L);
96
97        assertEquals(2, stats.size());
98        assertValues(stats, 0, 512L, 64L);
99        assertValues(stats, 1, 512L, 64L);
100    }
101
102    public void testRecordTouchingBuckets() throws Exception {
103        final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS;
104        stats = new NetworkStatsHistory(BUCKET_SIZE);
105
106        // split almost completely into middle bucket, but with a few minutes
107        // overlap into neighboring buckets. total record is 20 minutes.
108        final long recordStart = (TEST_START + BUCKET_SIZE) - MINUTE_IN_MILLIS;
109        final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4);
110        stats.recordData(recordStart, recordEnd, 1000L, 5000L);
111
112        assertEquals(3, stats.size());
113        // first bucket should have (1/20 of value)
114        assertValues(stats, 0, 50L, 250L);
115        // second bucket should have (15/20 of value)
116        assertValues(stats, 1, 750L, 3750L);
117        // final bucket should have (4/20 of value)
118        assertValues(stats, 2, 200L, 1000L);
119    }
120
121    public void testRecordGapBuckets() throws Exception {
122        final long BUCKET_SIZE = HOUR_IN_MILLIS;
123        stats = new NetworkStatsHistory(BUCKET_SIZE);
124
125        // record some data today and next week with large gap
126        final long firstStart = TEST_START;
127        final long lastStart = TEST_START + WEEK_IN_MILLIS;
128        stats.recordData(firstStart, firstStart + SECOND_IN_MILLIS, 128L, 256L);
129        stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS, 64L, 512L);
130
131        // we should have two buckets, far apart from each other
132        assertEquals(2, stats.size());
133        assertValues(stats, 0, 128L, 256L);
134        assertValues(stats, 1, 64L, 512L);
135
136        // now record something in middle, spread across two buckets
137        final long middleStart = TEST_START + DAY_IN_MILLIS;
138        final long middleEnd = middleStart + (HOUR_IN_MILLIS * 2);
139        stats.recordData(middleStart, middleEnd, 2048L, 2048L);
140
141        // now should have four buckets, with new record in middle two buckets
142        assertEquals(4, stats.size());
143        assertValues(stats, 0, 128L, 256L);
144        assertValues(stats, 1, 1024L, 1024L);
145        assertValues(stats, 2, 1024L, 1024L);
146        assertValues(stats, 3, 64L, 512L);
147    }
148
149    public void testRecordOverlapBuckets() throws Exception {
150        final long BUCKET_SIZE = HOUR_IN_MILLIS;
151        stats = new NetworkStatsHistory(BUCKET_SIZE);
152
153        // record some data in one bucket, and another overlapping buckets
154        stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 256L, 256L);
155        final long midStart = TEST_START + (HOUR_IN_MILLIS / 2);
156        stats.recordData(midStart, midStart + HOUR_IN_MILLIS, 1024L, 1024L);
157
158        // should have two buckets, with some data mixed together
159        assertEquals(2, stats.size());
160        assertValues(stats, 0, 768L, 768L);
161        assertValues(stats, 1, 512L, 512L);
162    }
163
164    public void testRecordEntireGapIdentical() throws Exception {
165        // first, create two separate histories far apart
166        final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
167        stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L);
168
169        final long TEST_START_2 = TEST_START + DAY_IN_MILLIS;
170        final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS);
171        stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L);
172
173        // combine together with identical bucket size
174        stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
175        stats.recordEntireHistory(stats1);
176        stats.recordEntireHistory(stats2);
177
178        // first verify that totals match up
179        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 3000L, 1500L);
180
181        // now inspect internal buckets
182        assertValues(stats, 0, 1000L, 500L);
183        assertValues(stats, 1, 1000L, 500L);
184        assertValues(stats, 2, 500L, 250L);
185        assertValues(stats, 3, 500L, 250L);
186    }
187
188    public void testRecordEntireOverlapVaryingBuckets() throws Exception {
189        // create history just over hour bucket boundary
190        final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
191        stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L);
192
193        final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS;
194        final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS);
195        stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L);
196
197        // combine together with minute bucket size
198        stats = new NetworkStatsHistory(MINUTE_IN_MILLIS);
199        stats.recordEntireHistory(stats1);
200        stats.recordEntireHistory(stats2);
201
202        // first verify that totals match up
203        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L);
204
205        // now inspect internal buckets
206        assertValues(stats, 0, 10L, 10L);
207        assertValues(stats, 1, 20L, 20L);
208        assertValues(stats, 2, 20L, 20L);
209        assertValues(stats, 3, 20L, 20L);
210        assertValues(stats, 4, 20L, 20L);
211        assertValues(stats, 5, 20L, 20L);
212        assertValues(stats, 6, 10L, 10L);
213
214        // now combine using 15min buckets
215        stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4);
216        stats.recordEntireHistory(stats1);
217        stats.recordEntireHistory(stats2);
218
219        // first verify that totals match up
220        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L);
221
222        // and inspect buckets
223        assertValues(stats, 0, 200L, 200L);
224        assertValues(stats, 1, 150L, 150L);
225        assertValues(stats, 2, 150L, 150L);
226        assertValues(stats, 3, 150L, 150L);
227    }
228
229    public void testRemove() throws Exception {
230        stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
231
232        // record some data across 24 buckets
233        stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L);
234        assertEquals(24, stats.size());
235
236        // try removing far before buckets; should be no change
237        stats.removeBucketsBefore(TEST_START - YEAR_IN_MILLIS);
238        assertEquals(24, stats.size());
239
240        // try removing just moments into first bucket; should be no change
241        // since that bucket contains data beyond the cutoff
242        stats.removeBucketsBefore(TEST_START + SECOND_IN_MILLIS);
243        assertEquals(24, stats.size());
244
245        // try removing single bucket
246        stats.removeBucketsBefore(TEST_START + HOUR_IN_MILLIS);
247        assertEquals(23, stats.size());
248
249        // try removing multiple buckets
250        stats.removeBucketsBefore(TEST_START + (4 * HOUR_IN_MILLIS));
251        assertEquals(20, stats.size());
252
253        // try removing all buckets
254        stats.removeBucketsBefore(TEST_START + YEAR_IN_MILLIS);
255        assertEquals(0, stats.size());
256    }
257
258    public void testTotalData() throws Exception {
259        final long BUCKET_SIZE = HOUR_IN_MILLIS;
260        stats = new NetworkStatsHistory(BUCKET_SIZE);
261
262        // record uniform data across day
263        stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L);
264
265        // verify that total outside range is 0
266        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, 0L, 0L);
267
268        // verify total in first hour
269        assertValues(stats, TEST_START, TEST_START + HOUR_IN_MILLIS, 100L, 200L);
270
271        // verify total across 1.5 hours
272        assertValues(stats, TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), 150L, 300L);
273
274        // verify total beyond end
275        assertValues(stats, TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, 100L, 200L);
276
277        // verify everything total
278        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 2400L, 4800L);
279
280    }
281
282    @Suppress
283    public void testFuzzing() throws Exception {
284        try {
285            // fuzzing with random events, looking for crashes
286            final Random r = new Random();
287            for (int i = 0; i < 500; i++) {
288                stats = new NetworkStatsHistory(r.nextLong());
289                for (int j = 0; j < 10000; j++) {
290                    if (r.nextBoolean()) {
291                        // add range
292                        final long start = r.nextLong();
293                        final long end = start + r.nextInt();
294                        stats.recordData(start, end, r.nextLong(), r.nextLong());
295                    } else {
296                        // trim something
297                        stats.removeBucketsBefore(r.nextLong());
298                    }
299                }
300                assertConsistent(stats);
301            }
302        } catch (Throwable e) {
303            Log.e(TAG, String.valueOf(stats));
304            throw new RuntimeException(e);
305        }
306    }
307
308    private static void assertConsistent(NetworkStatsHistory stats) {
309        // verify timestamps are monotonic
310        long lastStart = Long.MIN_VALUE;
311        NetworkStatsHistory.Entry entry = null;
312        for (int i = 0; i < stats.size(); i++) {
313            entry = stats.getValues(i, entry);
314            assertTrue(lastStart < entry.bucketStart);
315            lastStart = entry.bucketStart;
316        }
317    }
318
319    private static void assertValues(
320            NetworkStatsHistory stats, int index, long rxBytes, long txBytes) {
321        final NetworkStatsHistory.Entry entry = stats.getValues(index, null);
322        assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
323        assertEquals("unexpected txBytes", txBytes, entry.txBytes);
324    }
325
326    private static void assertValues(
327            NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) {
328        final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
329        assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
330        assertEquals("unexpected txBytes", txBytes, entry.txBytes);
331    }
332
333}
334