1085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki/* 2085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * Copyright (C) 2010 The Android Open Source Project 3085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * 4085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * Licensed under the Apache License, Version 2.0 (the "License"); 5085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * you may not use this file except in compliance with the License. 6085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * You may obtain a copy of the License at 7085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * 8085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * http://www.apache.org/licenses/LICENSE-2.0 9085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * 10085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * Unless required by applicable law or agreed to in writing, software 11085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * distributed under the License is distributed on an "AS IS" BASIS, 12085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * See the License for the specific language governing permissions and 14085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * limitations under the License. 15085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki */ 16085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 17085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onukipackage com.android.email; 18085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 19085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onukiimport android.os.Handler; 20085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onukiimport android.os.Message; 21085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onukiimport android.test.AndroidTestCase; 223555dc03bf2243dc275c9c832702abbee3b64387Jerry Xieimport android.test.suitebuilder.annotation.SmallTest; 23085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 245dcb447c04ccfc6bf3201ad127dcb1e9721f429aAndrew Sappersteinimport com.android.mail.utils.Clock; 2517d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xieimport com.android.mail.utils.Throttle; 265dcb447c04ccfc6bf3201ad127dcb1e9721f429aAndrew Sapperstein 27085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onukiimport java.util.Timer; 28085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onukiimport java.util.TimerTask; 29085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onukiimport java.util.concurrent.BlockingQueue; 30085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onukiimport java.util.concurrent.LinkedBlockingQueue; 31085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 323555dc03bf2243dc275c9c832702abbee3b64387Jerry Xie@SmallTest 33085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onukipublic class ThrottleTest extends AndroidTestCase { 34085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private static final int MIN_TIMEOUT = 100; 35085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private static final int MAX_TIMEOUT = 500; 36085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 37085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private final CountingRunnable mRunnable = new CountingRunnable(); 38085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private final MockClock mClock = new MockClock(); 39085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private final MockTimer mTimer = new MockTimer(mClock); 40085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private final Throttle mTarget = new Throttle("test", mRunnable, new CallItNowHandler(), 41085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki MIN_TIMEOUT, MAX_TIMEOUT, mClock, mTimer); 42085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 43085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki /** 44085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * Advance the clock. 45085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki */ 46085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private void advanceClock(int milliseconds) { 47085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mClock.advance(milliseconds); 48085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTimer.runExpiredTasks(); 49085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 50085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 51085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki /** 52085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * Gets two events. They're far apart enough that the timeout won't be extended. 53085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki */ 54085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public void testSingleCalls() { 55085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // T + 0 56085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.onEvent(); 57085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(0); 58085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(0, mRunnable.mCounter); 59085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 60085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // T + 99 61085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(99); 62085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(0, mRunnable.mCounter); // Still not called 63085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 64085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // T + 100 65085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(1); 66085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(1, mRunnable.mCounter); // Called 67085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 68085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // T + 10100 69085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(10000); 70085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(1, mRunnable.mCounter); 71085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 72085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // Do the same thing again. Should work in the same way. 73085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 74085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // T + 0 75085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.onEvent(); 76085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(0); 77085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(1, mRunnable.mCounter); 78085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 79085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // T + 99 80085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(99); 81085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(1, mRunnable.mCounter); // Still not called 82085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 83085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // T + 100 84085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(1); 85085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(2, mRunnable.mCounter); // Called 86085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 87085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // T + 10100 88085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(10000); 89085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(2, mRunnable.mCounter); 90085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 91085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 92085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki /** 93085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * Gets 5 events in a row in a short period. 94085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * 95085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * We only roughly check the consequence, as the detailed spec isn't really important. 96085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * Here, we check if the timeout is extended, and the callback get called less than 97085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * 5 times. 98085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki */ 99085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public void testMultiCalls() { 100085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.onEvent(); 101085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(1); 102085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.onEvent(); 103085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(1); 104085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.onEvent(); 105085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(1); 106085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.onEvent(); 107085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(1); 108085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.onEvent(); 109085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 110085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // Timeout should be extended 111085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertTrue(mTarget.getTimeoutForTest() > 100); 112085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 113085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // Shouldn't result in 5 callback calls. 114085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki advanceClock(2000); 115085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertTrue(mRunnable.mCounter < 5); 116085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 117085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 118085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public void testUpdateTimeout() { 119085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // Check initial value 120085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(100, mTarget.getTimeoutForTest()); 121085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 122085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // First call -- won't change the timeout 123085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.updateTimeout(); 124085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(100, mTarget.getTimeoutForTest()); 125085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 126085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // Call again in 10 ms -- will extend timeout. 127085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mClock.advance(10); 128085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.updateTimeout(); 129085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(200, mTarget.getTimeoutForTest()); 130085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 131085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // Call again in TIMEOUT_EXTEND_INTERAVL ms -- will extend timeout. 132085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mClock.advance(Throttle.TIMEOUT_EXTEND_INTERVAL); 133085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.updateTimeout(); 134085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(400, mTarget.getTimeoutForTest()); 135085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 136085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // Again -- timeout reaches max. 137085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mClock.advance(Throttle.TIMEOUT_EXTEND_INTERVAL); 138085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.updateTimeout(); 139085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(500, mTarget.getTimeoutForTest()); 140085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 141085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki // Call in TIMEOUT_EXTEND_INTERAVL + 1 ms -- timeout will get reset. 142085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mClock.advance(Throttle.TIMEOUT_EXTEND_INTERVAL + 1); 143085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTarget.updateTimeout(); 144085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki assertEquals(100, mTarget.getTimeoutForTest()); 145085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 146085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 147085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private static class CountingRunnable implements Runnable { 148085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public int mCounter; 149085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 150085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki @Override 151085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public void run() { 152085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mCounter++; 153085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 154085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 155085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 156085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki /** 157085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * Dummy {@link Handler} that executes {@link Runnable}s passed to {@link Handler#post} 158085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * immediately on the current thread. 159085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki */ 160085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private static class CallItNowHandler extends Handler { 161085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki @Override 162085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 163085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki msg.getCallback().run(); 164085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki return true; 165085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 166085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 167085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 168085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki /** 169085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * Substitute for {@link Timer} that works based on the provided {@link Clock}. 170085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki */ 171085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private static class MockTimer extends Timer { 172085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private final Clock mClock; 173085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 174085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private static class Entry { 175085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public long mScheduledTime; 176085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public TimerTask mTask; 177085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 178085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 179085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki private final BlockingQueue<Entry> mTasks = new LinkedBlockingQueue<Entry>(); 180085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 181085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public MockTimer(Clock clock) { 182085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mClock = clock; 183085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 184085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 185085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki @Override 186085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public void schedule(TimerTask task, long delay) { 187085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki if (delay == 0) { 188085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki task.run(); 189085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } else { 190085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki Entry e = new Entry(); 191085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki e.mScheduledTime = mClock.getTime() + delay; 192085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki e.mTask = task; 193085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTasks.offer(e); 194085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 195085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 196085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki 197085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki /** 198085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * {@link MockTimer} can't know when the clock advances. This method must be called 199085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki * whenever the (mock) current time changes. 200085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki */ 201085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki public void runExpiredTasks() { 202085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki while (!mTasks.isEmpty()) { 203085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki Entry e = mTasks.peek(); 204085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki if (e.mScheduledTime > mClock.getTime()) { 205085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki break; 206085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 207085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki e.mTask.run(); 208085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki mTasks.poll(); 209085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 210085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 211085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki } 212085f7eb1219f270faa8317a5c6069a562213cb83Makoto Onuki} 213