// Copyright 2013 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 #include "base/basictypes.h" #include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" #include "base/metrics/histogram_samples.h" #include "base/metrics/sample_map.h" #include "base/metrics/statistics_recorder.h" #include "base/run_loop.h" #include "base/test/simple_test_clock.h" #include "base/test/test_simple_task_runner.h" #include "base/time/time.h" #include "base/values.h" #include "chrome/browser/invalidation/fake_invalidation_service.h" #include "chrome/browser/policy/cloud/cloud_policy_invalidator.h" #include "components/policy/core/common/cloud/cloud_policy_constants.h" #include "components/policy/core/common/cloud/cloud_policy_core.h" #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h" #include "components/policy/core/common/cloud/enterprise_metrics.h" #include "components/policy/core/common/cloud/mock_cloud_policy_client.h" #include "components/policy/core/common/cloud/mock_cloud_policy_store.h" #include "components/policy/core/common/policy_types.h" #include "policy/policy_constants.h" #include "policy/proto/device_management_backend.pb.h" #include "sync/notifier/invalidation_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace policy { class CloudPolicyInvalidatorTest : public testing::Test { protected: // Policy objects which can be used in tests. enum PolicyObject { POLICY_OBJECT_NONE, POLICY_OBJECT_A, POLICY_OBJECT_B }; CloudPolicyInvalidatorTest(); virtual void SetUp() OVERRIDE; virtual void TearDown() OVERRIDE; // Starts the invalidator which will be tested. // |initialize| determines if the invalidator should be initialized. // |start_refresh_scheduler| determines if the refresh scheduler should start. void StartInvalidator(bool initialize, bool start_refresh_scheduler); void StartInvalidator() { StartInvalidator(true /* initialize */, true /* start_refresh_scheduler */); } // Calls Initialize on the invalidator. void InitializeInvalidator(); // Calls Shutdown on the invalidator. Test must call DestroyInvalidator // afterwards to prevent Shutdown from being called twice. void ShutdownInvalidator(); // Destroys the invalidator. void DestroyInvalidator(); // Connects the cloud policy core. void ConnectCore(); // Starts the refresh scheduler. void StartRefreshScheduler(); // Disconnects the cloud policy core. void DisconnectCore(); // Simulates storing a new policy to the policy store. // |object| determines which policy object the store will report the // invalidator should register for. May be POLICY_OBJECT_NONE for no object. // |invalidation_version| determines what invalidation the store will report. // |policy_changed| determines whether a policy value different from the // current value will be stored. // |time| determines the timestamp the store will report. void StorePolicy( PolicyObject object, int64 invalidation_version, bool policy_changed, const base::Time& time); void StorePolicy( PolicyObject object, int64 invalidation_version, bool policy_changed) { StorePolicy(object, invalidation_version, policy_changed, Now() - base::TimeDelta::FromMinutes(5)); } void StorePolicy(PolicyObject object, int64 invalidation_version) { StorePolicy(object, invalidation_version, false); } void StorePolicy(PolicyObject object) { StorePolicy(object, 0); } // Disables the invalidation service. It is enabled by default. void DisableInvalidationService(); // Enables the invalidation service. It is enabled by default. void EnableInvalidationService(); // Causes the invalidation service to fire an invalidation. syncer::Invalidation FireInvalidation( PolicyObject object, int64 version, const std::string& payload); // Causes the invalidation service to fire an invalidation with unknown // version. syncer::Invalidation FireUnknownVersionInvalidation(PolicyObject object); // Checks the expected value of the currently set invalidation info. bool CheckInvalidationInfo(int64 version, const std::string& payload); // Checks that the policy was not refreshed due to an invalidation. bool CheckPolicyNotRefreshed(); // Checks that the policy was refreshed due to an invalidation within an // appropriate timeframe depending on whether the invalidation had unknown // version. bool CheckPolicyRefreshed(); bool CheckPolicyRefreshedWithUnknownVersion(); bool IsUnsent(const syncer::Invalidation& invalidation); // Returns the invalidations enabled state set by the invalidator on the // refresh scheduler. bool InvalidationsEnabled(); // Determines if the invalidation with the given ack handle has been // acknowledged. bool IsInvalidationAcknowledged(const syncer::Invalidation& invalidation); // Determines if the invalidator has registered for an object with the // invalidation service. bool IsInvalidatorRegistered(); // Get the current count for the given metric. base::HistogramBase::Count GetCount(MetricPolicyRefresh metric); base::HistogramBase::Count GetInvalidationCount(PolicyInvalidationType type); // Advance the test clock. void AdvanceClock(base::TimeDelta delta); // Get the current time on the test clock. base::Time Now(); // Translate a version number into an appropriate invalidation version (which // is based on the current time). int64 V(int version); // Get an invalidation version for the given time. int64 GetVersion(base::Time time); private: // Checks that the policy was refreshed due to an invalidation with the given // base delay. bool CheckPolicyRefreshed(base::TimeDelta delay); // Checks that the policy was refreshed the given number of times. bool CheckPolicyRefreshCount(int count); // Returns the object id of the given policy object. const invalidation::ObjectId& GetPolicyObjectId(PolicyObject object) const; // Get histogram samples for the given histogram. scoped_ptr GetHistogramSamples( const std::string& name) const; base::MessageLoop loop_; // Objects the invalidator depends on. invalidation::FakeInvalidationService invalidation_service_; MockCloudPolicyStore store_; CloudPolicyCore core_; MockCloudPolicyClient* client_; scoped_refptr task_runner_; base::SimpleTestClock* clock_; // The invalidator which will be tested. scoped_ptr invalidator_; // Object ids for the test policy objects. invalidation::ObjectId object_id_a_; invalidation::ObjectId object_id_b_; // Fake policy values which are alternated to cause the store to report a // changed policy. const char* policy_value_a_; const char* policy_value_b_; // The currently used policy value. const char* policy_value_cur_; // Stores starting histogram counts for kMetricPolicyRefresh. scoped_ptr refresh_samples_; // Stores starting histogram counts for kMetricPolicyInvalidations. scoped_ptr invalidations_samples_; }; CloudPolicyInvalidatorTest::CloudPolicyInvalidatorTest() : core_(PolicyNamespaceKey(dm_protocol::kChromeUserPolicyType, std::string()), &store_, loop_.message_loop_proxy()), client_(NULL), task_runner_(new base::TestSimpleTaskRunner()), clock_(new base::SimpleTestClock()), object_id_a_(135, "asdf"), object_id_b_(246, "zxcv"), policy_value_a_("asdf"), policy_value_b_("zxcv"), policy_value_cur_(policy_value_a_) { clock_->SetNow( base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(987654321)); } void CloudPolicyInvalidatorTest::SetUp() { base::StatisticsRecorder::Initialize(); refresh_samples_ = GetHistogramSamples(kMetricPolicyRefresh); invalidations_samples_ = GetHistogramSamples(kMetricPolicyInvalidations); } void CloudPolicyInvalidatorTest::TearDown() { if (invalidator_) invalidator_->Shutdown(); core_.Disconnect(); } void CloudPolicyInvalidatorTest::StartInvalidator( bool initialize, bool start_refresh_scheduler) { invalidator_.reset(new CloudPolicyInvalidator( &core_, task_runner_, scoped_ptr(clock_))); if (start_refresh_scheduler) { ConnectCore(); StartRefreshScheduler(); } if (initialize) InitializeInvalidator(); } void CloudPolicyInvalidatorTest::InitializeInvalidator() { invalidator_->Initialize(&invalidation_service_); } void CloudPolicyInvalidatorTest::ShutdownInvalidator() { invalidator_->Shutdown(); } void CloudPolicyInvalidatorTest::DestroyInvalidator() { invalidator_.reset(); } void CloudPolicyInvalidatorTest::ConnectCore() { client_ = new MockCloudPolicyClient(); client_->SetDMToken("dm"); core_.Connect(scoped_ptr(client_)); } void CloudPolicyInvalidatorTest::StartRefreshScheduler() { core_.StartRefreshScheduler(); } void CloudPolicyInvalidatorTest::DisconnectCore() { client_ = NULL; core_.Disconnect(); } void CloudPolicyInvalidatorTest::StorePolicy( PolicyObject object, int64 invalidation_version, bool policy_changed, const base::Time& time) { enterprise_management::PolicyData* data = new enterprise_management::PolicyData(); if (object != POLICY_OBJECT_NONE) { data->set_invalidation_source(GetPolicyObjectId(object).source()); data->set_invalidation_name(GetPolicyObjectId(object).name()); } data->set_timestamp((time - base::Time::UnixEpoch()).InMilliseconds()); // Swap the policy value if a policy change is desired. if (policy_changed) policy_value_cur_ = policy_value_cur_ == policy_value_a_ ? policy_value_b_ : policy_value_a_; data->set_policy_value(policy_value_cur_); store_.invalidation_version_ = invalidation_version; store_.policy_.reset(data); base::DictionaryValue policies; policies.SetInteger( key::kMaxInvalidationFetchDelay, CloudPolicyInvalidator::kMaxFetchDelayMin); store_.policy_map_.LoadFrom( &policies, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE); store_.NotifyStoreLoaded(); } void CloudPolicyInvalidatorTest::DisableInvalidationService() { invalidation_service_.SetInvalidatorState( syncer::TRANSIENT_INVALIDATION_ERROR); } void CloudPolicyInvalidatorTest::EnableInvalidationService() { invalidation_service_.SetInvalidatorState(syncer::INVALIDATIONS_ENABLED); } syncer::Invalidation CloudPolicyInvalidatorTest::FireInvalidation( PolicyObject object, int64 version, const std::string& payload) { syncer::Invalidation invalidation = syncer::Invalidation::Init( GetPolicyObjectId(object), version, payload); invalidation_service_.EmitInvalidationForTest(invalidation); return invalidation; } syncer::Invalidation CloudPolicyInvalidatorTest::FireUnknownVersionInvalidation( PolicyObject object) { syncer::Invalidation invalidation = syncer::Invalidation::InitUnknownVersion( GetPolicyObjectId(object)); invalidation_service_.EmitInvalidationForTest(invalidation); return invalidation; } bool CloudPolicyInvalidatorTest::CheckInvalidationInfo( int64 version, const std::string& payload) { MockCloudPolicyClient* client = static_cast(core_.client()); return version == client->invalidation_version_ && payload == client->invalidation_payload_; } bool CloudPolicyInvalidatorTest::CheckPolicyNotRefreshed() { return CheckPolicyRefreshCount(0); } bool CloudPolicyInvalidatorTest::CheckPolicyRefreshed() { return CheckPolicyRefreshed(base::TimeDelta()); } bool CloudPolicyInvalidatorTest::IsUnsent( const syncer::Invalidation& invalidation) { return invalidation_service_.GetMockAckHandler()->IsUnsent(invalidation); } bool CloudPolicyInvalidatorTest::CheckPolicyRefreshedWithUnknownVersion() { return CheckPolicyRefreshed(base::TimeDelta::FromMinutes( CloudPolicyInvalidator::kMissingPayloadDelay)); } bool CloudPolicyInvalidatorTest::InvalidationsEnabled() { return core_.refresh_scheduler()->invalidations_available(); } bool CloudPolicyInvalidatorTest::IsInvalidationAcknowledged( const syncer::Invalidation& invalidation) { // The acknowledgement task is run through a WeakHandle that posts back to our // own thread. We need to run any posted tasks before we can check // acknowledgement status. loop_.RunUntilIdle(); EXPECT_FALSE(IsUnsent(invalidation)); return !invalidation_service_.GetMockAckHandler()->IsUnacked(invalidation); } bool CloudPolicyInvalidatorTest::IsInvalidatorRegistered() { return !invalidation_service_.invalidator_registrar() .GetRegisteredIds(invalidator_.get()).empty(); } base::HistogramBase::Count CloudPolicyInvalidatorTest::GetCount( MetricPolicyRefresh metric) { return GetHistogramSamples(kMetricPolicyRefresh)->GetCount(metric) - refresh_samples_->GetCount(metric); } base::HistogramBase::Count CloudPolicyInvalidatorTest::GetInvalidationCount( PolicyInvalidationType type) { return GetHistogramSamples(kMetricPolicyInvalidations)->GetCount(type) - invalidations_samples_->GetCount(type); } void CloudPolicyInvalidatorTest::AdvanceClock(base::TimeDelta delta) { clock_->Advance(delta); } base::Time CloudPolicyInvalidatorTest::Now() { return clock_->Now(); } int64 CloudPolicyInvalidatorTest::V(int version) { return GetVersion(Now()) + version; } int64 CloudPolicyInvalidatorTest::GetVersion(base::Time time) { return (time - base::Time::UnixEpoch()).InMicroseconds(); } bool CloudPolicyInvalidatorTest::CheckPolicyRefreshed(base::TimeDelta delay) { base::TimeDelta max_delay = delay + base::TimeDelta::FromMilliseconds( CloudPolicyInvalidator::kMaxFetchDelayMin); if (task_runner_->GetPendingTasks().empty()) return false; base::TimeDelta actual_delay = task_runner_->GetPendingTasks().back().delay; EXPECT_GE(actual_delay, delay); EXPECT_LE(actual_delay, max_delay); return CheckPolicyRefreshCount(1); } bool CloudPolicyInvalidatorTest::CheckPolicyRefreshCount(int count) { if (!client_) { task_runner_->RunUntilIdle(); return count == 0; } // Clear any non-invalidation refreshes which may be pending. EXPECT_CALL(*client_, FetchPolicy()).Times(testing::AnyNumber()); base::RunLoop().RunUntilIdle(); testing::Mock::VerifyAndClearExpectations(client_); // Run the invalidator tasks then check for invalidation refreshes. EXPECT_CALL(*client_, FetchPolicy()).Times(count); task_runner_->RunUntilIdle(); base::RunLoop().RunUntilIdle(); return testing::Mock::VerifyAndClearExpectations(client_); } const invalidation::ObjectId& CloudPolicyInvalidatorTest::GetPolicyObjectId( PolicyObject object) const { EXPECT_TRUE(object == POLICY_OBJECT_A || object == POLICY_OBJECT_B); return object == POLICY_OBJECT_A ? object_id_a_ : object_id_b_; } scoped_ptr CloudPolicyInvalidatorTest::GetHistogramSamples( const std::string& name) const { base::HistogramBase* histogram = base::StatisticsRecorder::FindHistogram(name); if (!histogram) return scoped_ptr(new base::SampleMap()); return histogram->SnapshotSamples(); } TEST_F(CloudPolicyInvalidatorTest, Uninitialized) { // No invalidations should be processed if the invalidator is not initialized. StartInvalidator(false /* initialize */, true /* start_refresh_scheduler */); StorePolicy(POLICY_OBJECT_A); EXPECT_FALSE(IsInvalidatorRegistered()); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_A))); EXPECT_TRUE(CheckPolicyNotRefreshed()); } TEST_F(CloudPolicyInvalidatorTest, RefreshSchedulerNotStarted) { // No invalidations should be processed if the refresh scheduler is not // started. StartInvalidator(true /* initialize */, false /* start_refresh_scheduler */); StorePolicy(POLICY_OBJECT_A); EXPECT_FALSE(IsInvalidatorRegistered()); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_A))); EXPECT_TRUE(CheckPolicyNotRefreshed()); } TEST_F(CloudPolicyInvalidatorTest, DisconnectCoreThenInitialize) { // No invalidations should be processed if the core is disconnected before // initialization. StartInvalidator(false /* initialize */, true /* start_refresh_scheduler */); DisconnectCore(); InitializeInvalidator(); StorePolicy(POLICY_OBJECT_A); EXPECT_FALSE(IsInvalidatorRegistered()); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_A))); EXPECT_TRUE(CheckPolicyNotRefreshed()); } TEST_F(CloudPolicyInvalidatorTest, InitializeThenStartRefreshScheduler) { // Make sure registration occurs and invalidations are processed when // Initialize is called before starting the refresh scheduler. // Note that the reverse case (start refresh scheduler then initialize) is // the default behavior for the test fixture, so will be tested in most other // tests. StartInvalidator(true /* initialize */, false /* start_refresh_scheduler */); ConnectCore(); StartRefreshScheduler(); StorePolicy(POLICY_OBJECT_A); EXPECT_TRUE(IsInvalidatorRegistered()); FireUnknownVersionInvalidation(POLICY_OBJECT_A); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); } TEST_F(CloudPolicyInvalidatorTest, RegisterOnStoreLoaded) { // No registration when store is not loaded. StartInvalidator(); EXPECT_FALSE(IsInvalidatorRegistered()); EXPECT_FALSE(InvalidationsEnabled()); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_A))); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_B))); EXPECT_TRUE(CheckPolicyNotRefreshed()); // No registration when store is loaded with no invalidation object id. StorePolicy(POLICY_OBJECT_NONE); EXPECT_FALSE(IsInvalidatorRegistered()); EXPECT_FALSE(InvalidationsEnabled()); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_A))); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_B))); EXPECT_TRUE(CheckPolicyNotRefreshed()); // Check registration when store is loaded for object A. StorePolicy(POLICY_OBJECT_A); EXPECT_TRUE(IsInvalidatorRegistered()); EXPECT_TRUE(InvalidationsEnabled()); FireUnknownVersionInvalidation(POLICY_OBJECT_A); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_B))); EXPECT_TRUE(CheckPolicyNotRefreshed()); } TEST_F(CloudPolicyInvalidatorTest, ChangeRegistration) { // Register for object A. StartInvalidator(); StorePolicy(POLICY_OBJECT_A); EXPECT_TRUE(IsInvalidatorRegistered()); EXPECT_TRUE(InvalidationsEnabled()); FireUnknownVersionInvalidation(POLICY_OBJECT_A); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_B))); EXPECT_TRUE(CheckPolicyNotRefreshed()); syncer::Invalidation inv = FireUnknownVersionInvalidation(POLICY_OBJECT_A); // Check re-registration for object B. Make sure the pending invalidation for // object A is acknowledged without making the callback. StorePolicy(POLICY_OBJECT_B); EXPECT_TRUE(IsInvalidatorRegistered()); EXPECT_TRUE(InvalidationsEnabled()); EXPECT_TRUE(IsInvalidationAcknowledged(inv)); EXPECT_TRUE(CheckPolicyNotRefreshed()); // Make sure future invalidations for object A are ignored and for object B // are processed. EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_A))); EXPECT_TRUE(CheckPolicyNotRefreshed()); FireUnknownVersionInvalidation(POLICY_OBJECT_B); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); } TEST_F(CloudPolicyInvalidatorTest, UnregisterOnStoreLoaded) { // Register for object A. StartInvalidator(); StorePolicy(POLICY_OBJECT_A); EXPECT_TRUE(IsInvalidatorRegistered()); EXPECT_TRUE(InvalidationsEnabled()); FireUnknownVersionInvalidation(POLICY_OBJECT_A); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); // Check unregistration when store is loaded with no invalidation object id. syncer::Invalidation inv = FireUnknownVersionInvalidation(POLICY_OBJECT_A); EXPECT_FALSE(IsInvalidationAcknowledged(inv)); StorePolicy(POLICY_OBJECT_NONE); EXPECT_FALSE(IsInvalidatorRegistered()); EXPECT_TRUE(IsInvalidationAcknowledged(inv)); EXPECT_FALSE(InvalidationsEnabled()); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_A))); EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_B))); EXPECT_TRUE(CheckPolicyNotRefreshed()); // Check re-registration for object B. StorePolicy(POLICY_OBJECT_B); EXPECT_TRUE(IsInvalidatorRegistered()); EXPECT_TRUE(InvalidationsEnabled()); FireUnknownVersionInvalidation(POLICY_OBJECT_B); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); } TEST_F(CloudPolicyInvalidatorTest, HandleInvalidation) { // Register and fire invalidation StorePolicy(POLICY_OBJECT_A); StartInvalidator(); EXPECT_TRUE(InvalidationsEnabled()); syncer::Invalidation inv = FireInvalidation(POLICY_OBJECT_A, V(12), "test_payload"); // Make sure client info is set as soon as the invalidation is received. EXPECT_TRUE(CheckInvalidationInfo(V(12), "test_payload")); EXPECT_TRUE(CheckPolicyRefreshed()); // Make sure invalidation is not acknowledged until the store is loaded. EXPECT_FALSE(IsInvalidationAcknowledged(inv)); EXPECT_TRUE(CheckInvalidationInfo(V(12), "test_payload")); StorePolicy(POLICY_OBJECT_A, V(12)); EXPECT_TRUE(IsInvalidationAcknowledged(inv)); EXPECT_TRUE(CheckInvalidationInfo(0, std::string())); } TEST_F(CloudPolicyInvalidatorTest, HandleInvalidationWithUnknownVersion) { // Register and fire invalidation with unknown version. StorePolicy(POLICY_OBJECT_A); StartInvalidator(); syncer::Invalidation inv = FireUnknownVersionInvalidation(POLICY_OBJECT_A); // Make sure client info is not set until after the invalidation callback is // made. EXPECT_TRUE(CheckInvalidationInfo(0, std::string())); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); EXPECT_TRUE(CheckInvalidationInfo(-1, std::string())); // Make sure invalidation is not acknowledged until the store is loaded. EXPECT_FALSE(IsInvalidationAcknowledged(inv)); StorePolicy(POLICY_OBJECT_A, -1); EXPECT_TRUE(IsInvalidationAcknowledged(inv)); EXPECT_TRUE(CheckInvalidationInfo(0, std::string())); } TEST_F(CloudPolicyInvalidatorTest, HandleMultipleInvalidations) { // Generate multiple invalidations. StorePolicy(POLICY_OBJECT_A); StartInvalidator(); syncer::Invalidation inv1 = FireInvalidation(POLICY_OBJECT_A, V(1), "test1"); EXPECT_TRUE(CheckInvalidationInfo(V(1), "test1")); syncer::Invalidation inv2 = FireInvalidation(POLICY_OBJECT_A, V(2), "test2"); EXPECT_TRUE(CheckInvalidationInfo(V(2), "test2")); syncer::Invalidation inv3 = FireInvalidation(POLICY_OBJECT_A, V(3), "test3"); EXPECT_TRUE(CheckInvalidationInfo(V(3), "test3")); // Make sure the replaced invalidations are acknowledged. EXPECT_TRUE(IsInvalidationAcknowledged(inv1)); EXPECT_TRUE(IsInvalidationAcknowledged(inv2)); // Make sure the policy is refreshed once. EXPECT_TRUE(CheckPolicyRefreshed()); // Make sure that the last invalidation is only acknowledged after the store // is loaded with the latest version. StorePolicy(POLICY_OBJECT_A, V(1)); EXPECT_FALSE(IsInvalidationAcknowledged(inv3)); StorePolicy(POLICY_OBJECT_A, V(2)); EXPECT_FALSE(IsInvalidationAcknowledged(inv3)); StorePolicy(POLICY_OBJECT_A, V(3)); EXPECT_TRUE(IsInvalidationAcknowledged(inv3)); } TEST_F(CloudPolicyInvalidatorTest, HandleMultipleInvalidationsWithUnknownVersion) { // Validate that multiple invalidations with unknown version each generate // unique invalidation version numbers. StorePolicy(POLICY_OBJECT_A); StartInvalidator(); syncer::Invalidation inv1 = FireUnknownVersionInvalidation(POLICY_OBJECT_A); EXPECT_TRUE(CheckInvalidationInfo(0, std::string())); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); EXPECT_TRUE(CheckInvalidationInfo(-1, std::string())); syncer::Invalidation inv2 = FireUnknownVersionInvalidation(POLICY_OBJECT_A); EXPECT_TRUE(CheckInvalidationInfo(0, std::string())); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); EXPECT_TRUE(CheckInvalidationInfo(-2, std::string())); syncer::Invalidation inv3 = FireUnknownVersionInvalidation(POLICY_OBJECT_A); EXPECT_TRUE(CheckInvalidationInfo(0, std::string())); EXPECT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); EXPECT_TRUE(CheckInvalidationInfo(-3, std::string())); // Make sure the replaced invalidations are acknowledged. EXPECT_TRUE(IsInvalidationAcknowledged(inv1)); EXPECT_TRUE(IsInvalidationAcknowledged(inv2)); // Make sure that the last invalidation is only acknowledged after the store // is loaded with the last unknown version. StorePolicy(POLICY_OBJECT_A, -1); EXPECT_FALSE(IsInvalidationAcknowledged(inv3)); StorePolicy(POLICY_OBJECT_A, -2); EXPECT_FALSE(IsInvalidationAcknowledged(inv3)); StorePolicy(POLICY_OBJECT_A, -3); EXPECT_TRUE(IsInvalidationAcknowledged(inv3)); } TEST_F(CloudPolicyInvalidatorTest, AcknowledgeBeforeRefresh) { // Generate an invalidation. StorePolicy(POLICY_OBJECT_A); StartInvalidator(); syncer::Invalidation inv = FireInvalidation(POLICY_OBJECT_A, V(3), "test"); // Ensure that the policy is not refreshed and the invalidation is // acknowledged if the store is loaded with the latest version before the // refresh can occur. StorePolicy(POLICY_OBJECT_A, V(3)); EXPECT_TRUE(IsInvalidationAcknowledged(inv)); EXPECT_TRUE(CheckPolicyNotRefreshed()); } TEST_F(CloudPolicyInvalidatorTest, NoCallbackAfterShutdown) { // Generate an invalidation. StorePolicy(POLICY_OBJECT_A); StartInvalidator(); syncer::Invalidation inv = FireInvalidation(POLICY_OBJECT_A, V(3), "test"); // Ensure that the policy refresh is not made after the invalidator is shut // down. ShutdownInvalidator(); EXPECT_TRUE(CheckPolicyNotRefreshed()); DestroyInvalidator(); } TEST_F(CloudPolicyInvalidatorTest, StateChanged) { // Test invalidation service state changes while not registered. StartInvalidator(); DisableInvalidationService(); EnableInvalidationService(); EXPECT_FALSE(InvalidationsEnabled()); // Test invalidation service state changes while registered. StorePolicy(POLICY_OBJECT_A); EXPECT_TRUE(InvalidationsEnabled()); DisableInvalidationService(); EXPECT_FALSE(InvalidationsEnabled()); DisableInvalidationService(); EXPECT_FALSE(InvalidationsEnabled()); EnableInvalidationService(); EXPECT_TRUE(InvalidationsEnabled()); EnableInvalidationService(); EXPECT_TRUE(InvalidationsEnabled()); // Test registration changes with invalidation service enabled. StorePolicy(POLICY_OBJECT_NONE); EXPECT_FALSE(InvalidationsEnabled()); StorePolicy(POLICY_OBJECT_NONE); EXPECT_FALSE(InvalidationsEnabled()); StorePolicy(POLICY_OBJECT_A); EXPECT_TRUE(InvalidationsEnabled()); StorePolicy(POLICY_OBJECT_A); EXPECT_TRUE(InvalidationsEnabled()); // Test registration changes with invalidation service disabled. DisableInvalidationService(); EXPECT_FALSE(InvalidationsEnabled()); StorePolicy(POLICY_OBJECT_NONE); StorePolicy(POLICY_OBJECT_A); EXPECT_FALSE(InvalidationsEnabled()); } TEST_F(CloudPolicyInvalidatorTest, Disconnect) { // Generate an invalidation. StorePolicy(POLICY_OBJECT_A); StartInvalidator(); syncer::Invalidation inv = FireInvalidation(POLICY_OBJECT_A, V(1), "test"); EXPECT_TRUE(InvalidationsEnabled()); // Ensure that the policy is not refreshed after disconnecting the core, but // a call to indicate that invalidations are disabled is made. DisconnectCore(); EXPECT_TRUE(CheckPolicyNotRefreshed()); // Ensure that invalidation service events do not cause refreshes while the // invalidator is stopped. EXPECT_TRUE(IsUnsent(FireInvalidation(POLICY_OBJECT_A, V(2), "test"))); EXPECT_TRUE(CheckPolicyNotRefreshed()); DisableInvalidationService(); EnableInvalidationService(); // Connect and disconnect without starting the refresh scheduler. ConnectCore(); EXPECT_TRUE(IsUnsent(FireInvalidation(POLICY_OBJECT_A, V(3), "test"))); EXPECT_TRUE(CheckPolicyNotRefreshed()); DisconnectCore(); EXPECT_TRUE(IsUnsent(FireInvalidation(POLICY_OBJECT_A, V(4), "test"))); EXPECT_TRUE(CheckPolicyNotRefreshed()); // Ensure that the invalidator returns to normal after reconnecting. ConnectCore(); StartRefreshScheduler(); EXPECT_TRUE(CheckPolicyNotRefreshed()); EXPECT_TRUE(InvalidationsEnabled()); FireInvalidation(POLICY_OBJECT_A, V(5), "test"); EXPECT_TRUE(CheckInvalidationInfo(V(5), "test")); EXPECT_TRUE(CheckPolicyRefreshed()); DisableInvalidationService(); EXPECT_FALSE(InvalidationsEnabled()); } TEST_F(CloudPolicyInvalidatorTest, RefreshMetricsUnregistered) { // Store loads occurring before invalidation registration are not counted. StartInvalidator(); StorePolicy(POLICY_OBJECT_NONE, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_NONE, 0, true /* policy_changed */); EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_CHANGED)); EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS)); EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_UNCHANGED)); EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_CHANGED)); EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED)); } TEST_F(CloudPolicyInvalidatorTest, RefreshMetricsNoInvalidations) { // Store loads occurring while registered should be differentiated depending // on whether the invalidation service was enabled or not. StorePolicy(POLICY_OBJECT_A); StartInvalidator(); // Initially, invalidations have not been enabled past the grace period, so // invalidations are OFF. StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); EXPECT_EQ(1, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS)); // If the clock advances less than the grace period, invalidations are OFF. AdvanceClock(base::TimeDelta::FromSeconds(1)); StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); EXPECT_EQ(2, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS)); // After the grace period elapses, invalidations are ON. AdvanceClock(base::TimeDelta::FromSeconds( CloudPolicyInvalidator::kInvalidationGracePeriod)); StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); EXPECT_EQ(1, GetCount(METRIC_POLICY_REFRESH_CHANGED)); // After the invalidation service is disabled, invalidations are OFF. DisableInvalidationService(); StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); EXPECT_EQ(3, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS)); // Enabling the invalidation service results in a new grace period, so // invalidations are OFF. EnableInvalidationService(); StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); EXPECT_EQ(4, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS)); // After the grace period elapses, invalidations are ON. AdvanceClock(base::TimeDelta::FromSeconds( CloudPolicyInvalidator::kInvalidationGracePeriod)); StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); EXPECT_EQ(2, GetCount(METRIC_POLICY_REFRESH_CHANGED)); EXPECT_EQ(4, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS)); EXPECT_EQ(6, GetCount(METRIC_POLICY_REFRESH_UNCHANGED)); EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_CHANGED)); EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED)); } TEST_F(CloudPolicyInvalidatorTest, RefreshMetricsInvalidation) { // Store loads after an invalidation are counted as invalidated, even if // the loads do not result in the invalidation being acknowledged. StartInvalidator(); StorePolicy(POLICY_OBJECT_A); AdvanceClock(base::TimeDelta::FromSeconds( CloudPolicyInvalidator::kInvalidationGracePeriod)); FireInvalidation(POLICY_OBJECT_A, V(5), "test"); StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); StorePolicy(POLICY_OBJECT_A, V(5), true /* policy_changed */); // Store loads after the invalidation is complete are not counted as // invalidated. StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */); StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */); EXPECT_EQ(3, GetCount(METRIC_POLICY_REFRESH_CHANGED)); EXPECT_EQ(0, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS)); EXPECT_EQ(4, GetCount(METRIC_POLICY_REFRESH_UNCHANGED)); EXPECT_EQ(2, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_CHANGED)); EXPECT_EQ(1, GetCount(METRIC_POLICY_REFRESH_INVALIDATED_UNCHANGED)); } TEST_F(CloudPolicyInvalidatorTest, ExpiredInvalidations) { StorePolicy(POLICY_OBJECT_A, 0, false, Now()); StartInvalidator(); // Invalidations fired before the last fetch time (adjusted by max time delta) // should be ignored. base::Time time = Now() - base::TimeDelta::FromSeconds( CloudPolicyInvalidator::kMaxInvalidationTimeDelta + 300); syncer::Invalidation inv = FireInvalidation(POLICY_OBJECT_A, GetVersion(time), "test"); ASSERT_TRUE(IsInvalidationAcknowledged(inv)); ASSERT_TRUE(CheckPolicyNotRefreshed()); time += base::TimeDelta::FromMinutes(5) - base::TimeDelta::FromSeconds(1); inv = FireInvalidation(POLICY_OBJECT_A, GetVersion(time), "test"); ASSERT_TRUE(IsInvalidationAcknowledged(inv)); ASSERT_TRUE(CheckPolicyNotRefreshed()); // Invalidations fired after the last fetch should not be ignored. time += base::TimeDelta::FromSeconds(1); inv = FireInvalidation(POLICY_OBJECT_A, GetVersion(time), "test"); ASSERT_FALSE(IsInvalidationAcknowledged(inv)); ASSERT_TRUE(CheckPolicyRefreshed()); time += base::TimeDelta::FromMinutes(10); inv = FireInvalidation(POLICY_OBJECT_A, GetVersion(time), "test"); ASSERT_FALSE(IsInvalidationAcknowledged(inv)); ASSERT_TRUE(CheckPolicyRefreshed()); time += base::TimeDelta::FromMinutes(10); inv = FireInvalidation(POLICY_OBJECT_A, GetVersion(time), "test"); ASSERT_FALSE(IsInvalidationAcknowledged(inv)); ASSERT_TRUE(CheckPolicyRefreshed()); // Unknown version invalidations fired just after the last fetch time should // be ignored. inv = FireUnknownVersionInvalidation(POLICY_OBJECT_A); ASSERT_TRUE(IsInvalidationAcknowledged(inv)); ASSERT_TRUE(CheckPolicyNotRefreshed()); AdvanceClock(base::TimeDelta::FromSeconds( CloudPolicyInvalidator::kUnknownVersionIgnorePeriod - 1)); inv = FireUnknownVersionInvalidation(POLICY_OBJECT_A); ASSERT_TRUE(IsInvalidationAcknowledged(inv)); ASSERT_TRUE(CheckPolicyNotRefreshed()); // Unknown version invalidations fired past the ignore period should not be // ignored. AdvanceClock(base::TimeDelta::FromSeconds(1)); inv = FireUnknownVersionInvalidation(POLICY_OBJECT_A); ASSERT_FALSE(IsInvalidationAcknowledged(inv)); ASSERT_TRUE(CheckPolicyRefreshedWithUnknownVersion()); // Verify that received invalidations metrics are correct. EXPECT_EQ(1, GetInvalidationCount(POLICY_INVALIDATION_TYPE_NO_PAYLOAD)); EXPECT_EQ(3, GetInvalidationCount(POLICY_INVALIDATION_TYPE_NORMAL)); EXPECT_EQ(2, GetInvalidationCount(POLICY_INVALIDATION_TYPE_NO_PAYLOAD_EXPIRED)); EXPECT_EQ(2, GetInvalidationCount(POLICY_INVALIDATION_TYPE_EXPIRED)); } } // namespace policy