NetworkStatsHistoryTest.java revision 434962e44ea93b1c4d216c55f636a435bf54aa54
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.size());
58        assertValues(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.size());
70        assertValues(stats, 0, 512L, 64L);
71        assertValues(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.size());
85        // first bucket should have (1/20 of value)
86        assertValues(stats, 0, 50L, 250L);
87        // second bucket should have (15/20 of value)
88        assertValues(stats, 1, 750L, 3750L);
89        // final bucket should have (4/20 of value)
90        assertValues(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.size());
105        assertValues(stats, 0, 128L, 256L);
106        assertValues(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.size());
115        assertValues(stats, 0, 128L, 256L);
116        assertValues(stats, 1, 1024L, 1024L);
117        assertValues(stats, 2, 1024L, 1024L);
118        assertValues(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.size());
132        assertValues(stats, 0, 768L, 768L);
133        assertValues(stats, 1, 512L, 512L);
134    }
135
136    public void testRecordEntireGapIdentical() throws Exception {
137        // first, create two separate histories far apart
138        final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
139        stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L);
140
141        final long TEST_START_2 = TEST_START + DAY_IN_MILLIS;
142        final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS);
143        stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L);
144
145        // combine together with identical bucket size
146        stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
147        stats.recordEntireHistory(stats1);
148        stats.recordEntireHistory(stats2);
149
150        // first verify that totals match up
151        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 3000L, 1500L);
152
153        // now inspect internal buckets
154        assertValues(stats, 0, 1000L, 500L);
155        assertValues(stats, 1, 1000L, 500L);
156        assertValues(stats, 2, 500L, 250L);
157        assertValues(stats, 3, 500L, 250L);
158    }
159
160    public void testRecordEntireOverlapVaryingBuckets() throws Exception {
161        // create history just over hour bucket boundary
162        final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
163        stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L);
164
165        final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS;
166        final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS);
167        stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L);
168
169        // combine together with minute bucket size
170        stats = new NetworkStatsHistory(MINUTE_IN_MILLIS);
171        stats.recordEntireHistory(stats1);
172        stats.recordEntireHistory(stats2);
173
174        // first verify that totals match up
175        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L);
176
177        // now inspect internal buckets
178        assertValues(stats, 0, 10L, 10L);
179        assertValues(stats, 1, 20L, 20L);
180        assertValues(stats, 2, 20L, 20L);
181        assertValues(stats, 3, 20L, 20L);
182        assertValues(stats, 4, 20L, 20L);
183        assertValues(stats, 5, 20L, 20L);
184        assertValues(stats, 6, 10L, 10L);
185
186        // now combine using 15min buckets
187        stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4);
188        stats.recordEntireHistory(stats1);
189        stats.recordEntireHistory(stats2);
190
191        // first verify that totals match up
192        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 650L, 650L);
193
194        // and inspect buckets
195        assertValues(stats, 0, 200L, 200L);
196        assertValues(stats, 1, 150L, 150L);
197        assertValues(stats, 2, 150L, 150L);
198        assertValues(stats, 3, 150L, 150L);
199    }
200
201    public void testRemove() throws Exception {
202        stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
203
204        // record some data across 24 buckets
205        stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L);
206        assertEquals(24, stats.size());
207
208        // try removing far before buckets; should be no change
209        stats.removeBucketsBefore(TEST_START - YEAR_IN_MILLIS);
210        assertEquals(24, stats.size());
211
212        // try removing just moments into first bucket; should be no change
213        // since that bucket contains data beyond the cutoff
214        stats.removeBucketsBefore(TEST_START + SECOND_IN_MILLIS);
215        assertEquals(24, stats.size());
216
217        // try removing single bucket
218        stats.removeBucketsBefore(TEST_START + HOUR_IN_MILLIS);
219        assertEquals(23, stats.size());
220
221        // try removing multiple buckets
222        stats.removeBucketsBefore(TEST_START + (4 * HOUR_IN_MILLIS));
223        assertEquals(20, stats.size());
224
225        // try removing all buckets
226        stats.removeBucketsBefore(TEST_START + YEAR_IN_MILLIS);
227        assertEquals(0, stats.size());
228    }
229
230    public void testTotalData() throws Exception {
231        final long BUCKET_SIZE = HOUR_IN_MILLIS;
232        stats = new NetworkStatsHistory(BUCKET_SIZE);
233
234        // record uniform data across day
235        stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L);
236
237        // verify that total outside range is 0
238        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, 0L, 0L);
239
240        // verify total in first hour
241        assertValues(stats, TEST_START, TEST_START + HOUR_IN_MILLIS, 100L, 200L);
242
243        // verify total across 1.5 hours
244        assertValues(stats, TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), 150L, 300L);
245
246        // verify total beyond end
247        assertValues(stats, TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, 100L, 200L);
248
249        // verify everything total
250        assertValues(stats, TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, 2400L, 4800L);
251
252    }
253
254    @Suppress
255    public void testFuzzing() throws Exception {
256        try {
257            // fuzzing with random events, looking for crashes
258            final Random r = new Random();
259            for (int i = 0; i < 500; i++) {
260                stats = new NetworkStatsHistory(r.nextLong());
261                for (int j = 0; j < 10000; j++) {
262                    if (r.nextBoolean()) {
263                        // add range
264                        final long start = r.nextLong();
265                        final long end = start + r.nextInt();
266                        stats.recordData(start, end, r.nextLong(), r.nextLong());
267                    } else {
268                        // trim something
269                        stats.removeBucketsBefore(r.nextLong());
270                    }
271                }
272                assertConsistent(stats);
273            }
274        } catch (Throwable e) {
275            Log.e(TAG, String.valueOf(stats));
276            throw new RuntimeException(e);
277        }
278    }
279
280    private static void assertConsistent(NetworkStatsHistory stats) {
281        // verify timestamps are monotonic
282        long lastStart = Long.MIN_VALUE;
283        NetworkStatsHistory.Entry entry = null;
284        for (int i = 0; i < stats.size(); i++) {
285            entry = stats.getValues(i, entry);
286            assertTrue(lastStart < entry.bucketStart);
287            lastStart = entry.bucketStart;
288        }
289    }
290
291    private static void assertValues(
292            NetworkStatsHistory stats, int index, long rxBytes, long txBytes) {
293        final NetworkStatsHistory.Entry entry = stats.getValues(index, null);
294        assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
295        assertEquals("unexpected txBytes", txBytes, entry.txBytes);
296    }
297
298    private static void assertValues(
299            NetworkStatsHistory stats, long start, long end, long rxBytes, long txBytes) {
300        final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
301        assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
302        assertEquals("unexpected txBytes", txBytes, entry.txBytes);
303    }
304
305}
306