blob: de089d4523355f0e5a9572855f79c40ae69c3edb [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/storage/policy_value_store.h"
#include <memory>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_types.h"
#include "content/public/test/test_browser_thread.h"
#include "extensions/browser/api/storage/settings_observer.h"
#include "extensions/browser/value_store/leveldb_value_store.h"
#include "extensions/browser/value_store/value_store_unittest.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::Mock;
namespace extensions {
namespace {
const char kTestExtensionId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const char kDatabaseUMAClientName[] = "Test";
class MockSettingsObserver : public SettingsObserver {
public:
MOCK_METHOD3(OnSettingsChanged, void(
const std::string& extension_id,
settings_namespace::Namespace settings_namespace,
const std::string& changes_json));
};
// Extends PolicyValueStore by overriding the mutating methods, so that the
// Get() base implementation can be tested with the ValueStoreTest parameterized
// tests.
class MutablePolicyValueStore : public PolicyValueStore {
public:
explicit MutablePolicyValueStore(const base::FilePath& path)
: PolicyValueStore(
kTestExtensionId,
make_scoped_refptr(new SettingsObserverList()),
base::WrapUnique(
new LeveldbValueStore(kDatabaseUMAClientName, path))) {}
~MutablePolicyValueStore() override {}
WriteResult Set(WriteOptions options,
const std::string& key,
const base::Value& value) override {
return delegate()->Set(options, key, value);
}
WriteResult Set(WriteOptions options,
const base::DictionaryValue& values) override {
return delegate()->Set(options, values);
}
WriteResult Remove(const std::string& key) override {
return delegate()->Remove(key);
}
WriteResult Remove(const std::vector<std::string>& keys) override {
return delegate()->Remove(keys);
}
WriteResult Clear() override { return delegate()->Clear(); }
private:
DISALLOW_COPY_AND_ASSIGN(MutablePolicyValueStore);
};
ValueStore* Param(const base::FilePath& file_path) {
return new MutablePolicyValueStore(file_path);
}
} // namespace
INSTANTIATE_TEST_CASE_P(
PolicyValueStoreTest,
ValueStoreTest,
testing::Values(&Param));
class PolicyValueStoreTest : public testing::Test {
public:
PolicyValueStoreTest()
: file_thread_(content::BrowserThread::FILE, &loop_) {}
~PolicyValueStoreTest() override {}
void SetUp() override {
ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
observers_ = new SettingsObserverList();
observers_->AddObserver(&observer_);
store_.reset(new PolicyValueStore(
kTestExtensionId, observers_,
base::WrapUnique(new LeveldbValueStore(kDatabaseUMAClientName,
scoped_temp_dir_.path()))));
}
void TearDown() override {
observers_->RemoveObserver(&observer_);
store_.reset();
}
protected:
base::ScopedTempDir scoped_temp_dir_;
base::MessageLoop loop_;
content::TestBrowserThread file_thread_;
std::unique_ptr<PolicyValueStore> store_;
MockSettingsObserver observer_;
scoped_refptr<SettingsObserverList> observers_;
};
TEST_F(PolicyValueStoreTest, DontProvideRecommendedPolicies) {
policy::PolicyMap policies;
base::FundamentalValue expected(123);
policies.Set("must", policy::POLICY_LEVEL_MANDATORY,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
expected.DeepCopy(), nullptr);
policies.Set("may", policy::POLICY_LEVEL_RECOMMENDED,
policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
new base::FundamentalValue(456), NULL);
store_->SetCurrentPolicy(policies);
ValueStore::ReadResult result = store_->Get();
ASSERT_TRUE(result->status().ok());
EXPECT_EQ(1u, result->settings().size());
base::Value* value = NULL;
EXPECT_FALSE(result->settings().Get("may", &value));
EXPECT_TRUE(result->settings().Get("must", &value));
EXPECT_TRUE(base::Value::Equals(&expected, value));
}
TEST_F(PolicyValueStoreTest, ReadOnly) {
ValueStore::WriteOptions options = ValueStore::DEFAULTS;
base::StringValue string_value("value");
EXPECT_FALSE(store_->Set(options, "key", string_value)->status().ok());
base::DictionaryValue dict;
dict.SetString("key", "value");
EXPECT_FALSE(store_->Set(options, dict)->status().ok());
EXPECT_FALSE(store_->Remove("key")->status().ok());
std::vector<std::string> keys;
keys.push_back("key");
EXPECT_FALSE(store_->Remove(keys)->status().ok());
EXPECT_FALSE(store_->Clear()->status().ok());
}
TEST_F(PolicyValueStoreTest, NotifyOnChanges) {
// Notify when setting the initial policy.
const base::StringValue value("111");
{
ValueStoreChangeList changes;
changes.push_back(ValueStoreChange("aaa", nullptr, value.CreateDeepCopy()));
EXPECT_CALL(observer_,
OnSettingsChanged(kTestExtensionId,
settings_namespace::MANAGED,
ValueStoreChange::ToJson(changes)));
}
policy::PolicyMap policies;
policies.Set("aaa", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_CLOUD, value.DeepCopy(), nullptr);
store_->SetCurrentPolicy(policies);
loop_.RunUntilIdle();
Mock::VerifyAndClearExpectations(&observer_);
// Notify when new policies are added.
{
ValueStoreChangeList changes;
changes.push_back(ValueStoreChange("bbb", nullptr, value.CreateDeepCopy()));
EXPECT_CALL(observer_,
OnSettingsChanged(kTestExtensionId,
settings_namespace::MANAGED,
ValueStoreChange::ToJson(changes)));
}
policies.Set("bbb", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_CLOUD, value.DeepCopy(), nullptr);
store_->SetCurrentPolicy(policies);
loop_.RunUntilIdle();
Mock::VerifyAndClearExpectations(&observer_);
// Notify when policies change.
const base::StringValue new_value("222");
{
ValueStoreChangeList changes;
changes.push_back(ValueStoreChange("bbb", value.CreateDeepCopy(),
new_value.CreateDeepCopy()));
EXPECT_CALL(observer_,
OnSettingsChanged(kTestExtensionId,
settings_namespace::MANAGED,
ValueStoreChange::ToJson(changes)));
}
policies.Set("bbb", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
policy::POLICY_SOURCE_CLOUD, new_value.DeepCopy(), nullptr);
store_->SetCurrentPolicy(policies);
loop_.RunUntilIdle();
Mock::VerifyAndClearExpectations(&observer_);
// Notify when policies are removed.
{
ValueStoreChangeList changes;
changes.push_back(
ValueStoreChange("bbb", new_value.CreateDeepCopy(), nullptr));
EXPECT_CALL(observer_,
OnSettingsChanged(kTestExtensionId,
settings_namespace::MANAGED,
ValueStoreChange::ToJson(changes)));
}
policies.Erase("bbb");
store_->SetCurrentPolicy(policies);
loop_.RunUntilIdle();
Mock::VerifyAndClearExpectations(&observer_);
// Don't notify when there aren't any changes.
EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0);
store_->SetCurrentPolicy(policies);
loop_.RunUntilIdle();
Mock::VerifyAndClearExpectations(&observer_);
}
} // namespace extensions