text: // 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 "base/callback.h"
  #include "base/files/file_path.h"
  #include "base/files/scoped_temp_dir.h"
  #include "base/memory/ref_counted.h"
  #include "base/memory/scoped_ptr.h"
  #include "base/message_loop/message_loop.h"
  #include "chrome/browser/extensions/api/storage/settings_observer.h"
  #include "chrome/browser/policy/external_data_fetcher.h"
  #include "chrome/browser/policy/policy_map.h"
  #include "chrome/browser/value_store/leveldb_value_store.h"
  #include "chrome/browser/value_store/value_store_unittest.h"
  #include "content/public/test/test_browser_thread.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";
  
  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()),
                           scoped_ptr<ValueStore>(new LeveldbValueStore(path))) {}
    virtual ~MutablePolicyValueStore() {}
  
    virtual WriteResult Set(
        WriteOptions options,
        const std::string& key,
        const base::Value& value) OVERRIDE {
      return delegate()->Set(options, key, value);
    }
  
    virtual WriteResult Set(
        WriteOptions options, const base::DictionaryValue& values) OVERRIDE {
      return delegate()->Set(options, values);
    }
  
    virtual WriteResult Remove(const std::string& key) OVERRIDE {
      return delegate()->Remove(key);
    }
  
    virtual WriteResult Remove(const std::vector<std::string>& keys) OVERRIDE {
      return delegate()->Remove(keys);
    }
  
    virtual 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_) {}
    virtual ~PolicyValueStoreTest() {}
  
    virtual void SetUp() OVERRIDE {
      ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
      observers_ = new SettingsObserverList();
      observers_->AddObserver(&observer_);
      store_.reset(new PolicyValueStore(
          kTestExtensionId,
          observers_,
          scoped_ptr<ValueStore>(
              new LeveldbValueStore(scoped_temp_dir_.path()))));
    }
  
    virtual void TearDown() OVERRIDE {
      observers_->RemoveObserver(&observer_);
      store_.reset();
    }
  
   protected:
    base::ScopedTempDir scoped_temp_dir_;
    base::MessageLoop loop_;
    content::TestBrowserThread file_thread_;
    scoped_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, expected.DeepCopy(), NULL);
    policies.Set("may", policy::POLICY_LEVEL_RECOMMENDED,
                 policy::POLICY_SCOPE_USER,
                 new base::FundamentalValue(456), NULL);
    store_->SetCurrentPolicy(policies, false);
    ValueStore::ReadResult result = store_->Get();
    ASSERT_FALSE(result->HasError());
    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_TRUE(store_->Set(options, "key", string_value)->HasError());
  
    base::DictionaryValue dict;
    dict.SetString("key", "value");
    EXPECT_TRUE(store_->Set(options, dict)->HasError());
  
    EXPECT_TRUE(store_->Remove("key")->HasError());
    std::vector<std::string> keys;
    keys.push_back("key");
    EXPECT_TRUE(store_->Remove(keys)->HasError());
    EXPECT_TRUE(store_->Clear()->HasError());
  }
  
  TEST_F(PolicyValueStoreTest, NotifyOnChanges) {
    policy::PolicyMap policies;
    policies.Set("aaa", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
                 new base::StringValue("111"), NULL);
    EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0);
    // No notification when setting the initial policy.
    store_->SetCurrentPolicy(policies, false);
    loop_.RunUntilIdle();
    Mock::VerifyAndClearExpectations(&observer_);
  
    // And no notifications on changes when not asked for.
    policies.Set("aaa", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
                 new base::StringValue("222"), NULL);
    policies.Set("bbb", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
                 new base::StringValue("223"), NULL);
    EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0);
    store_->SetCurrentPolicy(policies, false);
    loop_.RunUntilIdle();
    Mock::VerifyAndClearExpectations(&observer_);
  
    // Notify when new policies are added.
    ValueStoreChangeList changes;
    base::StringValue value("333");
    changes.push_back(ValueStoreChange("ccc", NULL, value.DeepCopy()));
    policies.Set("ccc", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
                 value.DeepCopy(), NULL);
    EXPECT_CALL(observer_, OnSettingsChanged(kTestExtensionId,
                                             settings_namespace::MANAGED,
                                             ValueStoreChange::ToJson(changes)));
    store_->SetCurrentPolicy(policies, true);
    loop_.RunUntilIdle();
    Mock::VerifyAndClearExpectations(&observer_);
  
    // Notify when policies change.
    changes.clear();
    base::StringValue new_value("444");
    changes.push_back(
        ValueStoreChange("ccc", value.DeepCopy(), new_value.DeepCopy()));
    policies.Set("ccc", policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
                 new_value.DeepCopy(), NULL);
    EXPECT_CALL(observer_, OnSettingsChanged(kTestExtensionId,
                                             settings_namespace::MANAGED,
                                             ValueStoreChange::ToJson(changes)));
    store_->SetCurrentPolicy(policies, true);
    loop_.RunUntilIdle();
    Mock::VerifyAndClearExpectations(&observer_);
  
    // Notify when policies are removed.
    changes.clear();
    changes.push_back(ValueStoreChange("ccc", new_value.DeepCopy(), NULL));
    policies.Erase("ccc");
    EXPECT_CALL(observer_, OnSettingsChanged(kTestExtensionId,
                                             settings_namespace::MANAGED,
                                             ValueStoreChange::ToJson(changes)));
    store_->SetCurrentPolicy(policies, true);
    loop_.RunUntilIdle();
    Mock::VerifyAndClearExpectations(&observer_);
  
    // Don't notify when there aren't changes.
    EXPECT_CALL(observer_, OnSettingsChanged(_, _, _)).Times(0);
    store_->SetCurrentPolicy(policies, true);
    loop_.RunUntilIdle();
    Mock::VerifyAndClearExpectations(&observer_);
  }
  
  }  // namespace extensions
  