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 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      */
     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      */
     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      */
     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();
    204                 if (e.mScheduledTime > mClock.getTime()) {
    205                     break;
    206                 }
    207                 e.mTask.run();
    208                 mTasks.poll();
    209             }
    210         }
    211     }
    212 }
    213