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 17package com.android.common; 18 19import android.content.SharedPreferences; 20import android.test.AndroidTestCase; 21import android.test.suitebuilder.annotation.MediumTest; 22import android.test.suitebuilder.annotation.SmallTest; 23 24public 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 @SmallTest 123 public void testParseOptions() throws Exception { 124 OperationScheduler.Options options = new OperationScheduler.Options(); 125 assertEquals( 126 "OperationScheduler.Options[backoff=0.0+5.0 max=86400.0 min=0.0 period=3600.0]", 127 OperationScheduler.parseOptions("3600", options).toString()); 128 129 assertEquals( 130 "OperationScheduler.Options[backoff=0.0+2.5 max=86400.0 min=0.0 period=3700.0]", 131 OperationScheduler.parseOptions("backoff=+2.5 3700", options).toString()); 132 133 assertEquals( 134 "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]", 135 OperationScheduler.parseOptions("max=12345.6 min=7 backoff=10 period=3800", 136 options).toString()); 137 138 assertEquals( 139 "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]", 140 OperationScheduler.parseOptions("", options).toString()); 141 } 142 143 @SmallTest 144 public void testMoratoriumWithHttpDate() throws Exception { 145 TimeTravelScheduler scheduler = new TimeTravelScheduler(); 146 OperationScheduler.Options options = new OperationScheduler.Options(); 147 148 long beforeTrigger = scheduler.timeMillis; 149 scheduler.setTriggerTimeMillis(beforeTrigger + 1000000); 150 assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options)); 151 152 scheduler.setMoratoriumTimeMillis(beforeTrigger + 2000000); 153 assertEquals(beforeTrigger + 2000000, scheduler.getNextTimeMillis(options)); 154 155 long beforeMoratorium = scheduler.timeMillis; 156 assertTrue(scheduler.setMoratoriumTimeHttp("3000")); 157 long afterMoratorium = scheduler.timeMillis; 158 assertTrue(beforeMoratorium + 3000000 <= scheduler.getNextTimeMillis(options)); 159 assertTrue(afterMoratorium + 3000000 >= scheduler.getNextTimeMillis(options)); 160 161 options.maxMoratoriumMillis = Long.MAX_VALUE / 2; 162 assertTrue(scheduler.setMoratoriumTimeHttp("Fri, 31 Dec 2030 23:59:59 GMT")); 163 assertEquals(1924991999000L, scheduler.getNextTimeMillis(options)); 164 165 assertFalse(scheduler.setMoratoriumTimeHttp("not actually a date")); 166 } 167 168 @SmallTest 169 public void testClockRollbackScenario() throws Exception { 170 TimeTravelScheduler scheduler = new TimeTravelScheduler(); 171 OperationScheduler.Options options = new OperationScheduler.Options(); 172 options.minTriggerMillis = 2000; 173 174 // First, set up a scheduler with reasons to wait: a transient 175 // error with backoff and a moratorium for a few minutes. 176 177 long beforeTrigger = scheduler.timeMillis; 178 long triggerTime = beforeTrigger - 10000000; 179 scheduler.setTriggerTimeMillis(triggerTime); 180 assertEquals(triggerTime, scheduler.getNextTimeMillis(options)); 181 assertEquals(0, scheduler.getLastAttemptTimeMillis()); 182 183 long beforeSuccess = (scheduler.timeMillis += 100); 184 scheduler.onSuccess(); 185 scheduler.setTriggerTimeMillis(triggerTime); 186 assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis()); 187 assertEquals(beforeSuccess + 2000, scheduler.getNextTimeMillis(options)); 188 189 long beforeError = (scheduler.timeMillis += 100); 190 scheduler.onTransientError(); 191 assertEquals(beforeError, scheduler.getLastAttemptTimeMillis()); 192 assertEquals(beforeError + 5000, scheduler.getNextTimeMillis(options)); 193 194 long beforeMoratorium = (scheduler.timeMillis += 100); 195 scheduler.setMoratoriumTimeMillis(beforeTrigger + 1000000); 196 assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options)); 197 198 // Now set the time back a few seconds. 199 // The moratorium time should still be honored. 200 long beforeRollback = (scheduler.timeMillis = beforeTrigger - 10000); 201 assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options)); 202 203 // The rollback also moved the last-attempt clock back to the rollback time. 204 assertEquals(scheduler.timeMillis, scheduler.getLastAttemptTimeMillis()); 205 206 // But if we set the time back more than a day, the moratorium 207 // resets to the maximum moratorium (a day, by default), exposing 208 // the original trigger time. 209 beforeRollback = (scheduler.timeMillis = beforeTrigger - 100000000); 210 assertEquals(triggerTime, scheduler.getNextTimeMillis(options)); 211 assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis()); 212 213 // If we roll forward until after the re-set moratorium, then it expires. 214 scheduler.timeMillis = triggerTime + 5000000; 215 assertEquals(triggerTime, scheduler.getNextTimeMillis(options)); 216 assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis()); 217 assertEquals(beforeRollback, scheduler.getLastSuccessTimeMillis()); 218 } 219} 220