// Copyright (c) 2010 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 <algorithm> #include <cmath> #include <cstddef> #include <deque> #include <vector> #include "base/basictypes.h" #include "base/message_loop.h" #include "chrome/browser/sync/notifier/invalidation_util.h" #include "chrome/browser/sync/syncable/model_type.h" #include "google/cacheinvalidation/invalidation-client.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_GT(registered_types_.count(model_type), 0u); registered_types_.erase(model_type); } void LoseAllRegistrations() { registered_types_.clear(); } // invalidation::InvalidationClient implementation. virtual void Start(const std::string& state) {} virtual void Register(const invalidation::ObjectId& oid) { syncable::ModelType model_type = ObjectIdToModelType(oid); EXPECT_EQ(0u, registered_types_.count(model_type)); registered_types_.insert(model_type); } virtual void Unregister(const invalidation::ObjectId& oid) { syncable::ModelType model_type = ObjectIdToModelType(oid); EXPECT_GT(registered_types_.count(model_type), 0u); registered_types_.erase(model_type); } virtual invalidation::NetworkEndpoint* network_endpoint() { ADD_FAILURE(); return NULL; } 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); void ExpectPendingRegistrations( const 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.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(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_EQ(expected_pending_types, pending_types); } class RegistrationManagerTest : public testing::Test { protected: RegistrationManagerTest() : fake_registration_manager_(&fake_invalidation_client_) {} virtual ~RegistrationManagerTest() {} void LoseRegistrations(const syncable::ModelTypeSet& types) { for (syncable::ModelTypeSet::const_iterator it = types.begin(); it != types.end(); ++it) { fake_invalidation_client_.LoseRegistration(*it); fake_registration_manager_.MarkRegistrationLost(*it); } } // Used by MarkRegistrationLostBackoff* tests. void RunBackoffTest(double jitter) { fake_registration_manager_.SetJitter(jitter); syncable::ModelTypeSet types(kModelTypes, kModelTypes + kModelTypeCount); fake_registration_manager_.SetRegisteredTypes(types); // Lose some types. syncable::ModelTypeSet lost_types(kModelTypes, 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 no_types; syncable::ModelTypeSet types(kModelTypes, kModelTypes + kModelTypeCount); EXPECT_EQ(no_types, fake_registration_manager_.GetRegisteredTypes()); EXPECT_EQ(no_types, fake_invalidation_client_.GetRegisteredTypes()); fake_registration_manager_.SetRegisteredTypes(types); EXPECT_EQ(types, fake_registration_manager_.GetRegisteredTypes()); EXPECT_EQ(types, fake_invalidation_client_.GetRegisteredTypes()); types.insert(syncable::APPS); types.erase(syncable::BOOKMARKS); fake_registration_manager_.SetRegisteredTypes(types); EXPECT_EQ(types, fake_registration_manager_.GetRegisteredTypes()); EXPECT_EQ(types, fake_invalidation_client_.GetRegisteredTypes()); } 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)); } TEST_F(RegistrationManagerTest, MarkRegistrationLost) { syncable::ModelTypeSet types(kModelTypes, kModelTypes + kModelTypeCount); fake_registration_manager_.SetRegisteredTypes(types); EXPECT_TRUE(fake_registration_manager_.GetPendingRegistrations().empty()); // Lose some types. syncable::ModelTypeSet lost_types( kModelTypes, kModelTypes + 3); syncable::ModelTypeSet non_lost_types( kModelTypes + 3, kModelTypes + kModelTypeCount); LoseRegistrations(lost_types); ExpectPendingRegistrations( lost_types, 0.0, fake_registration_manager_.GetPendingRegistrations()); EXPECT_EQ(non_lost_types, fake_registration_manager_.GetRegisteredTypes()); EXPECT_EQ(non_lost_types, fake_invalidation_client_.GetRegisteredTypes()); // Pretend we waited long enough to re-register. fake_registration_manager_.FirePendingRegistrationsForTest(); EXPECT_EQ(types, fake_registration_manager_.GetRegisteredTypes()); EXPECT_EQ(types, fake_invalidation_client_.GetRegisteredTypes()); } 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(kModelTypes, kModelTypes + kModelTypeCount); fake_registration_manager_.SetRegisteredTypes(types); // Lose some types. syncable::ModelTypeSet lost_types(kModelTypes, 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(kModelTypes, kModelTypes + kModelTypeCount); fake_registration_manager_.SetRegisteredTypes(types); fake_invalidation_client_.LoseAllRegistrations(); fake_registration_manager_.MarkAllRegistrationsLost(); syncable::ModelTypeSet expected_types; EXPECT_EQ(expected_types, fake_registration_manager_.GetRegisteredTypes()); EXPECT_EQ(expected_types, fake_invalidation_client_.GetRegisteredTypes()); 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_EQ(types, fake_registration_manager_.GetRegisteredTypes()); EXPECT_EQ(types, fake_invalidation_client_.GetRegisteredTypes()); } } // namespace } // namespace notifier