// 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