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