1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/proximity_auth/cryptauth/sync_scheduler_impl.h"
7 #include "base/timer/mock_timer.h"
8 #include "testing/gtest/include/gtest/gtest.h"
10 namespace proximity_auth
{
12 using Strategy
= SyncScheduler::Strategy
;
13 using SyncState
= SyncScheduler::SyncState
;
17 // Constants configuring the the scheduler.
18 const int kElapsedTimeDays
= 40;
19 const int kRefreshPeriodDays
= 30;
20 const int kRecoveryPeriodSeconds
= 10;
21 const double kMaxJitterPercentage
= 0.1;
22 const char kTestSchedulerName
[] = "TestSyncSchedulerImpl";
24 // Returns true if |jittered_time_delta| is within the range of a jittered
25 // |base_time_delta| with a maximum of |max_jitter_ratio|.
26 bool IsTimeDeltaWithinJitter(const base::TimeDelta
& base_time_delta
,
27 const base::TimeDelta
& jittered_time_delta
,
28 double max_jitter_ratio
) {
29 if (base_time_delta
.is_zero())
30 return jittered_time_delta
.is_zero();
32 base::TimeDelta difference
=
33 (jittered_time_delta
- base_time_delta
).magnitude();
34 double percentage_of_base
=
35 difference
.InMillisecondsF() / base_time_delta
.InMillisecondsF();
36 return percentage_of_base
< max_jitter_ratio
;
39 // Test harness for the SyncSchedulerImpl to create MockTimers.
40 class TestSyncSchedulerImpl
: public SyncSchedulerImpl
{
42 TestSyncSchedulerImpl(Delegate
* delegate
,
43 base::TimeDelta refresh_period
,
44 base::TimeDelta recovery_period
,
45 double max_jitter_ratio
)
46 : SyncSchedulerImpl(delegate
,
50 kTestSchedulerName
) {}
52 ~TestSyncSchedulerImpl() override
{}
54 base::MockTimer
* timer() { return mock_timer_
; }
57 scoped_ptr
<base::Timer
> CreateTimer() override
{
58 bool retain_user_task
= false;
59 bool is_repeating
= false;
60 mock_timer_
= new base::MockTimer(retain_user_task
, is_repeating
);
61 return make_scoped_ptr(mock_timer_
);
64 // A timer instance for testing. Owned by the parent scheduler.
65 base::MockTimer
* mock_timer_
;
67 DISALLOW_COPY_AND_ASSIGN(TestSyncSchedulerImpl
);
72 class ProximityAuthSyncSchedulerImplTest
: public testing::Test
,
73 public SyncSchedulerImpl::Delegate
{
75 ProximityAuthSyncSchedulerImplTest()
76 : refresh_period_(base::TimeDelta::FromDays(kRefreshPeriodDays
)),
77 base_recovery_period_(
78 base::TimeDelta::FromSeconds(kRecoveryPeriodSeconds
)),
79 zero_elapsed_time_(base::TimeDelta::FromSeconds(0)),
80 scheduler_(new TestSyncSchedulerImpl(this,
82 base_recovery_period_
,
85 ~ProximityAuthSyncSchedulerImplTest() override
{}
88 scoped_ptr
<SyncScheduler::SyncRequest
> sync_request
) override
{
89 sync_request_
= sync_request
.Pass();
92 base::MockTimer
* timer() { return scheduler_
->timer(); }
94 // The time deltas used to configure |scheduler_|.
95 base::TimeDelta refresh_period_
;
96 base::TimeDelta base_recovery_period_
;
97 base::TimeDelta zero_elapsed_time_
;
99 // The scheduler instance under test.
100 scoped_ptr
<TestSyncSchedulerImpl
> scheduler_
;
102 scoped_ptr
<SyncScheduler::SyncRequest
> sync_request_
;
104 DISALLOW_COPY_AND_ASSIGN(ProximityAuthSyncSchedulerImplTest
);
107 TEST_F(ProximityAuthSyncSchedulerImplTest
, ForceSyncSuccess
) {
108 scheduler_
->Start(zero_elapsed_time_
, Strategy::PERIODIC_REFRESH
);
109 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
110 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH
, scheduler_
->GetSyncState());
112 scheduler_
->ForceSync();
113 EXPECT_EQ(SyncState::SYNC_IN_PROGRESS
, scheduler_
->GetSyncState());
114 EXPECT_TRUE(sync_request_
);
115 sync_request_
->OnDidComplete(true);
116 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
117 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH
, scheduler_
->GetSyncState());
120 TEST_F(ProximityAuthSyncSchedulerImplTest
, ForceSyncFailure
) {
121 scheduler_
->Start(zero_elapsed_time_
, Strategy::PERIODIC_REFRESH
);
122 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
124 scheduler_
->ForceSync();
125 EXPECT_TRUE(sync_request_
);
126 sync_request_
->OnDidComplete(false);
127 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY
, scheduler_
->GetStrategy());
130 TEST_F(ProximityAuthSyncSchedulerImplTest
, PeriodicRefreshSuccess
) {
131 EXPECT_EQ(SyncState::NOT_STARTED
, scheduler_
->GetSyncState());
132 scheduler_
->Start(zero_elapsed_time_
, Strategy::PERIODIC_REFRESH
);
133 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
135 EXPECT_EQ(refresh_period_
, timer()->GetCurrentDelay());
137 EXPECT_EQ(SyncState::SYNC_IN_PROGRESS
, scheduler_
->GetSyncState());
138 ASSERT_TRUE(sync_request_
.get());
140 sync_request_
->OnDidComplete(true);
141 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH
, scheduler_
->GetSyncState());
142 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
145 TEST_F(ProximityAuthSyncSchedulerImplTest
, PeriodicRefreshFailure
) {
146 scheduler_
->Start(zero_elapsed_time_
, Strategy::PERIODIC_REFRESH
);
147 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
149 sync_request_
->OnDidComplete(false);
150 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY
, scheduler_
->GetStrategy());
153 TEST_F(ProximityAuthSyncSchedulerImplTest
, AggressiveRecoverySuccess
) {
154 scheduler_
->Start(zero_elapsed_time_
, Strategy::AGGRESSIVE_RECOVERY
);
155 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY
, scheduler_
->GetStrategy());
157 EXPECT_EQ(base_recovery_period_
, timer()->GetCurrentDelay());
159 EXPECT_EQ(SyncState::SYNC_IN_PROGRESS
, scheduler_
->GetSyncState());
160 ASSERT_TRUE(sync_request_
.get());
162 sync_request_
->OnDidComplete(true);
163 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH
, scheduler_
->GetSyncState());
164 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
167 TEST_F(ProximityAuthSyncSchedulerImplTest
, AggressiveRecoveryFailure
) {
168 scheduler_
->Start(zero_elapsed_time_
, Strategy::AGGRESSIVE_RECOVERY
);
171 sync_request_
->OnDidComplete(false);
172 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY
, scheduler_
->GetStrategy());
175 TEST_F(ProximityAuthSyncSchedulerImplTest
, AggressiveRecoveryBackOff
) {
176 scheduler_
->Start(zero_elapsed_time_
, Strategy::AGGRESSIVE_RECOVERY
);
177 base::TimeDelta last_recovery_period
= base::TimeDelta::FromSeconds(0);
179 for (int i
= 0; i
< 20; ++i
) {
181 EXPECT_EQ(SyncState::SYNC_IN_PROGRESS
, scheduler_
->GetSyncState());
182 sync_request_
->OnDidComplete(false);
183 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY
, scheduler_
->GetStrategy());
184 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH
, scheduler_
->GetSyncState());
186 base::TimeDelta recovery_period
= scheduler_
->GetTimeToNextSync();
187 EXPECT_LE(last_recovery_period
, recovery_period
);
188 last_recovery_period
= recovery_period
;
191 // Backoffs should rapidly converge to the normal refresh period.
192 EXPECT_EQ(refresh_period_
, last_recovery_period
);
195 TEST_F(ProximityAuthSyncSchedulerImplTest
, RefreshFailureRecoverySuccess
) {
196 scheduler_
->Start(zero_elapsed_time_
, Strategy::PERIODIC_REFRESH
);
197 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
200 sync_request_
->OnDidComplete(false);
201 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY
, scheduler_
->GetStrategy());
204 sync_request_
->OnDidComplete(true);
205 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
208 TEST_F(ProximityAuthSyncSchedulerImplTest
, SyncImmediatelyForPeriodicRefresh
) {
209 scheduler_
->Start(base::TimeDelta::FromDays(kElapsedTimeDays
),
210 Strategy::PERIODIC_REFRESH
);
211 EXPECT_TRUE(scheduler_
->GetTimeToNextSync().is_zero());
212 EXPECT_TRUE(timer()->GetCurrentDelay().is_zero());
214 EXPECT_TRUE(sync_request_
);
216 EXPECT_EQ(Strategy::PERIODIC_REFRESH
, scheduler_
->GetStrategy());
219 TEST_F(ProximityAuthSyncSchedulerImplTest
,
220 SyncImmediatelyForAggressiveRecovery
) {
221 scheduler_
->Start(base::TimeDelta::FromDays(kElapsedTimeDays
),
222 Strategy::AGGRESSIVE_RECOVERY
);
223 EXPECT_TRUE(scheduler_
->GetTimeToNextSync().is_zero());
224 EXPECT_TRUE(timer()->GetCurrentDelay().is_zero());
226 EXPECT_TRUE(sync_request_
);
228 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY
, scheduler_
->GetStrategy());
231 TEST_F(ProximityAuthSyncSchedulerImplTest
, InitialSyncShorterByElapsedTime
) {
232 base::TimeDelta elapsed_time
= base::TimeDelta::FromDays(2);
233 scheduler_
->Start(elapsed_time
, Strategy::PERIODIC_REFRESH
);
234 EXPECT_EQ(refresh_period_
- elapsed_time
, scheduler_
->GetTimeToNextSync());
236 EXPECT_TRUE(sync_request_
);
239 TEST_F(ProximityAuthSyncSchedulerImplTest
, PeriodicRefreshJitter
) {
240 scheduler_
.reset(new TestSyncSchedulerImpl(
241 this, refresh_period_
, base_recovery_period_
, kMaxJitterPercentage
));
243 scheduler_
->Start(zero_elapsed_time_
, Strategy::PERIODIC_REFRESH
);
245 base::TimeDelta cumulative_jitter
= base::TimeDelta::FromSeconds(0);
246 for (int i
= 0; i
< 10; ++i
) {
247 base::TimeDelta next_sync_delta
= scheduler_
->GetTimeToNextSync();
248 cumulative_jitter
+= (next_sync_delta
- refresh_period_
).magnitude();
249 EXPECT_TRUE(IsTimeDeltaWithinJitter(refresh_period_
, next_sync_delta
,
250 kMaxJitterPercentage
));
252 sync_request_
->OnDidComplete(true);
255 // The probablility that all periods are randomly equal to |refresh_period_|
256 // is so low that we would expect the heat death of the universe before this
258 EXPECT_FALSE(cumulative_jitter
.is_zero());
261 TEST_F(ProximityAuthSyncSchedulerImplTest
, JitteredTimeDeltaIsNonNegative
) {
262 base::TimeDelta zero_delta
= base::TimeDelta::FromSeconds(0);
263 double max_jitter_ratio
= 1;
264 scheduler_
.reset(new TestSyncSchedulerImpl(this, zero_delta
, zero_delta
,
266 scheduler_
->Start(zero_elapsed_time_
, Strategy::PERIODIC_REFRESH
);
268 for (int i
= 0; i
< 10; ++i
) {
269 base::TimeDelta next_sync_delta
= scheduler_
->GetTimeToNextSync();
270 EXPECT_GE(zero_delta
, next_sync_delta
);
272 IsTimeDeltaWithinJitter(zero_delta
, next_sync_delta
, max_jitter_ratio
));
274 sync_request_
->OnDidComplete(true);
278 TEST_F(ProximityAuthSyncSchedulerImplTest
, StartWithNegativeElapsedTime
) {
279 // This could happen in rare cases where the system clock changes.
280 scheduler_
->Start(base::TimeDelta::FromDays(-1000),
281 Strategy::PERIODIC_REFRESH
);
283 base::TimeDelta zero_delta
= base::TimeDelta::FromSeconds(0);
284 EXPECT_EQ(zero_delta
, scheduler_
->GetTimeToNextSync());
285 EXPECT_EQ(zero_delta
, timer()->GetCurrentDelay());
288 } // namespace proximity_auth