diff options
author | jianli <jianli@chromium.org> | 2014-09-18 19:42:46 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-19 02:43:28 +0000 |
commit | 2dc910b0b9082050e5be6c81b00dddf97ffa3e6e (patch) | |
tree | a071cff26b88c2f789ad72c03ed54a29e7afc019 /components/gcm_driver | |
parent | 99fa5464cb1b1ba9af0dee4438f182de5c0847ea (diff) | |
download | chromium_src-2dc910b0b9082050e5be6c81b00dddf97ffa3e6e.zip chromium_src-2dc910b0b9082050e5be6c81b00dddf97ffa3e6e.tar.gz chromium_src-2dc910b0b9082050e5be6c81b00dddf97ffa3e6e.tar.bz2 |
Reland: Add GCMChannelStatusSyncer to schedule requests and enable/disable GCM
BUG=384041
TEST=new tests added
Committed: https://crrev.com/3c23f4a188e171998f3042ad62f4aa5717e66d63
Cr-Commit-Position: refs/heads/master@{#295524}
Review URL: https://codereview.chromium.org/561943002
Cr-Commit-Position: refs/heads/master@{#295645}
Diffstat (limited to 'components/gcm_driver')
-rw-r--r-- | components/gcm_driver/BUILD.gn | 4 | ||||
-rw-r--r-- | components/gcm_driver/DEPS | 1 | ||||
-rw-r--r-- | components/gcm_driver/gcm_channel_status_request.cc | 4 | ||||
-rw-r--r-- | components/gcm_driver/gcm_channel_status_request.h | 5 | ||||
-rw-r--r-- | components/gcm_driver/gcm_channel_status_request_unittest.cc | 15 | ||||
-rw-r--r-- | components/gcm_driver/gcm_channel_status_syncer.cc | 183 | ||||
-rw-r--r-- | components/gcm_driver/gcm_channel_status_syncer.h | 98 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver_desktop.cc | 21 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver_desktop.h | 13 | ||||
-rw-r--r-- | components/gcm_driver/gcm_driver_desktop_unittest.cc | 270 |
10 files changed, 593 insertions, 21 deletions
diff --git a/components/gcm_driver/BUILD.gn b/components/gcm_driver/BUILD.gn index a446c9f..9e4f3d7 100644 --- a/components/gcm_driver/BUILD.gn +++ b/components/gcm_driver/BUILD.gn @@ -17,6 +17,8 @@ static_library("gcm_driver") { "gcm_backoff_policy.h", "gcm_channel_status_request.cc", "gcm_channel_status_request.h", + "gcm_channel_status_syncer.cc", + "gcm_channel_status_syncer.h", "gcm_client.cc", "gcm_client.h", "gcm_client_factory.cc", @@ -51,6 +53,8 @@ static_library("gcm_driver") { sources -= [ "gcm_channel_status_request.cc", "gcm_channel_status_request.h", + "gcm_channel_status_syncer.cc", + "gcm_channel_status_syncer.h", "gcm_client_factory.cc", "gcm_client_factory.h", "gcm_client_impl.cc", diff --git a/components/gcm_driver/DEPS b/components/gcm_driver/DEPS index cbffdad..93923ff 100644 --- a/components/gcm_driver/DEPS +++ b/components/gcm_driver/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+components/os_crypt", + "+components/pref_registry", # TODO(johnme): Fix this layering violation. "!content/public/android/java", "+google_apis/gaia", diff --git a/components/gcm_driver/gcm_channel_status_request.cc b/components/gcm_driver/gcm_channel_status_request.cc index 206ee39..fd405d1 100644 --- a/components/gcm_driver/gcm_channel_status_request.cc +++ b/components/gcm_driver/gcm_channel_status_request.cc @@ -41,12 +41,12 @@ GCMChannelStatusRequest::~GCMChannelStatusRequest() { } // static -int GCMChannelStatusRequest::default_poll_interval_seconds_for_testing() { +int GCMChannelStatusRequest::default_poll_interval_seconds() { return kDefaultPollIntervalSeconds; } // static -int GCMChannelStatusRequest::min_poll_interval_seconds_for_testing() { +int GCMChannelStatusRequest::min_poll_interval_seconds() { return kMinPollIntervalSeconds; } diff --git a/components/gcm_driver/gcm_channel_status_request.h b/components/gcm_driver/gcm_channel_status_request.h index b2725759..6ce1bab 100644 --- a/components/gcm_driver/gcm_channel_status_request.h +++ b/components/gcm_driver/gcm_channel_status_request.h @@ -36,9 +36,8 @@ class GCMChannelStatusRequest : public net::URLFetcherDelegate { void Start(); - // Exposed for testing purpose. - static int default_poll_interval_seconds_for_testing(); - static int min_poll_interval_seconds_for_testing(); + static int default_poll_interval_seconds(); + static int min_poll_interval_seconds(); private: // Overridden from URLFetcherDelegate: diff --git a/components/gcm_driver/gcm_channel_status_request_unittest.cc b/components/gcm_driver/gcm_channel_status_request_unittest.cc index daf8122..961ca4e 100644 --- a/components/gcm_driver/gcm_channel_status_request_unittest.cc +++ b/components/gcm_driver/gcm_channel_status_request_unittest.cc @@ -136,7 +136,7 @@ TEST_F(GCMChannelStatusRequestTest, ResponseWithDisabledStatus) { EXPECT_TRUE(request_callback_invoked_); EXPECT_FALSE(enabled_); EXPECT_EQ( - GCMChannelStatusRequest::default_poll_interval_seconds_for_testing(), + GCMChannelStatusRequest::default_poll_interval_seconds(), poll_interval_seconds_); } @@ -148,7 +148,7 @@ TEST_F(GCMChannelStatusRequestTest, ResponseWithEnabledStatus) { EXPECT_TRUE(request_callback_invoked_); EXPECT_TRUE(enabled_); EXPECT_EQ( - GCMChannelStatusRequest::default_poll_interval_seconds_for_testing(), + GCMChannelStatusRequest::default_poll_interval_seconds(), poll_interval_seconds_); } @@ -156,8 +156,7 @@ TEST_F(GCMChannelStatusRequestTest, ResponseWithPollInterval) { // Setting a poll interval 15 minutes longer than the minimum interval we // enforce. int poll_interval_seconds = - GCMChannelStatusRequest::min_poll_interval_seconds_for_testing() + - 15 * 60; + GCMChannelStatusRequest::min_poll_interval_seconds() + 15 * 60; StartRequest(); SetResponseProtoData(NOT_SPECIFIED, poll_interval_seconds); CompleteFetch(); @@ -171,22 +170,20 @@ TEST_F(GCMChannelStatusRequestTest, ResponseWithShortPollInterval) { // Setting a poll interval 15 minutes shorter than the minimum interval we // enforce. int poll_interval_seconds = - GCMChannelStatusRequest::min_poll_interval_seconds_for_testing() - - 15 * 60; + GCMChannelStatusRequest::min_poll_interval_seconds() - 15 * 60; StartRequest(); SetResponseProtoData(NOT_SPECIFIED, poll_interval_seconds); CompleteFetch(); EXPECT_TRUE(request_callback_invoked_); EXPECT_TRUE(enabled_); - EXPECT_EQ(GCMChannelStatusRequest::min_poll_interval_seconds_for_testing(), + EXPECT_EQ(GCMChannelStatusRequest::min_poll_interval_seconds(), poll_interval_seconds_); } TEST_F(GCMChannelStatusRequestTest, ResponseWithDisabledStatusAndPollInterval) { int poll_interval_seconds = - GCMChannelStatusRequest::min_poll_interval_seconds_for_testing() + - 15 * 60; + GCMChannelStatusRequest::min_poll_interval_seconds() + 15 * 60; StartRequest(); SetResponseProtoData(GCM_DISABLED, poll_interval_seconds); CompleteFetch(); diff --git a/components/gcm_driver/gcm_channel_status_syncer.cc b/components/gcm_driver/gcm_channel_status_syncer.cc new file mode 100644 index 0000000..7006721 --- /dev/null +++ b/components/gcm_driver/gcm_channel_status_syncer.cc @@ -0,0 +1,183 @@ +// Copyright 2014 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 "components/gcm_driver/gcm_channel_status_syncer.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service.h" +#include "base/rand_util.h" +#include "components/gcm_driver/gcm_channel_status_request.h" +#include "components/gcm_driver/gcm_driver.h" +#include "components/pref_registry/pref_registry_syncable.h" + +namespace gcm { + +namespace { + +// The GCM channel's enabled state. +const char kGCMChannelStatus[] = "gcm.channel_status"; + +// The GCM channel's polling interval (in seconds). +const char kGCMChannelPollIntervalSeconds[] = "gcm.poll_interval"; + +// Last time when checking with the GCM channel status server is done. +const char kGCMChannelLastCheckTime[] = "gcm.check_time"; + +// A small delay to avoid sending request at browser startup time for first-time +// request. +const int kFirstTimeDelaySeconds = 1 * 60; // 1 minute. + +// The fuzzing variation added to the polling delay. +const int kGCMChannelRequestTimeJitterSeconds = 15 * 60; // 15 minues. + +} // namespace + +// static +void GCMChannelStatusSyncer::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterBooleanPref(kGCMChannelStatus, true); + registry->RegisterIntegerPref( + kGCMChannelPollIntervalSeconds, + GCMChannelStatusRequest::default_poll_interval_seconds()); + registry->RegisterInt64Pref(kGCMChannelLastCheckTime, 0); +} + +// static +void GCMChannelStatusSyncer::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterBooleanPref( + kGCMChannelStatus, + true, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterIntegerPref( + kGCMChannelPollIntervalSeconds, + GCMChannelStatusRequest::default_poll_interval_seconds(), + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); + registry->RegisterInt64Pref( + kGCMChannelLastCheckTime, + 0, + user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); +} + +// static +int GCMChannelStatusSyncer::first_time_delay_seconds() { + return kFirstTimeDelaySeconds; +} + +GCMChannelStatusSyncer::GCMChannelStatusSyncer( + GCMDriver* driver, + PrefService* prefs, + const scoped_refptr<net::URLRequestContextGetter>& request_context) + : driver_(driver), + prefs_(prefs), + request_context_(request_context), + gcm_enabled_(true), + poll_interval_seconds_( + GCMChannelStatusRequest::default_poll_interval_seconds()), + delay_removed_for_testing_(false), + weak_ptr_factory_(this) { + gcm_enabled_ = prefs_->GetBoolean(kGCMChannelStatus); + poll_interval_seconds_ = prefs_->GetInteger(kGCMChannelPollIntervalSeconds); + if (poll_interval_seconds_ < + GCMChannelStatusRequest::min_poll_interval_seconds()) { + poll_interval_seconds_ = + GCMChannelStatusRequest::min_poll_interval_seconds(); + } + last_check_time_ = base::Time::FromInternalValue( + prefs_->GetInt64(kGCMChannelLastCheckTime)); +} + +GCMChannelStatusSyncer::~GCMChannelStatusSyncer() { +} + +void GCMChannelStatusSyncer::EnsureStarted() { + // Bail out if the request is already scheduled or started. + if (weak_ptr_factory_.HasWeakPtrs() || request_) + return; + + ScheduleRequest(); +} + +void GCMChannelStatusSyncer::Stop() { + request_.reset(); + weak_ptr_factory_.InvalidateWeakPtrs(); +} + +void GCMChannelStatusSyncer::OnRequestCompleted(bool enabled, + int poll_interval_seconds) { + DCHECK(request_); + request_.reset(); + + // Persist the current time as the last request complete time. + last_check_time_ = base::Time::Now(); + prefs_->SetInt64(kGCMChannelLastCheckTime, + last_check_time_.ToInternalValue()); + + if (gcm_enabled_ != enabled) { + gcm_enabled_ = enabled; + prefs_->SetBoolean(kGCMChannelStatus, enabled); + if (gcm_enabled_) + driver_->Enable(); + else + driver_->Disable(); + } + + DCHECK_GE(poll_interval_seconds, + GCMChannelStatusRequest::min_poll_interval_seconds()); + if (poll_interval_seconds_ != poll_interval_seconds) { + poll_interval_seconds_ = poll_interval_seconds; + prefs_->SetInteger(kGCMChannelPollIntervalSeconds, poll_interval_seconds_); + } + + ScheduleRequest(); +} + +void GCMChannelStatusSyncer::ScheduleRequest() { + current_request_delay_interval_ = GetRequestDelayInterval(); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&GCMChannelStatusSyncer::StartRequest, + weak_ptr_factory_.GetWeakPtr()), + current_request_delay_interval_); +} + +void GCMChannelStatusSyncer::StartRequest() { + DCHECK(!request_); + + request_.reset(new GCMChannelStatusRequest( + request_context_, + base::Bind(&GCMChannelStatusSyncer::OnRequestCompleted, + weak_ptr_factory_.GetWeakPtr()))); + request_->Start(); +} + +base::TimeDelta GCMChannelStatusSyncer::GetRequestDelayInterval() const { + // No delay during testing. + if (delay_removed_for_testing_) + return base::TimeDelta(); + + // Make sure that checking with server occurs at polling interval, regardless + // whether the browser restarts. + int64 delay_seconds = poll_interval_seconds_ - + (base::Time::Now() - last_check_time_).InSeconds(); + if (delay_seconds < 0) + delay_seconds = 0; + + if (last_check_time_.is_null()) { + // For the first-time request, add a small delay to avoid sending request at + // browser startup time. + DCHECK(!delay_seconds); + delay_seconds = kFirstTimeDelaySeconds; + } else { + // Otherwise, add a fuzzing variation to the delay. + delay_seconds += base::RandInt(0, kGCMChannelRequestTimeJitterSeconds); + } + + return base::TimeDelta::FromSeconds(delay_seconds); +} + +} // namespace gcm diff --git a/components/gcm_driver/gcm_channel_status_syncer.h b/components/gcm_driver/gcm_channel_status_syncer.h new file mode 100644 index 0000000..83cfbda3 --- /dev/null +++ b/components/gcm_driver/gcm_channel_status_syncer.h @@ -0,0 +1,98 @@ +// Copyright 2014 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. + +#ifndef COMPONENTS_GCM_DRIVER_GCM_CHANNEL_STATUS_SYNCER_H_ +#define COMPONENTS_GCM_DRIVER_GCM_CHANNEL_STATUS_SYNCER_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" + +class PrefService; +class PrefRegistrySimple; + +namespace net { +class URLRequestContextGetter; +} + +namespace user_prefs { +class PrefRegistrySyncable; +} + +namespace gcm { + +class GCMChannelStatusRequest; +class GCMDriver; + +// Syncing with the server for GCM channel status that controls if GCM +// functionality should be enabled or disabled. +class GCMChannelStatusSyncer { + public: + static void RegisterPrefs(PrefRegistrySimple* registry); + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + + GCMChannelStatusSyncer( + GCMDriver* driver, + PrefService* prefs, + const scoped_refptr<net::URLRequestContextGetter>& request_context); + ~GCMChannelStatusSyncer(); + + void EnsureStarted(); + void Stop(); + + bool gcm_enabled() const { return gcm_enabled_; } + + // For testing purpose. + void set_delay_removed_for_testing(bool delay_removed) { + delay_removed_for_testing_ = delay_removed; + } + base::TimeDelta current_request_delay_interval() const { + return current_request_delay_interval_; + } + static int first_time_delay_seconds(); + + private: + // Called when a request is completed. + void OnRequestCompleted(bool enabled, int poll_interval_seconds); + + // Schedules next request to start after appropriate delay. + void ScheduleRequest(); + + // Creates and starts a request immediately. + void StartRequest(); + + // Computes and returns a delay with the fuzzing variation added if needed, + // after which the request could start. + base::TimeDelta GetRequestDelayInterval() const; + + // GCMDriver owns GCMChannelStatusSyncer instance. + GCMDriver* driver_; + PrefService* prefs_; + + scoped_refptr<net::URLRequestContextGetter> request_context_; + scoped_ptr<GCMChannelStatusRequest> request_; + + bool gcm_enabled_; + int poll_interval_seconds_; + base::Time last_check_time_; + + // The flag that indicates if the delay, including fuzzing variation and poll + // interval, is removed for testing purpose. + bool delay_removed_for_testing_; + + // Tracked for testing purpose. + base::TimeDelta current_request_delay_interval_; + + // Used to pass a weak pointer to a task. + base::WeakPtrFactory<GCMChannelStatusSyncer> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(GCMChannelStatusSyncer); +}; + +} // namespace gcm + +#endif // COMPONENTS_GCM_DRIVER_GCM_CHANNEL_STATUS_SYNCER_H_ diff --git a/components/gcm_driver/gcm_driver_desktop.cc b/components/gcm_driver/gcm_driver_desktop.cc index 8d35c2c..82ca4b3 100644 --- a/components/gcm_driver/gcm_driver_desktop.cc +++ b/components/gcm_driver/gcm_driver_desktop.cc @@ -14,6 +14,7 @@ #include "base/sequenced_task_runner.h" #include "base/threading/sequenced_worker_pool.h" #include "components/gcm_driver/gcm_app_handler.h" +#include "components/gcm_driver/gcm_channel_status_syncer.h" #include "components/gcm_driver/gcm_client_factory.h" #include "components/gcm_driver/gcm_delayed_task_controller.h" #include "components/gcm_driver/system_encryptor.h" @@ -329,18 +330,23 @@ void GCMDriverDesktop::IOWorker::RemoveAccountMapping( GCMDriverDesktop::GCMDriverDesktop( scoped_ptr<GCMClientFactory> gcm_client_factory, const GCMClient::ChromeBuildInfo& chrome_build_info, + PrefService* prefs, const base::FilePath& store_path, const scoped_refptr<net::URLRequestContextGetter>& request_context, const scoped_refptr<base::SequencedTaskRunner>& ui_thread, const scoped_refptr<base::SequencedTaskRunner>& io_thread, const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) - : signed_in_(false), + : gcm_channel_status_syncer_( + new GCMChannelStatusSyncer(this, prefs, request_context)), + signed_in_(false), gcm_started_(false), gcm_enabled_(true), connected_(false), ui_thread_(ui_thread), io_thread_(io_thread), weak_ptr_factory_(this) { + gcm_enabled_ = gcm_channel_status_syncer_->gcm_enabled(); + // Create and initialize the GCMClient. Note that this does not initiate the // GCM check-in. io_worker_.reset(new IOWorker(ui_thread, io_thread)); @@ -361,6 +367,12 @@ GCMDriverDesktop::~GCMDriverDesktop() { void GCMDriverDesktop::Shutdown() { DCHECK(ui_thread_->RunsTasksOnCurrentThread()); GCMDriver::Shutdown(); + + // Dispose the syncer in order to release the reference to + // URLRequestContextGetter that needs to be done before IOThread gets + // deleted. + gcm_channel_status_syncer_.reset(); + io_thread_->DeleteSoon(FROM_HERE, io_worker_.release()); } @@ -442,6 +454,8 @@ void GCMDriverDesktop::Stop() { if (!gcm_started_) return; + gcm_channel_status_syncer_->Stop(); + RemoveCachedData(); io_thread_->PostTask( @@ -627,6 +641,11 @@ GCMClient::Result GCMDriverDesktop::EnsureStarted() { DCHECK(!delayed_task_controller_); delayed_task_controller_.reset(new GCMDelayedTaskController); + // Polling for channel status is only needed when GCM is supported for all + // users. + if (GCMDriver::IsAllowedForAllUsers()) + gcm_channel_status_syncer_->EnsureStarted(); + // Note that we need to pass weak pointer again since the existing weak // pointer in IOWorker might have been invalidated when check-out occurs. io_thread_->PostTask( diff --git a/components/gcm_driver/gcm_driver_desktop.h b/components/gcm_driver/gcm_driver_desktop.h index 51a9845..0ef9495 100644 --- a/components/gcm_driver/gcm_driver_desktop.h +++ b/components/gcm_driver/gcm_driver_desktop.h @@ -15,10 +15,13 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "components/gcm_driver/gcm_channel_status_syncer.h" #include "components/gcm_driver/gcm_client.h" #include "components/gcm_driver/gcm_connection_observer.h" #include "components/gcm_driver/gcm_driver.h" +class PrefService; + namespace base { class FilePath; class SequencedTaskRunner; @@ -44,6 +47,7 @@ class GCMDriverDesktop : public GCMDriver { GCMDriverDesktop( scoped_ptr<GCMClientFactory> gcm_client_factory, const GCMClient::ChromeBuildInfo& chrome_build_info, + PrefService* prefs, const base::FilePath& store_path, const scoped_refptr<net::URLRequestContextGetter>& request_context, const scoped_refptr<base::SequencedTaskRunner>& ui_thread, @@ -84,6 +88,12 @@ class GCMDriverDesktop : public GCMDriver { void SetAccountsForCheckin( const std::map<std::string, std::string>& account_tokens); + // Exposed for testing purpose. + bool gcm_enabled() const { return gcm_enabled_; } + GCMChannelStatusSyncer* gcm_channel_status_syncer_for_testing() { + return gcm_channel_status_syncer_.get(); + } + protected: // GCMDriver implementation: virtual GCMClient::Result EnsureStarted() OVERRIDE; @@ -125,6 +135,8 @@ class GCMDriverDesktop : public GCMDriver { void GetGCMStatisticsFinished(const GCMClient::GCMStatistics& stats); + scoped_ptr<GCMChannelStatusSyncer> gcm_channel_status_syncer_; + // Flag to indicate whether the user is signed in to a GAIA account. // TODO(jianli): To be removed when sign-in enforcement is dropped. bool signed_in_; @@ -133,6 +145,7 @@ class GCMDriverDesktop : public GCMDriver { bool gcm_started_; // Flag to indicate if GCM is enabled. + // TODO(jianli): Removed when we switch completely to support all users. bool gcm_enabled_; // Flag to indicate the last known state of the GCM client. Because this diff --git a/components/gcm_driver/gcm_driver_desktop_unittest.cc b/components/gcm_driver/gcm_driver_desktop_unittest.cc index ad95f86..8a25768 100644 --- a/components/gcm_driver/gcm_driver_desktop_unittest.cc +++ b/components/gcm_driver/gcm_driver_desktop_unittest.cc @@ -11,6 +11,8 @@ #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_proxy.h" #include "base/metrics/field_trial.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/testing_pref_service.h" #include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/test/test_simple_task_runner.h" @@ -19,8 +21,13 @@ #include "components/gcm_driver/fake_gcm_client.h" #include "components/gcm_driver/fake_gcm_client_factory.h" #include "components/gcm_driver/gcm_app_handler.h" +#include "components/gcm_driver/gcm_channel_status_request.h" +#include "components/gcm_driver/gcm_channel_status_syncer.h" #include "components/gcm_driver/gcm_client_factory.h" #include "components/gcm_driver/gcm_connection_observer.h" +#include "components/gcm_driver/proto/gcm_channel_status.pb.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "net/url_request/url_fetcher_delegate.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -97,7 +104,7 @@ class GCMDriverTest : public testing::Test { virtual void SetUp() OVERRIDE; virtual void TearDown() OVERRIDE; - GCMDriver* driver() { return driver_.get(); } + GCMDriverDesktop* driver() { return driver_.get(); } FakeGCMAppHandler* gcm_app_handler() { return gcm_app_handler_.get(); } FakeGCMConnectionObserver* gcm_connection_observer() { return gcm_connection_observer_.get(); @@ -118,6 +125,7 @@ class GCMDriverTest : public testing::Test { FakeGCMClient* GetGCMClient(); void CreateDriver(FakeGCMClient::StartMode gcm_client_start_mode); + void ShutdownDriver(); void AddAppHandlers(); void RemoveAppHandlers(); @@ -142,11 +150,12 @@ class GCMDriverTest : public testing::Test { void UnregisterCompleted(GCMClient::Result result); base::ScopedTempDir temp_dir_; + TestingPrefServiceSimple prefs_; scoped_refptr<base::TestSimpleTaskRunner> task_runner_; base::MessageLoopForUI message_loop_; base::Thread io_thread_; base::FieldTrialList field_trial_list_; - scoped_ptr<GCMDriver> driver_; + scoped_ptr<GCMDriverDesktop> driver_; scoped_ptr<FakeGCMAppHandler> gcm_app_handler_; scoped_ptr<FakeGCMConnectionObserver> gcm_connection_observer_; @@ -174,6 +183,7 @@ GCMDriverTest::~GCMDriverTest() { } void GCMDriverTest::SetUp() { + GCMChannelStatusSyncer::RegisterPrefs(prefs_.registry()); io_thread_.Start(); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } @@ -182,9 +192,7 @@ void GCMDriverTest::TearDown() { if (!driver_) return; - if (gcm_connection_observer_.get()) - driver_->RemoveConnectionObserver(gcm_connection_observer_.get()); - driver_->Shutdown(); + ShutdownDriver(); driver_.reset(); PumpIOLoop(); @@ -229,6 +237,7 @@ void GCMDriverTest::CreateDriver( base::MessageLoopProxy::current(), io_thread_.message_loop_proxy())).Pass(), GCMClient::ChromeBuildInfo(), + &prefs_, temp_dir_.path(), request_context, base::MessageLoopProxy::current(), @@ -241,6 +250,12 @@ void GCMDriverTest::CreateDriver( driver_->AddConnectionObserver(gcm_connection_observer_.get()); } +void GCMDriverTest::ShutdownDriver() { + if (gcm_connection_observer()) + driver()->RemoveConnectionObserver(gcm_connection_observer()); + driver()->Shutdown(); +} + void GCMDriverTest::AddAppHandlers() { driver_->AddAppHandler(kTestAppID1, gcm_app_handler_.get()); driver_->AddAppHandler(kTestAppID2, gcm_app_handler_.get()); @@ -386,7 +401,7 @@ TEST_F(GCMDriverTest, Shutdown) { AddAppHandlers(); EXPECT_TRUE(HasAppHandlers()); - driver()->Shutdown(); + ShutdownDriver(); EXPECT_FALSE(HasAppHandlers()); EXPECT_FALSE(driver()->IsConnected()); EXPECT_FALSE(gcm_connection_observer()->connected()); @@ -941,4 +956,247 @@ TEST_F(GCMDriverFunctionalTest, MessagesDeleted) { EXPECT_EQ(kTestAppID1, gcm_app_handler()->app_id()); } +// Tests a single instance of GCMDriver. +class GCMChannelStatusSyncerTest : public GCMDriverTest { + public: + GCMChannelStatusSyncerTest(); + virtual ~GCMChannelStatusSyncerTest(); + + // testing::Test: + virtual void SetUp() OVERRIDE; + + void CompleteGCMChannelStatusRequest(bool enabled, int poll_interval_seconds); + bool CompareDelaySeconds(bool expected_delay_seconds, + bool actual_delay_seconds); + + GCMChannelStatusSyncer* syncer() { + return driver()->gcm_channel_status_syncer_for_testing(); + } + + private: + net::TestURLFetcherFactory url_fetcher_factory_; + + DISALLOW_COPY_AND_ASSIGN(GCMChannelStatusSyncerTest); +}; + +GCMChannelStatusSyncerTest::GCMChannelStatusSyncerTest() { +} + +GCMChannelStatusSyncerTest::~GCMChannelStatusSyncerTest() { +} + +void GCMChannelStatusSyncerTest::SetUp() { + GCMDriverTest::SetUp(); + + // Turn on all-user support. + ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("GCM", "Enabled")); +} + +void GCMChannelStatusSyncerTest::CompleteGCMChannelStatusRequest( + bool enabled, int poll_interval_seconds) { + gcm_proto::ExperimentStatusResponse response_proto; + response_proto.mutable_gcm_channel()->set_enabled(enabled); + + if (poll_interval_seconds) + response_proto.set_poll_interval_seconds(poll_interval_seconds); + + std::string response_string; + response_proto.SerializeToString(&response_string); + + net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + fetcher->set_response_code(net::HTTP_OK); + fetcher->SetResponseString(response_string); + fetcher->delegate()->OnURLFetchComplete(fetcher); +} + +bool GCMChannelStatusSyncerTest::CompareDelaySeconds( + bool expected_delay_seconds, bool actual_delay_seconds) { + // Most of time, the actual delay should not be smaller than the expected + // delay. + if (actual_delay_seconds >= expected_delay_seconds) + return true; + // It is also OK that the actual delay is a bit smaller than the expected + // delay in case that the test runs slowly. + return expected_delay_seconds - actual_delay_seconds < 30; +} + +TEST_F(GCMChannelStatusSyncerTest, DisableAndEnable) { + // Create GCMDriver first. GCM is not started. + CreateDriver(FakeGCMClient::NO_DELAY_START); + EXPECT_FALSE(driver()->IsStarted()); + + // By default, GCM is enabled. + EXPECT_TRUE(driver()->gcm_enabled()); + EXPECT_TRUE(syncer()->gcm_enabled()); + + // Remove delay such that the request could be executed immediately. + syncer()->set_delay_removed_for_testing(true); + + // GCM will be started after app handler is added. + AddAppHandlers(); + EXPECT_TRUE(driver()->IsStarted()); + + // GCM is still enabled at this point. + EXPECT_TRUE(driver()->gcm_enabled()); + EXPECT_TRUE(syncer()->gcm_enabled()); + + // Wait until the GCM channel status request gets triggered. + PumpUILoop(); + + // Complete the request that disables the GCM. + CompleteGCMChannelStatusRequest(false, 0); + EXPECT_FALSE(driver()->gcm_enabled()); + EXPECT_FALSE(syncer()->gcm_enabled()); + EXPECT_FALSE(driver()->IsStarted()); + + // Wait until next GCM channel status request gets triggered. + PumpUILoop(); + + // Complete the request that enables the GCM. + CompleteGCMChannelStatusRequest(true, 0); + EXPECT_TRUE(driver()->gcm_enabled()); + EXPECT_TRUE(syncer()->gcm_enabled()); + EXPECT_TRUE(driver()->IsStarted()); +} + +TEST_F(GCMChannelStatusSyncerTest, DisableAndRestart) { + // Create GCMDriver first. GCM is not started. + CreateDriver(FakeGCMClient::NO_DELAY_START); + EXPECT_FALSE(driver()->IsStarted()); + + // By default, GCM is enabled. + EXPECT_TRUE(driver()->gcm_enabled()); + EXPECT_TRUE(syncer()->gcm_enabled()); + + // Remove delay such that the request could be executed immediately. + syncer()->set_delay_removed_for_testing(true); + + // GCM will be started after app handler is added. + AddAppHandlers(); + EXPECT_TRUE(driver()->IsStarted()); + + // GCM is still enabled at this point. + EXPECT_TRUE(driver()->gcm_enabled()); + EXPECT_TRUE(syncer()->gcm_enabled()); + + // Wait until the GCM channel status request gets triggered. + PumpUILoop(); + + // Complete the request that disables the GCM. + CompleteGCMChannelStatusRequest(false, 0); + EXPECT_FALSE(driver()->gcm_enabled()); + EXPECT_FALSE(syncer()->gcm_enabled()); + EXPECT_FALSE(driver()->IsStarted()); + + // Simulate browser start by recreating GCMDriver. + ShutdownDriver(); + CreateDriver(FakeGCMClient::NO_DELAY_START); + + // GCM is still disabled. + EXPECT_FALSE(driver()->gcm_enabled()); + EXPECT_FALSE(syncer()->gcm_enabled()); + EXPECT_FALSE(driver()->IsStarted()); + + AddAppHandlers(); + EXPECT_FALSE(driver()->gcm_enabled()); + EXPECT_FALSE(syncer()->gcm_enabled()); + EXPECT_FALSE(driver()->IsStarted()); +} + +TEST_F(GCMChannelStatusSyncerTest, FirstTimePolling) { + // Start GCM. + CreateDriver(FakeGCMClient::NO_DELAY_START); + AddAppHandlers(); + + // The 1st request should be triggered shortly without jittering. + EXPECT_EQ(GCMChannelStatusSyncer::first_time_delay_seconds(), + syncer()->current_request_delay_interval().InSeconds()); +} + +TEST_F(GCMChannelStatusSyncerTest, SubsequentPollingWithDefaultInterval) { + // Create GCMDriver first. GCM is not started. + CreateDriver(FakeGCMClient::NO_DELAY_START); + + // Remove delay such that the request could be executed immediately. + syncer()->set_delay_removed_for_testing(true); + + // Now GCM is started. + AddAppHandlers(); + + // Wait until the GCM channel status request gets triggered. + PumpUILoop(); + + // Keep delay such that we can find out the computed delay time. + syncer()->set_delay_removed_for_testing(false); + + // Complete the request. The default interval is intact. + CompleteGCMChannelStatusRequest(true, 0); + + // The next request should be scheduled at the expected default interval. + int64 actual_delay_seconds = + syncer()->current_request_delay_interval().InSeconds(); + int64 expected_delay_seconds = + GCMChannelStatusRequest::default_poll_interval_seconds(); + EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds)) + << "expected delay: " << expected_delay_seconds + << " actual delay: " << actual_delay_seconds; + + // Simulate browser start by recreating GCMDriver. + ShutdownDriver(); + CreateDriver(FakeGCMClient::NO_DELAY_START); + AddAppHandlers(); + + // After start-up, the request should still be scheduled at the expected + // default interval. + actual_delay_seconds = + syncer()->current_request_delay_interval().InSeconds(); + EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds)) + << "expected delay: " << expected_delay_seconds + << " actual delay: " << actual_delay_seconds; +} + +TEST_F(GCMChannelStatusSyncerTest, SubsequentPollingWithUpdatedInterval) { + // Create GCMDriver first. GCM is not started. + CreateDriver(FakeGCMClient::NO_DELAY_START); + + // Remove delay such that the request could be executed immediately. + syncer()->set_delay_removed_for_testing(true); + + // Now GCM is started. + AddAppHandlers(); + + // Wait until the GCM channel status request gets triggered. + PumpUILoop(); + + // Keep delay such that we can find out the computed delay time. + syncer()->set_delay_removed_for_testing(false); + + // Complete the request. The interval is being changed. + int new_poll_interval_seconds = + GCMChannelStatusRequest::default_poll_interval_seconds() * 2; + CompleteGCMChannelStatusRequest(true, new_poll_interval_seconds); + + // The next request should be scheduled at the expected updated interval. + int64 actual_delay_seconds = + syncer()->current_request_delay_interval().InSeconds(); + int64 expected_delay_seconds = new_poll_interval_seconds; + EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds)) + << "expected delay: " << expected_delay_seconds + << " actual delay: " << actual_delay_seconds; + + // Simulate browser start by recreating GCMDriver. + ShutdownDriver(); + CreateDriver(FakeGCMClient::NO_DELAY_START); + AddAppHandlers(); + + // After start-up, the request should still be scheduled at the expected + // updated interval. + actual_delay_seconds = + syncer()->current_request_delay_interval().InSeconds(); + EXPECT_TRUE(CompareDelaySeconds(expected_delay_seconds, actual_delay_seconds)) + << "expected delay: " << expected_delay_seconds + << " actual delay: " << actual_delay_seconds; +} + } // namespace gcm |