Home | History | Annotate | Download | only in common
      1 /*
      2  * Copyright (C) 2009 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.common;
     18 
     19 import android.content.SharedPreferences;
     20 import android.test.AndroidTestCase;
     21 import android.test.suitebuilder.annotation.MediumTest;
     22 import android.test.suitebuilder.annotation.SmallTest;
     23 
     24 public class OperationSchedulerTest extends AndroidTestCase {
     25     /**
     26      * OperationScheduler subclass which uses an artificial time.
     27      * Set {@link #timeMillis} to whatever value you like.
     28      */
     29     private class TimeTravelScheduler extends OperationScheduler {
     30         static final long DEFAULT_TIME = 1250146800000L;  // 13-Aug-2009, 12:00:00 am
     31         public long timeMillis = DEFAULT_TIME;
     32 
     33         @Override
     34         protected long currentTimeMillis() { return timeMillis; }
     35         public TimeTravelScheduler() { super(getFreshStorage()); }
     36     }
     37 
     38     private SharedPreferences getFreshStorage() {
     39         SharedPreferences sp = getContext().getSharedPreferences("OperationSchedulerTest", 0);
     40         sp.edit().clear().commit();
     41         return sp;
     42     }
     43 
     44     @MediumTest
     45     public void testScheduler() throws Exception {
     46         TimeTravelScheduler scheduler = new TimeTravelScheduler();
     47         OperationScheduler.Options options = new OperationScheduler.Options();
     48         assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
     49         assertEquals(0, scheduler.getLastSuccessTimeMillis());
     50         assertEquals(0, scheduler.getLastAttemptTimeMillis());
     51 
     52         long beforeTrigger = scheduler.timeMillis;
     53         scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
     54         assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
     55 
     56         // It will schedule for the later of the trigger and the moratorium...
     57         scheduler.setMoratoriumTimeMillis(beforeTrigger + 500000);
     58         assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
     59         scheduler.setMoratoriumTimeMillis(beforeTrigger + 1500000);
     60         assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
     61 
     62         // Test enable/disable toggle
     63         scheduler.setEnabledState(false);
     64         assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
     65         scheduler.setEnabledState(true);
     66         assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
     67 
     68         // Backoff interval after an error
     69         long beforeError = (scheduler.timeMillis += 100);
     70         scheduler.onTransientError();
     71         assertEquals(0, scheduler.getLastSuccessTimeMillis());
     72         assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
     73         assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
     74         options.backoffFixedMillis = 1000000;
     75         options.backoffIncrementalMillis = 500000;
     76         assertEquals(beforeError + 1500000, scheduler.getNextTimeMillis(options));
     77 
     78         // Two errors: backoff interval increases
     79         beforeError = (scheduler.timeMillis += 100);
     80         scheduler.onTransientError();
     81         assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
     82         assertEquals(beforeError + 2000000, scheduler.getNextTimeMillis(options));
     83 
     84         // Reset transient error: no backoff interval
     85         scheduler.resetTransientError();
     86         assertEquals(0, scheduler.getLastSuccessTimeMillis());
     87         assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
     88         assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
     89 
     90         // Permanent error holds true even if transient errors are reset
     91         // However, we remember that the transient error was reset...
     92         scheduler.onPermanentError();
     93         assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
     94         scheduler.resetTransientError();
     95         assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
     96         scheduler.resetPermanentError();
     97         assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
     98 
     99         // Success resets the trigger
    100         long beforeSuccess = (scheduler.timeMillis += 100);
    101         scheduler.onSuccess();
    102         assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
    103         assertEquals(beforeSuccess, scheduler.getLastSuccessTimeMillis());
    104         assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
    105 
    106         // The moratorium is not reset by success!
    107         scheduler.setTriggerTimeMillis(0);
    108         assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
    109         scheduler.setMoratoriumTimeMillis(0);
    110         assertEquals(beforeSuccess, scheduler.getNextTimeMillis(options));
    111 
    112         // Periodic interval after success
    113         options.periodicIntervalMillis = 250000;
    114         scheduler.setTriggerTimeMillis(Long.MAX_VALUE);
    115         assertEquals(beforeSuccess + 250000, scheduler.getNextTimeMillis(options));
    116 
    117         // Trigger minimum is also since the last success
    118         options.minTriggerMillis = 1000000;
    119         assertEquals(beforeSuccess + 1000000, scheduler.getNextTimeMillis(options));
    120     }
    121 
    122     @MediumTest
    123     public void testExponentialBackoff() throws Exception {
    124         TimeTravelScheduler scheduler = new TimeTravelScheduler();
    125         OperationScheduler.Options options = new OperationScheduler.Options();
    126         options.backoffFixedMillis = 100;
    127         options.backoffIncrementalMillis = 1000;
    128         options.backoffExponentialMillis = 10000;
    129         scheduler.setTriggerTimeMillis(0);
    130         scheduler.setEnabledState(true);
    131 
    132         // Backoff interval after an error
    133         long beforeError = (scheduler.timeMillis += 10);
    134         scheduler.onTransientError();
    135         assertEquals(0, scheduler.getLastSuccessTimeMillis());
    136         assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
    137         assertEquals(beforeError + 11100, scheduler.getNextTimeMillis(options));
    138 
    139         // Second error
    140         beforeError = (scheduler.timeMillis += 10);
    141         scheduler.onTransientError();
    142         assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
    143         assertEquals(beforeError + 22100, scheduler.getNextTimeMillis(options));
    144 
    145         // Third error
    146         beforeError = (scheduler.timeMillis += 10);
    147         scheduler.onTransientError();
    148         assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
    149         assertEquals(beforeError + 43100, scheduler.getNextTimeMillis(options));
    150 
    151         // Fourth error
    152         beforeError = (scheduler.timeMillis += 10);
    153         scheduler.onTransientError();
    154         assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
    155         assertEquals(beforeError + 84100, scheduler.getNextTimeMillis(options));
    156     }
    157 
    158     @MediumTest
    159     public void testExponentialBackoffBoundedByMoratorium() throws Exception {
    160         TimeTravelScheduler scheduler = new TimeTravelScheduler();
    161         scheduler.setTriggerTimeMillis(0);
    162         scheduler.setEnabledState(true);
    163         scheduler.timeMillis = System.currentTimeMillis();
    164 
    165         OperationScheduler.Options options = new OperationScheduler.Options();
    166         options.backoffFixedMillis = 100;
    167         options.backoffIncrementalMillis = 1000;
    168         options.backoffExponentialMillis = 10000;
    169         options.maxMoratoriumMillis = 24 * 3600 * 1000;
    170 
    171         for(int i = 1; i < 31; i++) {
    172             // report an error - increments the errorCount
    173             scheduler.onTransientError();
    174             final long nextTime = scheduler.getNextTimeMillis(options);
    175             final long timeAfterOperation = System.currentTimeMillis();
    176             assertTrue("Backoff is not bounded by max moratorium for iteration " + i,
    177                     nextTime < timeAfterOperation + options.maxMoratoriumMillis);
    178         }
    179     }
    180 
    181     @SmallTest
    182     public void testParseOptions() throws Exception {
    183          OperationScheduler.Options options = new OperationScheduler.Options();
    184          assertEquals(
    185                  "OperationScheduler.Options[backoff=0.0+5.0 max=86400.0 min=0.0 period=3600.0]",
    186                  OperationScheduler.parseOptions("3600", options).toString());
    187 
    188          assertEquals(
    189                  "OperationScheduler.Options[backoff=0.0+2.5 max=86400.0 min=0.0 period=3700.0]",
    190                  OperationScheduler.parseOptions("backoff=+2.5 3700", options).toString());
    191 
    192          assertEquals(
    193                  "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
    194                  OperationScheduler.parseOptions("max=12345.6 min=7 backoff=10 period=3800",
    195                          options).toString());
    196 
    197          assertEquals(
    198                 "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
    199                  OperationScheduler.parseOptions("", options).toString());
    200 
    201          assertEquals(
    202                  "OperationScheduler.Options[backoff=5.0+2.5+10.0 max=12345.6 min=7.0 period=3600.0]",
    203                  OperationScheduler.parseOptions("backoff=5.0++10.0 3600", options).toString());
    204     }
    205 
    206     @SmallTest
    207     public void testMoratoriumWithHttpDate() throws Exception {
    208         TimeTravelScheduler scheduler = new TimeTravelScheduler();
    209         OperationScheduler.Options options = new OperationScheduler.Options();
    210 
    211         long beforeTrigger = scheduler.timeMillis;
    212         scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
    213         assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
    214 
    215         scheduler.setMoratoriumTimeMillis(beforeTrigger + 2000000);
    216         assertEquals(beforeTrigger + 2000000, scheduler.getNextTimeMillis(options));
    217 
    218         long beforeMoratorium = scheduler.timeMillis;
    219         assertTrue(scheduler.setMoratoriumTimeHttp("3000"));
    220         long afterMoratorium = scheduler.timeMillis;
    221         assertTrue(beforeMoratorium + 3000000 <= scheduler.getNextTimeMillis(options));
    222         assertTrue(afterMoratorium + 3000000 >= scheduler.getNextTimeMillis(options));
    223 
    224         options.maxMoratoriumMillis = Long.MAX_VALUE / 2;
    225         assertTrue(scheduler.setMoratoriumTimeHttp("Fri, 31 Dec 2030 23:59:59 GMT"));
    226         assertEquals(1924991999000L, scheduler.getNextTimeMillis(options));
    227 
    228         assertFalse(scheduler.setMoratoriumTimeHttp("not actually a date"));
    229     }
    230 
    231     @SmallTest
    232     public void testClockRollbackScenario() throws Exception {
    233         TimeTravelScheduler scheduler = new TimeTravelScheduler();
    234         OperationScheduler.Options options = new OperationScheduler.Options();
    235         options.minTriggerMillis = 2000;
    236 
    237         // First, set up a scheduler with reasons to wait: a transient
    238         // error with backoff and a moratorium for a few minutes.
    239 
    240         long beforeTrigger = scheduler.timeMillis;
    241         long triggerTime = beforeTrigger - 10000000;
    242         scheduler.setTriggerTimeMillis(triggerTime);
    243         assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
    244         assertEquals(0, scheduler.getLastAttemptTimeMillis());
    245 
    246         long beforeSuccess = (scheduler.timeMillis += 100);
    247         scheduler.onSuccess();
    248         scheduler.setTriggerTimeMillis(triggerTime);
    249         assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
    250         assertEquals(beforeSuccess + 2000, scheduler.getNextTimeMillis(options));
    251 
    252         long beforeError = (scheduler.timeMillis += 100);
    253         scheduler.onTransientError();
    254         assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
    255         assertEquals(beforeError + 5000, scheduler.getNextTimeMillis(options));
    256 
    257         long beforeMoratorium = (scheduler.timeMillis += 100);
    258         scheduler.setMoratoriumTimeMillis(beforeTrigger + 1000000);
    259         assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
    260 
    261         // Now set the time back a few seconds.
    262         // The moratorium time should still be honored.
    263         long beforeRollback = (scheduler.timeMillis = beforeTrigger - 10000);
    264         assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
    265 
    266         // The rollback also moved the last-attempt clock back to the rollback time.
    267         assertEquals(scheduler.timeMillis, scheduler.getLastAttemptTimeMillis());
    268 
    269         // But if we set the time back more than a day, the moratorium
    270         // resets to the maximum moratorium (a day, by default), exposing
    271         // the original trigger time.
    272         beforeRollback = (scheduler.timeMillis = beforeTrigger - 100000000);
    273         assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
    274         assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
    275 
    276         // If we roll forward until after the re-set moratorium, then it expires.
    277         scheduler.timeMillis = triggerTime + 5000000;
    278         assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
    279         assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
    280         assertEquals(beforeRollback, scheduler.getLastSuccessTimeMillis());
    281     }
    282 }
    283