ThrottleTest.java revision 085f7eb1219f270faa8317a5c6069a562213cb83
1/* 2 * Copyright (C) 2010 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.email; 18 19import android.os.Handler; 20import android.os.Message; 21import android.test.AndroidTestCase; 22 23import java.util.Timer; 24import java.util.TimerTask; 25import java.util.concurrent.BlockingQueue; 26import java.util.concurrent.LinkedBlockingQueue; 27 28public class ThrottleTest extends AndroidTestCase { 29 private static final int MIN_TIMEOUT = 100; 30 private static final int MAX_TIMEOUT = 500; 31 32 private final CountingRunnable mRunnable = new CountingRunnable(); 33 private final MockClock mClock = new MockClock(); 34 private final MockTimer mTimer = new MockTimer(mClock); 35 private final Throttle mTarget = new Throttle("test", mRunnable, new CallItNowHandler(), 36 MIN_TIMEOUT, MAX_TIMEOUT, mClock, mTimer); 37 38 /** 39 * Advance the clock. 40 */ 41 private void advanceClock(int milliseconds) { 42 mClock.advance(milliseconds); 43 mTimer.runExpiredTasks(); 44 } 45 46 /** 47 * Gets two events. They're far apart enough that the timeout won't be extended. 48 */ 49 public void testSingleCalls() { 50 // T + 0 51 mTarget.onEvent(); 52 advanceClock(0); 53 assertEquals(0, mRunnable.mCounter); 54 55 // T + 99 56 advanceClock(99); 57 assertEquals(0, mRunnable.mCounter); // Still not called 58 59 // T + 100 60 advanceClock(1); 61 assertEquals(1, mRunnable.mCounter); // Called 62 63 // T + 10100 64 advanceClock(10000); 65 assertEquals(1, mRunnable.mCounter); 66 67 // Do the same thing again. Should work in the same way. 68 69 // T + 0 70 mTarget.onEvent(); 71 advanceClock(0); 72 assertEquals(1, mRunnable.mCounter); 73 74 // T + 99 75 advanceClock(99); 76 assertEquals(1, mRunnable.mCounter); // Still not called 77 78 // T + 100 79 advanceClock(1); 80 assertEquals(2, mRunnable.mCounter); // Called 81 82 // T + 10100 83 advanceClock(10000); 84 assertEquals(2, mRunnable.mCounter); 85 } 86 87 /** 88 * Gets 5 events in a row in a short period. 89 * 90 * We only roughly check the consequence, as the detailed spec isn't really important. 91 * Here, we check if the timeout is extended, and the callback get called less than 92 * 5 times. 93 */ 94 public void testMultiCalls() { 95 mTarget.onEvent(); 96 advanceClock(1); 97 mTarget.onEvent(); 98 advanceClock(1); 99 mTarget.onEvent(); 100 advanceClock(1); 101 mTarget.onEvent(); 102 advanceClock(1); 103 mTarget.onEvent(); 104 105 // Timeout should be extended 106 assertTrue(mTarget.getTimeoutForTest() > 100); 107 108 // Shouldn't result in 5 callback calls. 109 advanceClock(2000); 110 assertTrue(mRunnable.mCounter < 5); 111 } 112 113 public void testUpdateTimeout() { 114 // Check initial value 115 assertEquals(100, mTarget.getTimeoutForTest()); 116 117 // First call -- won't change the timeout 118 mTarget.updateTimeout(); 119 assertEquals(100, mTarget.getTimeoutForTest()); 120 121 // Call again in 10 ms -- will extend timeout. 122 mClock.advance(10); 123 mTarget.updateTimeout(); 124 assertEquals(200, mTarget.getTimeoutForTest()); 125 126 // Call again in TIMEOUT_EXTEND_INTERAVL ms -- will extend timeout. 127 mClock.advance(Throttle.TIMEOUT_EXTEND_INTERVAL); 128 mTarget.updateTimeout(); 129 assertEquals(400, mTarget.getTimeoutForTest()); 130 131 // Again -- timeout reaches max. 132 mClock.advance(Throttle.TIMEOUT_EXTEND_INTERVAL); 133 mTarget.updateTimeout(); 134 assertEquals(500, mTarget.getTimeoutForTest()); 135 136 // Call in TIMEOUT_EXTEND_INTERAVL + 1 ms -- timeout will get reset. 137 mClock.advance(Throttle.TIMEOUT_EXTEND_INTERVAL + 1); 138 mTarget.updateTimeout(); 139 assertEquals(100, mTarget.getTimeoutForTest()); 140 } 141 142 private static class CountingRunnable implements Runnable { 143 public int mCounter; 144 145 @Override 146 public void run() { 147 mCounter++; 148 } 149 } 150 151 /** 152 * Dummy {@link Handler} that executes {@link Runnable}s passed to {@link Handler#post} 153 * immediately on the current thread. 154 */ 155 private static class CallItNowHandler extends Handler { 156 @Override 157 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 158 msg.getCallback().run(); 159 return true; 160 } 161 } 162 163 /** 164 * Substitute for {@link Timer} that works based on the provided {@link Clock}. 165 */ 166 private static class MockTimer extends Timer { 167 private final Clock mClock; 168 169 private static class Entry { 170 public long mScheduledTime; 171 public TimerTask mTask; 172 } 173 174 private final BlockingQueue<Entry> mTasks = new LinkedBlockingQueue<Entry>(); 175 176 public MockTimer(Clock clock) { 177 mClock = clock; 178 } 179 180 @Override 181 public void schedule(TimerTask task, long delay) { 182 if (delay == 0) { 183 task.run(); 184 } else { 185 Entry e = new Entry(); 186 e.mScheduledTime = mClock.getTime() + delay; 187 e.mTask = task; 188 mTasks.offer(e); 189 } 190 } 191 192 /** 193 * {@link MockTimer} can't know when the clock advances. This method must be called 194 * whenever the (mock) current time changes. 195 */ 196 public void runExpiredTasks() { 197 while (!mTasks.isEmpty()) { 198 Entry e = mTasks.peek(); 199 if (e.mScheduledTime > mClock.getTime()) { 200 break; 201 } 202 e.mTask.run(); 203 mTasks.poll(); 204 } 205 } 206 } 207} 208