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 @SmallTest 159 public void testParseOptions() throws Exception { 160 OperationScheduler.Options options = new OperationScheduler.Options(); 161 assertEquals( 162 "OperationScheduler.Options[backoff=0.0+5.0 max=86400.0 min=0.0 period=3600.0]", 163 OperationScheduler.parseOptions("3600", options).toString()); 164 165 assertEquals( 166 "OperationScheduler.Options[backoff=0.0+2.5 max=86400.0 min=0.0 period=3700.0]", 167 OperationScheduler.parseOptions("backoff=+2.5 3700", options).toString()); 168 169 assertEquals( 170 "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]", 171 OperationScheduler.parseOptions("max=12345.6 min=7 backoff=10 period=3800", 172 options).toString()); 173 174 assertEquals( 175 "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]", 176 OperationScheduler.parseOptions("", options).toString()); 177 178 assertEquals( 179 "OperationScheduler.Options[backoff=5.0+2.5+10.0 max=12345.6 min=7.0 period=3600.0]", 180 OperationScheduler.parseOptions("backoff=5.0++10.0 3600", options).toString()); 181 } 182 183 @SmallTest 184 public void testMoratoriumWithHttpDate() throws Exception { 185 TimeTravelScheduler scheduler = new TimeTravelScheduler(); 186 OperationScheduler.Options options = new OperationScheduler.Options(); 187 188 long beforeTrigger = scheduler.timeMillis; 189 scheduler.setTriggerTimeMillis(beforeTrigger + 1000000); 190 assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options)); 191 192 scheduler.setMoratoriumTimeMillis(beforeTrigger + 2000000); 193 assertEquals(beforeTrigger + 2000000, scheduler.getNextTimeMillis(options)); 194 195 long beforeMoratorium = scheduler.timeMillis; 196 assertTrue(scheduler.setMoratoriumTimeHttp("3000")); 197 long afterMoratorium = scheduler.timeMillis; 198 assertTrue(beforeMoratorium + 3000000 <= scheduler.getNextTimeMillis(options)); 199 assertTrue(afterMoratorium + 3000000 >= scheduler.getNextTimeMillis(options)); 200 201 options.maxMoratoriumMillis = Long.MAX_VALUE / 2; 202 assertTrue(scheduler.setMoratoriumTimeHttp("Fri, 31 Dec 2030 23:59:59 GMT")); 203 assertEquals(1924991999000L, scheduler.getNextTimeMillis(options)); 204 205 assertFalse(scheduler.setMoratoriumTimeHttp("not actually a date")); 206 } 207 208 @SmallTest 209 public void testClockRollbackScenario() throws Exception { 210 TimeTravelScheduler scheduler = new TimeTravelScheduler(); 211 OperationScheduler.Options options = new OperationScheduler.Options(); 212 options.minTriggerMillis = 2000; 213 214 // First, set up a scheduler with reasons to wait: a transient 215 // error with backoff and a moratorium for a few minutes. 216 217 long beforeTrigger = scheduler.timeMillis; 218 long triggerTime = beforeTrigger - 10000000; 219 scheduler.setTriggerTimeMillis(triggerTime); 220 assertEquals(triggerTime, scheduler.getNextTimeMillis(options)); 221 assertEquals(0, scheduler.getLastAttemptTimeMillis()); 222 223 long beforeSuccess = (scheduler.timeMillis += 100); 224 scheduler.onSuccess(); 225 scheduler.setTriggerTimeMillis(triggerTime); 226 assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis()); 227 assertEquals(beforeSuccess + 2000, scheduler.getNextTimeMillis(options)); 228 229 long beforeError = (scheduler.timeMillis += 100); 230 scheduler.onTransientError(); 231 assertEquals(beforeError, scheduler.getLastAttemptTimeMillis()); 232 assertEquals(beforeError + 5000, scheduler.getNextTimeMillis(options)); 233 234 long beforeMoratorium = (scheduler.timeMillis += 100); 235 scheduler.setMoratoriumTimeMillis(beforeTrigger + 1000000); 236 assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options)); 237 238 // Now set the time back a few seconds. 239 // The moratorium time should still be honored. 240 long beforeRollback = (scheduler.timeMillis = beforeTrigger - 10000); 241 assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options)); 242 243 // The rollback also moved the last-attempt clock back to the rollback time. 244 assertEquals(scheduler.timeMillis, scheduler.getLastAttemptTimeMillis()); 245 246 // But if we set the time back more than a day, the moratorium 247 // resets to the maximum moratorium (a day, by default), exposing 248 // the original trigger time. 249 beforeRollback = (scheduler.timeMillis = beforeTrigger - 100000000); 250 assertEquals(triggerTime, scheduler.getNextTimeMillis(options)); 251 assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis()); 252 253 // If we roll forward until after the re-set moratorium, then it expires. 254 scheduler.timeMillis = triggerTime + 5000000; 255 assertEquals(triggerTime, scheduler.getNextTimeMillis(options)); 256 assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis()); 257 assertEquals(beforeRollback, scheduler.getLastSuccessTimeMillis()); 258 } 259 } 260