1823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang/*
2823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Copyright (C) 2009 The Android Open Source Project
3823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *
4823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Licensed under the Apache License, Version 2.0 (the "License");
5823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * you may not use this file except in compliance with the License.
6823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * You may obtain a copy of the License at
7823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *
8823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *      http://www.apache.org/licenses/LICENSE-2.0
9823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang *
10823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Unless required by applicable law or agreed to in writing, software
11823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * distributed under the License is distributed on an "AS IS" BASIS,
12823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * See the License for the specific language governing permissions and
14823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * limitations under the License.
15823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */
16823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
17823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangpackage com.android.common;
18823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
19823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.content.SharedPreferences;
20823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.test.AndroidTestCase;
21823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.test.suitebuilder.annotation.MediumTest;
22823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.test.suitebuilder.annotation.SmallTest;
23823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
24823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangpublic class OperationSchedulerTest extends AndroidTestCase {
25823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    /**
26823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * OperationScheduler subclass which uses an artificial time.
27823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     * Set {@link #timeMillis} to whatever value you like.
28823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang     */
29823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    private class TimeTravelScheduler extends OperationScheduler {
30823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        static final long DEFAULT_TIME = 1250146800000L;  // 13-Aug-2009, 12:00:00 am
31823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        public long timeMillis = DEFAULT_TIME;
32823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
33823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        @Override
34823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        protected long currentTimeMillis() { return timeMillis; }
35823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        public TimeTravelScheduler() { super(getFreshStorage()); }
36823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
37823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
38823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    private SharedPreferences getFreshStorage() {
39823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        SharedPreferences sp = getContext().getSharedPreferences("OperationSchedulerTest", 0);
40823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        sp.edit().clear().commit();
41823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        return sp;
42823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
43823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
44823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    @MediumTest
45823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public void testScheduler() throws Exception {
46823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        TimeTravelScheduler scheduler = new TimeTravelScheduler();
47823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        OperationScheduler.Options options = new OperationScheduler.Options();
48823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
49823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(0, scheduler.getLastSuccessTimeMillis());
50823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(0, scheduler.getLastAttemptTimeMillis());
51823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
52823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeTrigger = scheduler.timeMillis;
53823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
54823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
55823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
56823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // It will schedule for the later of the trigger and the moratorium...
57823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setMoratoriumTimeMillis(beforeTrigger + 500000);
58823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
59823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setMoratoriumTimeMillis(beforeTrigger + 1500000);
60823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
61823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
62823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Test enable/disable toggle
63823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setEnabledState(false);
64823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
65823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setEnabledState(true);
66823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
67823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
68823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Backoff interval after an error
69823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeError = (scheduler.timeMillis += 100);
70823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.onTransientError();
71823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(0, scheduler.getLastSuccessTimeMillis());
72823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
73823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
74823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        options.backoffFixedMillis = 1000000;
75823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        options.backoffIncrementalMillis = 500000;
76823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeError + 1500000, scheduler.getNextTimeMillis(options));
77823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
78823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Two errors: backoff interval increases
79823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        beforeError = (scheduler.timeMillis += 100);
80823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.onTransientError();
81823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
82823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeError + 2000000, scheduler.getNextTimeMillis(options));
83823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
84823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Reset transient error: no backoff interval
85823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.resetTransientError();
86823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(0, scheduler.getLastSuccessTimeMillis());
87823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
88823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
89823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
90823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Permanent error holds true even if transient errors are reset
91823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // However, we remember that the transient error was reset...
92823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.onPermanentError();
93823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
94823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.resetTransientError();
95823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
96823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.resetPermanentError();
97823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
98823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
99823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Success resets the trigger
100823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeSuccess = (scheduler.timeMillis += 100);
101823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.onSuccess();
102823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
103823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeSuccess, scheduler.getLastSuccessTimeMillis());
104823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
105823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
106823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // The moratorium is not reset by success!
107823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setTriggerTimeMillis(0);
108823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
109823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setMoratoriumTimeMillis(0);
110823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeSuccess, scheduler.getNextTimeMillis(options));
111823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
112823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Periodic interval after success
113823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        options.periodicIntervalMillis = 250000;
114823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setTriggerTimeMillis(Long.MAX_VALUE);
115823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeSuccess + 250000, scheduler.getNextTimeMillis(options));
116823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
117823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Trigger minimum is also since the last success
118823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        options.minTriggerMillis = 1000000;
119823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeSuccess + 1000000, scheduler.getNextTimeMillis(options));
120823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
121823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
1221ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker    @MediumTest
1231ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker    public void testExponentialBackoff() throws Exception {
1241ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        TimeTravelScheduler scheduler = new TimeTravelScheduler();
1251ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        OperationScheduler.Options options = new OperationScheduler.Options();
1261ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        options.backoffFixedMillis = 100;
1271ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        options.backoffIncrementalMillis = 1000;
1281ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        options.backoffExponentialMillis = 10000;
1291ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        scheduler.setTriggerTimeMillis(0);
1301ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        scheduler.setEnabledState(true);
1311ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker
1321ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        // Backoff interval after an error
1331ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        long beforeError = (scheduler.timeMillis += 10);
1341ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        scheduler.onTransientError();
1351ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        assertEquals(0, scheduler.getLastSuccessTimeMillis());
1361ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
1371ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        assertEquals(beforeError + 11100, scheduler.getNextTimeMillis(options));
1381ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker
1391ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        // Second error
1401ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        beforeError = (scheduler.timeMillis += 10);
1411ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        scheduler.onTransientError();
1421ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
1431ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        assertEquals(beforeError + 22100, scheduler.getNextTimeMillis(options));
1441ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker
1451ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        // Third error
1461ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        beforeError = (scheduler.timeMillis += 10);
1471ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        scheduler.onTransientError();
1481ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
1491ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        assertEquals(beforeError + 43100, scheduler.getNextTimeMillis(options));
1501ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker
1511ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        // Fourth error
1521ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        beforeError = (scheduler.timeMillis += 10);
1531ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        scheduler.onTransientError();
1541ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
1551ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker        assertEquals(beforeError + 84100, scheduler.getNextTimeMillis(options));
1561ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker    }
1571ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker
158823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    @SmallTest
159823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public void testParseOptions() throws Exception {
160823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang         OperationScheduler.Options options = new OperationScheduler.Options();
161823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang         assertEquals(
162823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                 "OperationScheduler.Options[backoff=0.0+5.0 max=86400.0 min=0.0 period=3600.0]",
163823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                 OperationScheduler.parseOptions("3600", options).toString());
164823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
165823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang         assertEquals(
166823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                 "OperationScheduler.Options[backoff=0.0+2.5 max=86400.0 min=0.0 period=3700.0]",
167823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                 OperationScheduler.parseOptions("backoff=+2.5 3700", options).toString());
168823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
169823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang         assertEquals(
170823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                 "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
171823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                 OperationScheduler.parseOptions("max=12345.6 min=7 backoff=10 period=3800",
172823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                         options).toString());
173823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
174823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang         assertEquals(
175823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
176823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang                 OperationScheduler.parseOptions("", options).toString());
1771ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker
1781ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker         assertEquals(
1791ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker                 "OperationScheduler.Options[backoff=5.0+2.5+10.0 max=12345.6 min=7.0 period=3600.0]",
1801ad9f44796cad21c9d2166c33c3dd8ca3adc41b2Doug Zongker                 OperationScheduler.parseOptions("backoff=5.0++10.0 3600", options).toString());
181823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
182823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
183823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    @SmallTest
184823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public void testMoratoriumWithHttpDate() throws Exception {
185823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        TimeTravelScheduler scheduler = new TimeTravelScheduler();
186823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        OperationScheduler.Options options = new OperationScheduler.Options();
187823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
188823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeTrigger = scheduler.timeMillis;
189823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
190823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
191823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
192823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setMoratoriumTimeMillis(beforeTrigger + 2000000);
193823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 2000000, scheduler.getNextTimeMillis(options));
194823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
195823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeMoratorium = scheduler.timeMillis;
196823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertTrue(scheduler.setMoratoriumTimeHttp("3000"));
197823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long afterMoratorium = scheduler.timeMillis;
198823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertTrue(beforeMoratorium + 3000000 <= scheduler.getNextTimeMillis(options));
199823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertTrue(afterMoratorium + 3000000 >= scheduler.getNextTimeMillis(options));
200823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
201823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        options.maxMoratoriumMillis = Long.MAX_VALUE / 2;
202823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertTrue(scheduler.setMoratoriumTimeHttp("Fri, 31 Dec 2030 23:59:59 GMT"));
203823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(1924991999000L, scheduler.getNextTimeMillis(options));
204823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
205823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertFalse(scheduler.setMoratoriumTimeHttp("not actually a date"));
206823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
207823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
208823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    @SmallTest
209823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    public void testClockRollbackScenario() throws Exception {
210823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        TimeTravelScheduler scheduler = new TimeTravelScheduler();
211823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        OperationScheduler.Options options = new OperationScheduler.Options();
212823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        options.minTriggerMillis = 2000;
213823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
214823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // First, set up a scheduler with reasons to wait: a transient
215823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // error with backoff and a moratorium for a few minutes.
216823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
217823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeTrigger = scheduler.timeMillis;
218823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long triggerTime = beforeTrigger - 10000000;
219823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setTriggerTimeMillis(triggerTime);
220823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
221823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(0, scheduler.getLastAttemptTimeMillis());
222823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
223823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeSuccess = (scheduler.timeMillis += 100);
224823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.onSuccess();
225823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setTriggerTimeMillis(triggerTime);
226823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
227823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeSuccess + 2000, scheduler.getNextTimeMillis(options));
228823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
229823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeError = (scheduler.timeMillis += 100);
230823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.onTransientError();
231823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
232823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeError + 5000, scheduler.getNextTimeMillis(options));
233823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
234823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeMoratorium = (scheduler.timeMillis += 100);
235823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.setMoratoriumTimeMillis(beforeTrigger + 1000000);
236823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
237823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
238823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // Now set the time back a few seconds.
239823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // The moratorium time should still be honored.
240823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        long beforeRollback = (scheduler.timeMillis = beforeTrigger - 10000);
241823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
242823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
243823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // The rollback also moved the last-attempt clock back to the rollback time.
244823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(scheduler.timeMillis, scheduler.getLastAttemptTimeMillis());
245823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
246823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // But if we set the time back more than a day, the moratorium
247823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // resets to the maximum moratorium (a day, by default), exposing
248823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // the original trigger time.
249823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        beforeRollback = (scheduler.timeMillis = beforeTrigger - 100000000);
250823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
251823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
252823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang
253823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        // If we roll forward until after the re-set moratorium, then it expires.
254823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        scheduler.timeMillis = triggerTime + 5000000;
255823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
256823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
257823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang        assertEquals(beforeRollback, scheduler.getLastSuccessTimeMillis());
258823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang    }
259823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang}
260