Home | History | Annotate | Download | only in email
      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      */
     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      */
     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      */
     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();
    202                 if (e.mScheduledTime > mClock.getTime()) {
    203                     break;
    204                 }
    205                 e.mTask.run();
    206                 mTasks.poll();
    207             }
    208         }
    209     }
    210 }
    211