| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sync/notifier/registration_manager.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <cstddef> |
| #include <deque> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/stl_util.h" |
| #include "google/cacheinvalidation/include/invalidation-client.h" |
| #include "sync/notifier/invalidation_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace syncer { |
| namespace { |
| |
| // Fake registration manager that lets you override jitter. |
| class FakeRegistrationManager : public RegistrationManager { |
| public: |
| explicit FakeRegistrationManager( |
| invalidation::InvalidationClient* invalidation_client) |
| : RegistrationManager(invalidation_client), |
| jitter_(0.0) {} |
| |
| virtual ~FakeRegistrationManager() {} |
| |
| void SetJitter(double jitter) { |
| jitter_ = jitter; |
| } |
| |
| protected: |
| virtual double GetJitter() OVERRIDE { |
| return jitter_; |
| } |
| |
| private: |
| double jitter_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeRegistrationManager); |
| }; |
| |
| // Fake invalidation client that just stores the currently-registered |
| // object IDs. |
| class FakeInvalidationClient : public invalidation::InvalidationClient { |
| public: |
| FakeInvalidationClient() {} |
| |
| virtual ~FakeInvalidationClient() {} |
| |
| void LoseRegistration(const invalidation::ObjectId& oid) { |
| EXPECT_TRUE(ContainsKey(registered_ids_, oid)); |
| registered_ids_.erase(oid); |
| } |
| |
| void LoseAllRegistrations() { |
| registered_ids_.clear(); |
| } |
| |
| // invalidation::InvalidationClient implementation. |
| |
| virtual void Start() OVERRIDE {} |
| virtual void Stop() OVERRIDE {} |
| virtual void Acknowledge(const invalidation::AckHandle& handle) OVERRIDE {} |
| |
| virtual void Register(const invalidation::ObjectId& oid) OVERRIDE { |
| EXPECT_FALSE(ContainsKey(registered_ids_, oid)); |
| registered_ids_.insert(oid); |
| } |
| |
| virtual void Register( |
| const std::vector<invalidation::ObjectId>& oids) OVERRIDE { |
| // Unused for now. |
| } |
| |
| virtual void Unregister(const invalidation::ObjectId& oid) OVERRIDE { |
| EXPECT_TRUE(ContainsKey(registered_ids_, oid)); |
| registered_ids_.erase(oid); |
| } |
| |
| virtual void Unregister( |
| const std::vector<invalidation::ObjectId>& oids) OVERRIDE { |
| // Unused for now. |
| } |
| |
| const ObjectIdSet& GetRegisteredIdsForTest() const { |
| return registered_ids_; |
| } |
| |
| private: |
| ObjectIdSet registered_ids_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeInvalidationClient); |
| }; |
| |
| size_t kObjectIdsCount = 5; |
| |
| invalidation::ObjectId GetIdForIndex(size_t index) { |
| char name[2] = "a"; |
| name[0] += static_cast<char>(index); |
| return invalidation::ObjectId(1 + index, name); |
| } |
| |
| ObjectIdSet GetSequenceOfIdsStartingAt(size_t start, size_t count) { |
| ObjectIdSet ids; |
| for (size_t i = start; i < start + count; ++i) |
| ids.insert(GetIdForIndex(i)); |
| return ids; |
| } |
| |
| ObjectIdSet GetSequenceOfIds(size_t count) { |
| return GetSequenceOfIdsStartingAt(0, count); |
| } |
| |
| void ExpectPendingRegistrations( |
| const ObjectIdSet& expected_pending_ids, |
| double expected_delay_seconds, |
| const RegistrationManager::PendingRegistrationMap& pending_registrations) { |
| ObjectIdSet pending_ids; |
| for (RegistrationManager::PendingRegistrationMap::const_iterator it = |
| pending_registrations.begin(); it != pending_registrations.end(); |
| ++it) { |
| SCOPED_TRACE(ObjectIdToString(it->first)); |
| pending_ids.insert(it->first); |
| base::TimeDelta offset = |
| it->second.last_registration_request - |
| it->second.registration_attempt; |
| base::TimeDelta expected_delay = |
| base::TimeDelta::FromSeconds( |
| static_cast<int64>(expected_delay_seconds)) + offset; |
| // TODO(akalin): Add base::PrintTo() for base::Time and |
| // base::TimeDeltas. |
| EXPECT_EQ(expected_delay, it->second.delay) |
| << expected_delay.InMicroseconds() |
| << ", " << it->second.delay.InMicroseconds(); |
| if (it->second.delay <= base::TimeDelta()) { |
| EXPECT_EQ(base::TimeDelta(), it->second.actual_delay); |
| } else { |
| EXPECT_EQ(it->second.actual_delay, it->second.delay); |
| } |
| } |
| EXPECT_EQ(expected_pending_ids, pending_ids); |
| } |
| |
| class RegistrationManagerTest : public testing::Test { |
| protected: |
| RegistrationManagerTest() |
| : fake_registration_manager_(&fake_invalidation_client_) {} |
| |
| virtual ~RegistrationManagerTest() {} |
| |
| void LoseRegistrations(const ObjectIdSet& oids) { |
| for (ObjectIdSet::const_iterator it = oids.begin(); it != oids.end(); |
| ++it) { |
| fake_invalidation_client_.LoseRegistration(*it); |
| fake_registration_manager_.MarkRegistrationLost(*it); |
| } |
| } |
| |
| void DisableIds(const ObjectIdSet& oids) { |
| for (ObjectIdSet::const_iterator it = oids.begin(); it != oids.end(); |
| ++it) { |
| fake_invalidation_client_.LoseRegistration(*it); |
| fake_registration_manager_.DisableId(*it); |
| } |
| } |
| |
| // Used by MarkRegistrationLostBackoff* tests. |
| void RunBackoffTest(double jitter) { |
| fake_registration_manager_.SetJitter(jitter); |
| ObjectIdSet ids = GetSequenceOfIds(kObjectIdsCount); |
| fake_registration_manager_.UpdateRegisteredIds(ids); |
| |
| // Lose some ids. |
| ObjectIdSet lost_ids = GetSequenceOfIds(2); |
| LoseRegistrations(lost_ids); |
| ExpectPendingRegistrations( |
| lost_ids, 0.0, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| |
| // Trigger another failure to start delaying. |
| fake_registration_manager_.FirePendingRegistrationsForTest(); |
| LoseRegistrations(lost_ids); |
| |
| double scaled_jitter = |
| jitter * RegistrationManager::kRegistrationDelayMaxJitter; |
| |
| double expected_delay = |
| RegistrationManager::kInitialRegistrationDelaySeconds * |
| (1.0 + scaled_jitter); |
| expected_delay = std::floor(expected_delay); |
| ExpectPendingRegistrations( |
| lost_ids, expected_delay, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| |
| // Trigger another failure. |
| fake_registration_manager_.FirePendingRegistrationsForTest(); |
| LoseRegistrations(lost_ids); |
| expected_delay *= |
| RegistrationManager::kRegistrationDelayExponent + scaled_jitter; |
| expected_delay = std::floor(expected_delay); |
| ExpectPendingRegistrations( |
| lost_ids, expected_delay, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| |
| // Trigger enough failures to hit the ceiling. |
| while (expected_delay < RegistrationManager::kMaxRegistrationDelaySeconds) { |
| fake_registration_manager_.FirePendingRegistrationsForTest(); |
| LoseRegistrations(lost_ids); |
| expected_delay *= |
| RegistrationManager::kRegistrationDelayExponent + scaled_jitter; |
| expected_delay = std::floor(expected_delay); |
| } |
| ExpectPendingRegistrations( |
| lost_ids, |
| RegistrationManager::kMaxRegistrationDelaySeconds, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| } |
| |
| FakeInvalidationClient fake_invalidation_client_; |
| FakeRegistrationManager fake_registration_manager_; |
| |
| private: |
| // Needed by timers in RegistrationManager. |
| base::MessageLoop message_loop_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RegistrationManagerTest); |
| }; |
| |
| // Basic test of UpdateRegisteredIds to make sure we properly register |
| // new IDs and unregister any IDs no longer in the set. |
| TEST_F(RegistrationManagerTest, UpdateRegisteredIds) { |
| ObjectIdSet ids = GetSequenceOfIds(kObjectIdsCount - 1); |
| |
| EXPECT_TRUE(fake_registration_manager_.GetRegisteredIdsForTest().empty()); |
| EXPECT_TRUE(fake_invalidation_client_.GetRegisteredIdsForTest().empty()); |
| |
| ObjectIdSet expected_unregistered_ids; |
| |
| ObjectIdSet unregistered_ids = |
| fake_registration_manager_.UpdateRegisteredIds(ids); |
| EXPECT_EQ(expected_unregistered_ids, unregistered_ids); |
| EXPECT_EQ(ids, fake_registration_manager_.GetRegisteredIdsForTest()); |
| EXPECT_EQ(ids, fake_invalidation_client_.GetRegisteredIdsForTest()); |
| |
| ids.insert(GetIdForIndex(kObjectIdsCount - 1)); |
| ids.erase(GetIdForIndex(kObjectIdsCount - 2)); |
| unregistered_ids = fake_registration_manager_.UpdateRegisteredIds(ids); |
| expected_unregistered_ids.insert(GetIdForIndex(kObjectIdsCount - 2)); |
| EXPECT_EQ(expected_unregistered_ids, unregistered_ids); |
| EXPECT_EQ(ids, fake_registration_manager_.GetRegisteredIdsForTest()); |
| EXPECT_EQ(ids, fake_invalidation_client_.GetRegisteredIdsForTest()); |
| } |
| |
| int GetRoundedBackoff(double retry_interval, double jitter) { |
| const double kInitialRetryInterval = 3.0; |
| const double kMinRetryInterval = 2.0; |
| const double kMaxRetryInterval = 20.0; |
| const double kBackoffExponent = 2.0; |
| const double kMaxJitter = 0.5; |
| |
| return static_cast<int>( |
| RegistrationManager::CalculateBackoff(retry_interval, |
| kInitialRetryInterval, |
| kMinRetryInterval, |
| kMaxRetryInterval, |
| kBackoffExponent, |
| jitter, |
| kMaxJitter)); |
| } |
| |
| TEST_F(RegistrationManagerTest, CalculateBackoff) { |
| // Test initial. |
| EXPECT_EQ(2, GetRoundedBackoff(0.0, -1.0)); |
| EXPECT_EQ(3, GetRoundedBackoff(0.0, 0.0)); |
| EXPECT_EQ(4, GetRoundedBackoff(0.0, +1.0)); |
| |
| // Test non-initial. |
| EXPECT_EQ(4, GetRoundedBackoff(3.0, -1.0)); |
| EXPECT_EQ(6, GetRoundedBackoff(3.0, 0.0)); |
| EXPECT_EQ(7, GetRoundedBackoff(3.0, +1.0)); |
| |
| EXPECT_EQ(7, GetRoundedBackoff(5.0, -1.0)); |
| EXPECT_EQ(10, GetRoundedBackoff(5.0, 0.0)); |
| EXPECT_EQ(12, GetRoundedBackoff(5.0, +1.0)); |
| |
| // Test ceiling. |
| EXPECT_EQ(19, GetRoundedBackoff(13.0, -1.0)); |
| EXPECT_EQ(20, GetRoundedBackoff(13.0, 0.0)); |
| EXPECT_EQ(20, GetRoundedBackoff(13.0, +1.0)); |
| } |
| |
| // Losing a registration should queue automatic re-registration. |
| TEST_F(RegistrationManagerTest, MarkRegistrationLost) { |
| ObjectIdSet ids = GetSequenceOfIds(kObjectIdsCount); |
| |
| fake_registration_manager_.UpdateRegisteredIds(ids); |
| EXPECT_TRUE( |
| fake_registration_manager_.GetPendingRegistrationsForTest().empty()); |
| |
| // Lose some ids. |
| ObjectIdSet lost_ids = GetSequenceOfIds(3); |
| ObjectIdSet non_lost_ids = GetSequenceOfIdsStartingAt(3, kObjectIdsCount - 3); |
| LoseRegistrations(lost_ids); |
| ExpectPendingRegistrations( |
| lost_ids, 0.0, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| EXPECT_EQ(non_lost_ids, fake_registration_manager_.GetRegisteredIdsForTest()); |
| EXPECT_EQ(non_lost_ids, fake_invalidation_client_.GetRegisteredIdsForTest()); |
| |
| // Pretend we waited long enough to re-register. |
| fake_registration_manager_.FirePendingRegistrationsForTest(); |
| EXPECT_EQ(ids, fake_registration_manager_.GetRegisteredIdsForTest()); |
| EXPECT_EQ(ids, fake_invalidation_client_.GetRegisteredIdsForTest()); |
| } |
| |
| TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffLow) { |
| RunBackoffTest(-1.0); |
| } |
| |
| TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffMid) { |
| RunBackoffTest(0.0); |
| } |
| |
| TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffHigh) { |
| RunBackoffTest(+1.0); |
| } |
| |
| // Exponential backoff on lost registrations should be reset to zero if |
| // UpdateRegisteredIds is called. |
| TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffReset) { |
| ObjectIdSet ids = GetSequenceOfIds(kObjectIdsCount); |
| |
| fake_registration_manager_.UpdateRegisteredIds(ids); |
| |
| // Lose some ids. |
| ObjectIdSet lost_ids = GetSequenceOfIds(2); |
| LoseRegistrations(lost_ids); |
| ExpectPendingRegistrations( |
| lost_ids, 0.0, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| |
| // Trigger another failure to start delaying. |
| fake_registration_manager_.FirePendingRegistrationsForTest(); |
| LoseRegistrations(lost_ids); |
| double expected_delay = |
| RegistrationManager::kInitialRegistrationDelaySeconds; |
| ExpectPendingRegistrations( |
| lost_ids, expected_delay, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| |
| // Set ids again. |
| fake_registration_manager_.UpdateRegisteredIds(ids); |
| ExpectPendingRegistrations( |
| ObjectIdSet(), |
| 0.0, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| } |
| |
| TEST_F(RegistrationManagerTest, MarkAllRegistrationsLost) { |
| ObjectIdSet ids = GetSequenceOfIds(kObjectIdsCount); |
| |
| fake_registration_manager_.UpdateRegisteredIds(ids); |
| |
| fake_invalidation_client_.LoseAllRegistrations(); |
| fake_registration_manager_.MarkAllRegistrationsLost(); |
| |
| EXPECT_TRUE(fake_registration_manager_.GetRegisteredIdsForTest().empty()); |
| EXPECT_TRUE(fake_invalidation_client_.GetRegisteredIdsForTest().empty()); |
| |
| ExpectPendingRegistrations( |
| ids, 0.0, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| |
| // Trigger another failure to start delaying. |
| fake_registration_manager_.FirePendingRegistrationsForTest(); |
| fake_invalidation_client_.LoseAllRegistrations(); |
| fake_registration_manager_.MarkAllRegistrationsLost(); |
| double expected_delay = |
| RegistrationManager::kInitialRegistrationDelaySeconds; |
| ExpectPendingRegistrations( |
| ids, expected_delay, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| |
| // Pretend we waited long enough to re-register. |
| fake_registration_manager_.FirePendingRegistrationsForTest(); |
| EXPECT_EQ(ids, fake_registration_manager_.GetRegisteredIdsForTest()); |
| EXPECT_EQ(ids, fake_invalidation_client_.GetRegisteredIdsForTest()); |
| } |
| |
| // IDs that are disabled should not be re-registered by UpdateRegisteredIds or |
| // automatic re-registration if that registration is lost. |
| TEST_F(RegistrationManagerTest, DisableId) { |
| ObjectIdSet ids = GetSequenceOfIds(kObjectIdsCount); |
| |
| fake_registration_manager_.UpdateRegisteredIds(ids); |
| EXPECT_TRUE( |
| fake_registration_manager_.GetPendingRegistrationsForTest().empty()); |
| |
| // Disable some ids. |
| ObjectIdSet disabled_ids = GetSequenceOfIds(3); |
| ObjectIdSet enabled_ids = GetSequenceOfIdsStartingAt(3, kObjectIdsCount - 3); |
| DisableIds(disabled_ids); |
| ExpectPendingRegistrations( |
| ObjectIdSet(), |
| 0.0, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| EXPECT_EQ(enabled_ids, fake_registration_manager_.GetRegisteredIdsForTest()); |
| EXPECT_EQ(enabled_ids, fake_invalidation_client_.GetRegisteredIdsForTest()); |
| |
| fake_registration_manager_.UpdateRegisteredIds(ids); |
| EXPECT_EQ(enabled_ids, fake_registration_manager_.GetRegisteredIdsForTest()); |
| |
| fake_registration_manager_.MarkRegistrationLost( |
| *disabled_ids.begin()); |
| ExpectPendingRegistrations( |
| ObjectIdSet(), |
| 0.0, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| |
| fake_registration_manager_.MarkAllRegistrationsLost(); |
| ExpectPendingRegistrations( |
| enabled_ids, 0.0, |
| fake_registration_manager_.GetPendingRegistrationsForTest()); |
| } |
| |
| } // namespace |
| } // namespace syncer |