// 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 "chrome/browser/sync/notifier/registration_manager.h" #include #include #include #include #include #include "base/basictypes.h" #include "base/message_loop.h" #include "chrome/browser/sync/notifier/invalidation_util.h" #include "google/cacheinvalidation/include/invalidation-client.h" #include "sync/syncable/model_type.h" #include "testing/gtest/include/gtest/gtest.h" namespace sync_notifier { namespace { syncable::ModelType ObjectIdToModelType( const invalidation::ObjectId& object_id) { syncable::ModelType model_type = syncable::UNSPECIFIED; EXPECT_TRUE(ObjectIdToRealModelType(object_id, &model_type)); return model_type; } // 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() { return jitter_; } private: double jitter_; DISALLOW_COPY_AND_ASSIGN(FakeRegistrationManager); }; // Fake invalidation client that just stores the currently-registered // model types. class FakeInvalidationClient : public invalidation::InvalidationClient { public: FakeInvalidationClient() {} virtual ~FakeInvalidationClient() {} void LoseRegistration(syncable::ModelType model_type) { EXPECT_TRUE(registered_types_.Has(model_type)); registered_types_.Remove(model_type); } void LoseAllRegistrations() { registered_types_.Clear(); } // invalidation::InvalidationClient implementation. virtual void Start() {} virtual void Stop() {} virtual void Acknowledge(const invalidation::AckHandle& handle) {} virtual void Register(const invalidation::ObjectId& oid) { syncable::ModelType model_type = ObjectIdToModelType(oid); EXPECT_FALSE(registered_types_.Has(model_type)); registered_types_.Put(model_type); } virtual void Register(const std::vector& oids) { // Unused for now. } virtual void Unregister(const invalidation::ObjectId& oid) { syncable::ModelType model_type = ObjectIdToModelType(oid); EXPECT_TRUE(registered_types_.Has(model_type)); registered_types_.Remove(model_type); } virtual void Unregister(const std::vector& oids) { // Unused for now. } const syncable::ModelTypeSet GetRegisteredTypes() const { return registered_types_; } private: syncable::ModelTypeSet registered_types_; DISALLOW_COPY_AND_ASSIGN(FakeInvalidationClient); }; const syncable::ModelType kModelTypes[] = { syncable::BOOKMARKS, syncable::PREFERENCES, syncable::THEMES, syncable::AUTOFILL, syncable::EXTENSIONS, }; const size_t kModelTypeCount = arraysize(kModelTypes); syncable::ModelTypeSet FromPtr( const syncable::ModelType* types, size_t count) { syncable::ModelTypeSet type_set; for (size_t i = 0; i < count; ++i) { type_set.Put(types[i]); } return type_set; } void ExpectPendingRegistrations( syncable::ModelTypeSet expected_pending_types, double expected_delay_seconds, const RegistrationManager::PendingRegistrationMap& pending_registrations) { syncable::ModelTypeSet pending_types; for (RegistrationManager::PendingRegistrationMap::const_iterator it = pending_registrations.begin(); it != pending_registrations.end(); ++it) { SCOPED_TRACE(syncable::ModelTypeToString(it->first)); pending_types.Put(it->first); base::TimeDelta offset = it->second.last_registration_request - it->second.registration_attempt; base::TimeDelta expected_delay = base::TimeDelta::FromSeconds( static_cast(expected_delay_seconds)) + offset; // TODO(akalin): Add base::PrintTo() for base::Time and // base::TimeDeltas. EXPECT_EQ(it->second.delay, expected_delay) << it->second.delay.InMicroseconds() << ", " << expected_delay.InMicroseconds(); if (it->second.delay <= base::TimeDelta()) { EXPECT_EQ(it->second.actual_delay, base::TimeDelta()); } else { EXPECT_EQ(it->second.delay, it->second.actual_delay); } } EXPECT_TRUE(pending_types.Equals(expected_pending_types)); } class RegistrationManagerTest : public testing::Test { protected: RegistrationManagerTest() : fake_registration_manager_(&fake_invalidation_client_) {} virtual ~RegistrationManagerTest() {} void LoseRegistrations(syncable::ModelTypeSet types) { for (syncable::ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { fake_invalidation_client_.LoseRegistration(it.Get()); fake_registration_manager_.MarkRegistrationLost(it.Get()); } } void DisableTypes(syncable::ModelTypeSet types) { for (syncable::ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { fake_invalidation_client_.LoseRegistration(it.Get()); fake_registration_manager_.DisableType(it.Get()); } } // Used by MarkRegistrationLostBackoff* tests. void RunBackoffTest(double jitter) { fake_registration_manager_.SetJitter(jitter); syncable::ModelTypeSet types = FromPtr(kModelTypes, kModelTypeCount); fake_registration_manager_.SetRegisteredTypes(types); // Lose some types. syncable::ModelTypeSet lost_types = FromPtr(kModelTypes, 2); LoseRegistrations(lost_types); ExpectPendingRegistrations( lost_types, 0.0, fake_registration_manager_.GetPendingRegistrations()); // Trigger another failure to start delaying. fake_registration_manager_.FirePendingRegistrationsForTest(); LoseRegistrations(lost_types); double scaled_jitter = jitter * RegistrationManager::kRegistrationDelayMaxJitter; double expected_delay = RegistrationManager::kInitialRegistrationDelaySeconds * (1.0 + scaled_jitter); expected_delay = std::floor(expected_delay); ExpectPendingRegistrations( lost_types, expected_delay, fake_registration_manager_.GetPendingRegistrations()); // Trigger another failure. fake_registration_manager_.FirePendingRegistrationsForTest(); LoseRegistrations(lost_types); expected_delay *= RegistrationManager::kRegistrationDelayExponent + scaled_jitter; expected_delay = std::floor(expected_delay); ExpectPendingRegistrations( lost_types, expected_delay, fake_registration_manager_.GetPendingRegistrations()); // Trigger enough failures to hit the ceiling. while (expected_delay < RegistrationManager::kMaxRegistrationDelaySeconds) { fake_registration_manager_.FirePendingRegistrationsForTest(); LoseRegistrations(lost_types); expected_delay *= RegistrationManager::kRegistrationDelayExponent + scaled_jitter; expected_delay = std::floor(expected_delay); } ExpectPendingRegistrations( lost_types, RegistrationManager::kMaxRegistrationDelaySeconds, fake_registration_manager_.GetPendingRegistrations()); } FakeInvalidationClient fake_invalidation_client_; FakeRegistrationManager fake_registration_manager_; private: // Needed by timers in RegistrationManager. MessageLoop message_loop_; DISALLOW_COPY_AND_ASSIGN(RegistrationManagerTest); }; TEST_F(RegistrationManagerTest, SetRegisteredTypes) { syncable::ModelTypeSet types = FromPtr(kModelTypes, kModelTypeCount); EXPECT_TRUE(fake_registration_manager_.GetRegisteredTypes().Empty()); EXPECT_TRUE(fake_invalidation_client_.GetRegisteredTypes().Empty()); fake_registration_manager_.SetRegisteredTypes(types); EXPECT_TRUE(fake_registration_manager_.GetRegisteredTypes().Equals(types)); EXPECT_TRUE(fake_invalidation_client_.GetRegisteredTypes().Equals(types)); types.Put(syncable::APPS); types.Remove(syncable::BOOKMARKS); fake_registration_manager_.SetRegisteredTypes(types); EXPECT_TRUE(fake_registration_manager_.GetRegisteredTypes().Equals(types)); EXPECT_TRUE(fake_invalidation_client_.GetRegisteredTypes().Equals(types)); } 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( 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)); } TEST_F(RegistrationManagerTest, MarkRegistrationLost) { syncable::ModelTypeSet types = FromPtr(kModelTypes, kModelTypeCount); fake_registration_manager_.SetRegisteredTypes(types); EXPECT_TRUE(fake_registration_manager_.GetPendingRegistrations().empty()); // Lose some types. syncable::ModelTypeSet lost_types = FromPtr( kModelTypes, 3); syncable::ModelTypeSet non_lost_types = FromPtr( kModelTypes + 3, kModelTypeCount - 3); LoseRegistrations(lost_types); ExpectPendingRegistrations( lost_types, 0.0, fake_registration_manager_.GetPendingRegistrations()); EXPECT_TRUE( fake_registration_manager_.GetRegisteredTypes().Equals(non_lost_types)); EXPECT_TRUE( fake_invalidation_client_.GetRegisteredTypes().Equals(non_lost_types)); // Pretend we waited long enough to re-register. fake_registration_manager_.FirePendingRegistrationsForTest(); EXPECT_TRUE( fake_registration_manager_.GetRegisteredTypes().Equals(types)); EXPECT_TRUE( fake_invalidation_client_.GetRegisteredTypes().Equals(types)); } TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffLow) { RunBackoffTest(-1.0); } TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffMid) { RunBackoffTest(0.0); } TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffHigh) { RunBackoffTest(+1.0); } TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffReset) { syncable::ModelTypeSet types = FromPtr(kModelTypes, kModelTypeCount); fake_registration_manager_.SetRegisteredTypes(types); // Lose some types. syncable::ModelTypeSet lost_types = FromPtr(kModelTypes, 2); LoseRegistrations(lost_types); ExpectPendingRegistrations( lost_types, 0.0, fake_registration_manager_.GetPendingRegistrations()); // Trigger another failure to start delaying. fake_registration_manager_.FirePendingRegistrationsForTest(); LoseRegistrations(lost_types); double expected_delay = RegistrationManager::kInitialRegistrationDelaySeconds; ExpectPendingRegistrations( lost_types, expected_delay, fake_registration_manager_.GetPendingRegistrations()); // Set types again. fake_registration_manager_.SetRegisteredTypes(types); ExpectPendingRegistrations( syncable::ModelTypeSet(), 0.0, fake_registration_manager_.GetPendingRegistrations()); } TEST_F(RegistrationManagerTest, MarkAllRegistrationsLost) { syncable::ModelTypeSet types = FromPtr(kModelTypes, kModelTypeCount); fake_registration_manager_.SetRegisteredTypes(types); fake_invalidation_client_.LoseAllRegistrations(); fake_registration_manager_.MarkAllRegistrationsLost(); syncable::ModelTypeSet expected_types; EXPECT_TRUE( fake_registration_manager_.GetRegisteredTypes().Equals(expected_types)); EXPECT_TRUE( fake_invalidation_client_.GetRegisteredTypes().Equals(expected_types)); ExpectPendingRegistrations( types, 0.0, fake_registration_manager_.GetPendingRegistrations()); // Trigger another failure to start delaying. fake_registration_manager_.FirePendingRegistrationsForTest(); fake_invalidation_client_.LoseAllRegistrations(); fake_registration_manager_.MarkAllRegistrationsLost(); double expected_delay = RegistrationManager::kInitialRegistrationDelaySeconds; ExpectPendingRegistrations( types, expected_delay, fake_registration_manager_.GetPendingRegistrations()); // Pretend we waited long enough to re-register. fake_registration_manager_.FirePendingRegistrationsForTest(); EXPECT_TRUE( fake_registration_manager_.GetRegisteredTypes().Equals(types)); EXPECT_TRUE( fake_invalidation_client_.GetRegisteredTypes().Equals(types)); } TEST_F(RegistrationManagerTest, DisableType) { syncable::ModelTypeSet types = FromPtr(kModelTypes, kModelTypeCount); fake_registration_manager_.SetRegisteredTypes(types); EXPECT_TRUE(fake_registration_manager_.GetPendingRegistrations().empty()); // Disable some types. syncable::ModelTypeSet disabled_types = FromPtr( kModelTypes, 3); syncable::ModelTypeSet enabled_types = FromPtr( kModelTypes + 3, kModelTypeCount - 3); DisableTypes(disabled_types); ExpectPendingRegistrations( syncable::ModelTypeSet(), 0.0, fake_registration_manager_.GetPendingRegistrations()); EXPECT_TRUE( fake_registration_manager_.GetRegisteredTypes().Equals(enabled_types)); EXPECT_TRUE( fake_invalidation_client_.GetRegisteredTypes().Equals(enabled_types)); fake_registration_manager_.SetRegisteredTypes(types); EXPECT_TRUE( fake_registration_manager_.GetRegisteredTypes().Equals(enabled_types)); fake_registration_manager_.MarkRegistrationLost( disabled_types.First().Get()); ExpectPendingRegistrations( syncable::ModelTypeSet(), 0.0, fake_registration_manager_.GetPendingRegistrations()); fake_registration_manager_.MarkAllRegistrationsLost(); ExpectPendingRegistrations( enabled_types, 0.0, fake_registration_manager_.GetPendingRegistrations()); } } // namespace } // namespace notifier