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