blob: a7da08c337f1fc7396a1a319a4d9a16573f0b53d [file] [log] [blame]
// 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 <string>
#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/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.
// |timestamp| determines the response timestamp the store will report.
void StorePolicy(
PolicyObject object,
int64 invalidation_version,
bool policy_changed,
int64 timestamp);
void StorePolicy(
PolicyObject object,
int64 invalidation_version,
bool policy_changed) {
StorePolicy(object, invalidation_version, policy_changed, ++timestamp_);
}
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(bool with_payload);
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<base::HistogramSamples> 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<base::TestSimpleTaskRunner> task_runner_;
// The invalidator which will be tested.
scoped_ptr<CloudPolicyInvalidator> invalidator_;
// Object ids for the test policy objects.
invalidation::ObjectId object_id_a_;
invalidation::ObjectId object_id_b_;
// Increasing policy timestamp.
int64 timestamp_;
// 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<base::HistogramSamples> refresh_samples_;
// Stores starting histogram counts for kMetricPolicyInvalidations.
scoped_ptr<base::HistogramSamples> invalidations_samples_;
};
CloudPolicyInvalidatorTest::CloudPolicyInvalidatorTest()
: core_(PolicyNamespaceKey(dm_protocol::kChromeUserPolicyType,
std::string()),
&store_,
loop_.message_loop_proxy()),
client_(NULL),
task_runner_(new base::TestSimpleTaskRunner()),
object_id_a_(135, "asdf"),
object_id_b_(246, "zxcv"),
timestamp_(123456),
policy_value_a_("asdf"),
policy_value_b_("zxcv"),
policy_value_cur_(policy_value_a_) {}
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_));
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<CloudPolicyClient>(client_));
}
void CloudPolicyInvalidatorTest::StartRefreshScheduler() {
core_.StartRefreshScheduler();
}
void CloudPolicyInvalidatorTest::DisconnectCore() {
client_ = NULL;
core_.Disconnect();
}
void CloudPolicyInvalidatorTest::StorePolicy(
PolicyObject object,
int64 invalidation_version,
bool policy_changed,
int64 timestamp) {
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(timestamp);
// 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<MockCloudPolicyClient*>(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(
bool with_payload) {
int metric = with_payload ? 1 : 0;
return GetHistogramSamples(kMetricPolicyInvalidations)->GetCount(metric) -
invalidations_samples_->GetCount(metric);
}
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<base::HistogramSamples>
CloudPolicyInvalidatorTest::GetHistogramSamples(
const std::string& name) const {
base::HistogramBase* histogram =
base::StatisticsRecorder::FindHistogram(name);
if (!histogram)
return scoped_ptr<base::HistogramSamples>(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, 12, "test_payload");
// Make sure client info is set as soon as the invalidation is received.
EXPECT_TRUE(CheckInvalidationInfo(12, "test_payload"));
EXPECT_TRUE(CheckPolicyRefreshed());
// Make sure invalidation is not acknowledged until the store is loaded.
EXPECT_FALSE(IsInvalidationAcknowledged(inv));
EXPECT_TRUE(CheckInvalidationInfo(12, "test_payload"));
StorePolicy(POLICY_OBJECT_A, 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, 1, "test1");
EXPECT_TRUE(CheckInvalidationInfo(1, "test1"));
syncer::Invalidation inv2 = FireInvalidation(POLICY_OBJECT_A, 2, "test2");
EXPECT_TRUE(CheckInvalidationInfo(2, "test2"));
syncer::Invalidation inv3 = FireInvalidation(POLICY_OBJECT_A, 3, "test3");
EXPECT_TRUE(CheckInvalidationInfo(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, 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,
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, 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, 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, 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, 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, 2, "test")));
EXPECT_TRUE(CheckPolicyNotRefreshed());
DisableInvalidationService();
EnableInvalidationService();
// Connect and disconnect without starting the refresh scheduler.
ConnectCore();
EXPECT_TRUE(IsUnsent(FireInvalidation(POLICY_OBJECT_A, 3, "test")));
EXPECT_TRUE(CheckPolicyNotRefreshed());
DisconnectCore();
EXPECT_TRUE(IsUnsent(FireInvalidation(POLICY_OBJECT_A, 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, 5, "test");
EXPECT_TRUE(CheckInvalidationInfo(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();
StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */);
DisableInvalidationService();
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 */);
EXPECT_EQ(1, GetCount(METRIC_POLICY_REFRESH_CHANGED));
EXPECT_EQ(2, GetCount(METRIC_POLICY_REFRESH_CHANGED_NO_INVALIDATIONS));
EXPECT_EQ(3, 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, RefreshMetricsStoreSameTimestamp) {
// Store loads with the same timestamp as the load which causes registration
// are not counted.
StartInvalidator();
StorePolicy(
POLICY_OBJECT_A, 0, false /* policy_changed */, 12 /* timestamp */);
StorePolicy(
POLICY_OBJECT_A, 0, false /* policy_changed */, 12 /* timestamp */);
StorePolicy(
POLICY_OBJECT_A, 0, true /* policy_changed */, 12 /* timestamp */);
// The next load with a different timestamp counts.
StorePolicy(
POLICY_OBJECT_A, 0, true /* policy_changed */, 13 /* timestamp */);
EXPECT_EQ(1, 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, 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);
FireInvalidation(POLICY_OBJECT_A, 5, "test");
StorePolicy(POLICY_OBJECT_A, 0, false /* policy_changed */);
StorePolicy(POLICY_OBJECT_A, 0, true /* policy_changed */);
StorePolicy(POLICY_OBJECT_A, 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, InvalidationMetrics) {
// Generate a mix of versioned and unknown-version invalidations.
StorePolicy(POLICY_OBJECT_A);
StartInvalidator();
EXPECT_TRUE(IsUnsent(FireUnknownVersionInvalidation(POLICY_OBJECT_B)));
FireUnknownVersionInvalidation(POLICY_OBJECT_A);
EXPECT_TRUE(IsUnsent(FireInvalidation(POLICY_OBJECT_B, 1, "test")));
FireInvalidation(POLICY_OBJECT_A, 1, "test");
FireInvalidation(POLICY_OBJECT_A, 2, "test");
FireUnknownVersionInvalidation(POLICY_OBJECT_A);
FireUnknownVersionInvalidation(POLICY_OBJECT_A);
FireInvalidation(POLICY_OBJECT_A, 3, "test");
FireInvalidation(POLICY_OBJECT_A, 4, "test");
// Verify that received invalidations metrics are correct.
EXPECT_EQ(3, GetInvalidationCount(false /* with_payload */));
EXPECT_EQ(4, GetInvalidationCount(true /* with_payload */));
}
} // namespace policy