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