OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "components/proximity_auth/cryptauth/sync_scheduler_impl.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/macros.h" | |
10 #include "base/memory/ptr_util.h" | |
11 #include "base/timer/mock_timer.h" | |
12 #include "testing/gtest/include/gtest/gtest.h" | |
13 | |
14 namespace proximity_auth { | |
15 | |
16 using Strategy = SyncScheduler::Strategy; | |
17 using SyncState = SyncScheduler::SyncState; | |
18 | |
19 namespace { | |
20 | |
21 // Constants configuring the the scheduler. | |
22 const int kElapsedTimeDays = 40; | |
23 const int kRefreshPeriodDays = 30; | |
24 const int kRecoveryPeriodSeconds = 10; | |
25 const double kMaxJitterPercentage = 0.1; | |
26 const char kTestSchedulerName[] = "TestSyncSchedulerImpl"; | |
27 | |
28 // Returns true if |jittered_time_delta| is within the range of a jittered | |
29 // |base_time_delta| with a maximum of |max_jitter_ratio|. | |
30 bool IsTimeDeltaWithinJitter(const base::TimeDelta& base_time_delta, | |
31 const base::TimeDelta& jittered_time_delta, | |
32 double max_jitter_ratio) { | |
33 if (base_time_delta.is_zero()) | |
34 return jittered_time_delta.is_zero(); | |
35 | |
36 base::TimeDelta difference = | |
37 (jittered_time_delta - base_time_delta).magnitude(); | |
38 double percentage_of_base = | |
39 difference.InMillisecondsF() / base_time_delta.InMillisecondsF(); | |
40 return percentage_of_base < max_jitter_ratio; | |
41 } | |
42 | |
43 // Test harness for the SyncSchedulerImpl to create MockTimers. | |
44 class TestSyncSchedulerImpl : public SyncSchedulerImpl { | |
45 public: | |
46 TestSyncSchedulerImpl(Delegate* delegate, | |
47 base::TimeDelta refresh_period, | |
48 base::TimeDelta recovery_period, | |
49 double max_jitter_ratio) | |
50 : SyncSchedulerImpl(delegate, | |
51 refresh_period, | |
52 recovery_period, | |
53 max_jitter_ratio, | |
54 kTestSchedulerName) {} | |
55 | |
56 ~TestSyncSchedulerImpl() override {} | |
57 | |
58 base::MockTimer* timer() { return mock_timer_; } | |
59 | |
60 private: | |
61 std::unique_ptr<base::Timer> CreateTimer() override { | |
62 bool retain_user_task = false; | |
63 bool is_repeating = false; | |
64 mock_timer_ = new base::MockTimer(retain_user_task, is_repeating); | |
65 return base::WrapUnique(mock_timer_); | |
66 } | |
67 | |
68 // A timer instance for testing. Owned by the parent scheduler. | |
69 base::MockTimer* mock_timer_; | |
70 | |
71 DISALLOW_COPY_AND_ASSIGN(TestSyncSchedulerImpl); | |
72 }; | |
73 | |
74 } // namespace | |
75 | |
76 class ProximityAuthSyncSchedulerImplTest : public testing::Test, | |
77 public SyncSchedulerImpl::Delegate { | |
78 protected: | |
79 ProximityAuthSyncSchedulerImplTest() | |
80 : refresh_period_(base::TimeDelta::FromDays(kRefreshPeriodDays)), | |
81 base_recovery_period_( | |
82 base::TimeDelta::FromSeconds(kRecoveryPeriodSeconds)), | |
83 zero_elapsed_time_(base::TimeDelta::FromSeconds(0)), | |
84 scheduler_(new TestSyncSchedulerImpl(this, | |
85 refresh_period_, | |
86 base_recovery_period_, | |
87 0)) {} | |
88 | |
89 ~ProximityAuthSyncSchedulerImplTest() override {} | |
90 | |
91 void OnSyncRequested( | |
92 std::unique_ptr<SyncScheduler::SyncRequest> sync_request) override { | |
93 sync_request_ = std::move(sync_request); | |
94 } | |
95 | |
96 base::MockTimer* timer() { return scheduler_->timer(); } | |
97 | |
98 // The time deltas used to configure |scheduler_|. | |
99 base::TimeDelta refresh_period_; | |
100 base::TimeDelta base_recovery_period_; | |
101 base::TimeDelta zero_elapsed_time_; | |
102 | |
103 // The scheduler instance under test. | |
104 std::unique_ptr<TestSyncSchedulerImpl> scheduler_; | |
105 | |
106 std::unique_ptr<SyncScheduler::SyncRequest> sync_request_; | |
107 | |
108 DISALLOW_COPY_AND_ASSIGN(ProximityAuthSyncSchedulerImplTest); | |
109 }; | |
110 | |
111 TEST_F(ProximityAuthSyncSchedulerImplTest, ForceSyncSuccess) { | |
112 scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH); | |
113 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
114 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState()); | |
115 | |
116 scheduler_->ForceSync(); | |
117 EXPECT_EQ(SyncState::SYNC_IN_PROGRESS, scheduler_->GetSyncState()); | |
118 EXPECT_TRUE(sync_request_); | |
119 sync_request_->OnDidComplete(true); | |
120 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
121 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState()); | |
122 } | |
123 | |
124 TEST_F(ProximityAuthSyncSchedulerImplTest, ForceSyncFailure) { | |
125 scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH); | |
126 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
127 | |
128 scheduler_->ForceSync(); | |
129 EXPECT_TRUE(sync_request_); | |
130 sync_request_->OnDidComplete(false); | |
131 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy()); | |
132 } | |
133 | |
134 TEST_F(ProximityAuthSyncSchedulerImplTest, PeriodicRefreshSuccess) { | |
135 EXPECT_EQ(SyncState::NOT_STARTED, scheduler_->GetSyncState()); | |
136 scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH); | |
137 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
138 | |
139 EXPECT_EQ(refresh_period_, timer()->GetCurrentDelay()); | |
140 timer()->Fire(); | |
141 EXPECT_EQ(SyncState::SYNC_IN_PROGRESS, scheduler_->GetSyncState()); | |
142 ASSERT_TRUE(sync_request_.get()); | |
143 | |
144 sync_request_->OnDidComplete(true); | |
145 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState()); | |
146 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
147 } | |
148 | |
149 TEST_F(ProximityAuthSyncSchedulerImplTest, PeriodicRefreshFailure) { | |
150 scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH); | |
151 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
152 timer()->Fire(); | |
153 sync_request_->OnDidComplete(false); | |
154 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy()); | |
155 } | |
156 | |
157 TEST_F(ProximityAuthSyncSchedulerImplTest, AggressiveRecoverySuccess) { | |
158 scheduler_->Start(zero_elapsed_time_, Strategy::AGGRESSIVE_RECOVERY); | |
159 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy()); | |
160 | |
161 EXPECT_EQ(base_recovery_period_, timer()->GetCurrentDelay()); | |
162 timer()->Fire(); | |
163 EXPECT_EQ(SyncState::SYNC_IN_PROGRESS, scheduler_->GetSyncState()); | |
164 ASSERT_TRUE(sync_request_.get()); | |
165 | |
166 sync_request_->OnDidComplete(true); | |
167 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState()); | |
168 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
169 } | |
170 | |
171 TEST_F(ProximityAuthSyncSchedulerImplTest, AggressiveRecoveryFailure) { | |
172 scheduler_->Start(zero_elapsed_time_, Strategy::AGGRESSIVE_RECOVERY); | |
173 | |
174 timer()->Fire(); | |
175 sync_request_->OnDidComplete(false); | |
176 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy()); | |
177 } | |
178 | |
179 TEST_F(ProximityAuthSyncSchedulerImplTest, AggressiveRecoveryBackOff) { | |
180 scheduler_->Start(zero_elapsed_time_, Strategy::AGGRESSIVE_RECOVERY); | |
181 base::TimeDelta last_recovery_period = base::TimeDelta::FromSeconds(0); | |
182 | |
183 for (int i = 0; i < 20; ++i) { | |
184 timer()->Fire(); | |
185 EXPECT_EQ(SyncState::SYNC_IN_PROGRESS, scheduler_->GetSyncState()); | |
186 sync_request_->OnDidComplete(false); | |
187 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy()); | |
188 EXPECT_EQ(SyncState::WAITING_FOR_REFRESH, scheduler_->GetSyncState()); | |
189 | |
190 base::TimeDelta recovery_period = scheduler_->GetTimeToNextSync(); | |
191 EXPECT_LE(last_recovery_period, recovery_period); | |
192 last_recovery_period = recovery_period; | |
193 } | |
194 | |
195 // Backoffs should rapidly converge to the normal refresh period. | |
196 EXPECT_EQ(refresh_period_, last_recovery_period); | |
197 } | |
198 | |
199 TEST_F(ProximityAuthSyncSchedulerImplTest, RefreshFailureRecoverySuccess) { | |
200 scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH); | |
201 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
202 | |
203 timer()->Fire(); | |
204 sync_request_->OnDidComplete(false); | |
205 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy()); | |
206 | |
207 timer()->Fire(); | |
208 sync_request_->OnDidComplete(true); | |
209 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
210 } | |
211 | |
212 TEST_F(ProximityAuthSyncSchedulerImplTest, SyncImmediatelyForPeriodicRefresh) { | |
213 scheduler_->Start(base::TimeDelta::FromDays(kElapsedTimeDays), | |
214 Strategy::PERIODIC_REFRESH); | |
215 EXPECT_TRUE(scheduler_->GetTimeToNextSync().is_zero()); | |
216 EXPECT_TRUE(timer()->GetCurrentDelay().is_zero()); | |
217 timer()->Fire(); | |
218 EXPECT_TRUE(sync_request_); | |
219 | |
220 EXPECT_EQ(Strategy::PERIODIC_REFRESH, scheduler_->GetStrategy()); | |
221 } | |
222 | |
223 TEST_F(ProximityAuthSyncSchedulerImplTest, | |
224 SyncImmediatelyForAggressiveRecovery) { | |
225 scheduler_->Start(base::TimeDelta::FromDays(kElapsedTimeDays), | |
226 Strategy::AGGRESSIVE_RECOVERY); | |
227 EXPECT_TRUE(scheduler_->GetTimeToNextSync().is_zero()); | |
228 EXPECT_TRUE(timer()->GetCurrentDelay().is_zero()); | |
229 timer()->Fire(); | |
230 EXPECT_TRUE(sync_request_); | |
231 | |
232 EXPECT_EQ(Strategy::AGGRESSIVE_RECOVERY, scheduler_->GetStrategy()); | |
233 } | |
234 | |
235 TEST_F(ProximityAuthSyncSchedulerImplTest, InitialSyncShorterByElapsedTime) { | |
236 base::TimeDelta elapsed_time = base::TimeDelta::FromDays(2); | |
237 scheduler_->Start(elapsed_time, Strategy::PERIODIC_REFRESH); | |
238 EXPECT_EQ(refresh_period_ - elapsed_time, scheduler_->GetTimeToNextSync()); | |
239 timer()->Fire(); | |
240 EXPECT_TRUE(sync_request_); | |
241 } | |
242 | |
243 TEST_F(ProximityAuthSyncSchedulerImplTest, PeriodicRefreshJitter) { | |
244 scheduler_.reset(new TestSyncSchedulerImpl( | |
245 this, refresh_period_, base_recovery_period_, kMaxJitterPercentage)); | |
246 | |
247 scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH); | |
248 | |
249 base::TimeDelta cumulative_jitter = base::TimeDelta::FromSeconds(0); | |
250 for (int i = 0; i < 10; ++i) { | |
251 base::TimeDelta next_sync_delta = scheduler_->GetTimeToNextSync(); | |
252 cumulative_jitter += (next_sync_delta - refresh_period_).magnitude(); | |
253 EXPECT_TRUE(IsTimeDeltaWithinJitter(refresh_period_, next_sync_delta, | |
254 kMaxJitterPercentage)); | |
255 timer()->Fire(); | |
256 sync_request_->OnDidComplete(true); | |
257 } | |
258 | |
259 // The probablility that all periods are randomly equal to |refresh_period_| | |
260 // is so low that we would expect the heat death of the universe before this | |
261 // test flakes. | |
262 EXPECT_FALSE(cumulative_jitter.is_zero()); | |
263 } | |
264 | |
265 TEST_F(ProximityAuthSyncSchedulerImplTest, JitteredTimeDeltaIsNonNegative) { | |
266 base::TimeDelta zero_delta = base::TimeDelta::FromSeconds(0); | |
267 double max_jitter_ratio = 1; | |
268 scheduler_.reset(new TestSyncSchedulerImpl(this, zero_delta, zero_delta, | |
269 max_jitter_ratio)); | |
270 scheduler_->Start(zero_elapsed_time_, Strategy::PERIODIC_REFRESH); | |
271 | |
272 for (int i = 0; i < 10; ++i) { | |
273 base::TimeDelta next_sync_delta = scheduler_->GetTimeToNextSync(); | |
274 EXPECT_GE(zero_delta, next_sync_delta); | |
275 EXPECT_TRUE( | |
276 IsTimeDeltaWithinJitter(zero_delta, next_sync_delta, max_jitter_ratio)); | |
277 timer()->Fire(); | |
278 sync_request_->OnDidComplete(true); | |
279 } | |
280 } | |
281 | |
282 TEST_F(ProximityAuthSyncSchedulerImplTest, StartWithNegativeElapsedTime) { | |
283 // This could happen in rare cases where the system clock changes. | |
284 scheduler_->Start(base::TimeDelta::FromDays(-1000), | |
285 Strategy::PERIODIC_REFRESH); | |
286 | |
287 base::TimeDelta zero_delta = base::TimeDelta::FromSeconds(0); | |
288 EXPECT_EQ(zero_delta, scheduler_->GetTimeToNextSync()); | |
289 EXPECT_EQ(zero_delta, timer()->GetCurrentDelay()); | |
290 } | |
291 | |
292 } // namespace proximity_auth | |
OLD | NEW |