// Copyright (c) 2011 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 "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_pref_value_map.h"
#include "chrome/common/pref_store_observer_mock.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {
const char kExt1[] = "ext1";
const char kExt2[] = "ext2";
const char kExt3[] = "ext3";

const char kPref1[] = "path1.subpath";
const char kPref2[] = "path2";
const char kPref3[] = "path3";
const char kPref4[] = "path4";
}  // namespace

static Value* CreateVal(const char* str) {
  return Value::CreateStringValue(str);
}

static base::Time CreateTime(int64 t) {
  return base::Time::FromInternalValue(t);
}

template <typename BASECLASS>
class ExtensionPrefValueMapTestBase : public BASECLASS {
 public:
  // Returns an empty string if the key is not set.
  std::string GetValue(const char * key, bool incognito) const {
    const Value *value = epvm_.GetEffectivePrefValue(key, incognito, NULL);
    std::string string_value = "";
    if (value)
      value->GetAsString(&string_value);
    return string_value;
  }

 protected:
  ExtensionPrefValueMap epvm_;
};

class ExtensionPrefValueMapTest
    : public ExtensionPrefValueMapTestBase<testing::Test> {
};

// A gmock-ified implementation of PrefStore::Observer.
class ExtensionPrefValueMapObserverMock
    : public ExtensionPrefValueMap::Observer {
 public:
  ExtensionPrefValueMapObserverMock() {}
  virtual ~ExtensionPrefValueMapObserverMock() {}

  MOCK_METHOD1(OnPrefValueChanged, void(const std::string&));
  MOCK_METHOD0(OnInitializationCompleted, void());
  MOCK_METHOD0(OnExtensionPrefValueMapDestruction, void());

 private:
  DISALLOW_COPY_AND_ASSIGN(ExtensionPrefValueMapObserverMock);
};

TEST_F(ExtensionPrefValueMapTest, SetAndGetPrefValue) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  EXPECT_EQ("val1", GetValue(kPref1, false));
};

TEST_F(ExtensionPrefValueMapTest, GetNotSetPrefValue) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  EXPECT_EQ("", GetValue(kPref1, false));
};

// Make sure the last-installed extension wins for each preference.
TEST_F(ExtensionPrefValueMapTest, Override) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.RegisterExtension(kExt2, CreateTime(20), true);
  epvm_.RegisterExtension(kExt3, CreateTime(30), true);

  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  epvm_.SetExtensionPref(kExt2, kPref1, false, CreateVal("val2"));
  epvm_.SetExtensionPref(kExt3, kPref1, false, CreateVal("val3"));

  epvm_.SetExtensionPref(kExt1, kPref2, false, CreateVal("val4"));
  epvm_.SetExtensionPref(kExt2, kPref2, false, CreateVal("val5"));

  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val6"));
  epvm_.SetExtensionPref(kExt1, kPref2, false, CreateVal("val7"));
  epvm_.SetExtensionPref(kExt1, kPref3, false, CreateVal("val8"));

  EXPECT_EQ("val3", GetValue(kPref1, false));
  EXPECT_EQ("val5", GetValue(kPref2, false));
  EXPECT_EQ("val8", GetValue(kPref3, false));
}

TEST_F(ExtensionPrefValueMapTest, OverrideChecks) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.RegisterExtension(kExt2, CreateTime(20), true);
  epvm_.RegisterExtension(kExt3, CreateTime(30), true);

  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, false));
  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt2, kPref1, false));
  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, false));
  EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt1, kPref1, false));
  EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt2, kPref1, false));
  EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt3, kPref1, false));

  epvm_.SetExtensionPref(kExt2, kPref1, false, CreateVal("val1"));

  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt1, kPref1, false));
  EXPECT_TRUE(epvm_.DoesExtensionControlPref(kExt2, kPref1, false));
  EXPECT_FALSE(epvm_.DoesExtensionControlPref(kExt3, kPref1, false));
  EXPECT_FALSE(epvm_.CanExtensionControlPref(kExt1, kPref1, false));
  EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt2, kPref1, false));
  EXPECT_TRUE(epvm_.CanExtensionControlPref(kExt3, kPref1, false));
}

TEST_F(ExtensionPrefValueMapTest, SetAndGetPrefValueIncognito) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  EXPECT_EQ("val1", GetValue(kPref1, true));
}

TEST_F(ExtensionPrefValueMapTest, UninstallOnlyExtension) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  epvm_.UnregisterExtension(kExt1);

  EXPECT_EQ("", GetValue(kPref1, false));
}

// Tests uninstalling an extension that wasn't winning for any preferences.
TEST_F(ExtensionPrefValueMapTest, UninstallIrrelevantExtension) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.RegisterExtension(kExt2, CreateTime(10), true);

  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  epvm_.SetExtensionPref(kExt2, kPref1, false, CreateVal("val2"));

  epvm_.SetExtensionPref(kExt1, kPref2, false, CreateVal("val3"));
  epvm_.SetExtensionPref(kExt2, kPref2, false, CreateVal("val4"));

  epvm_.UnregisterExtension(kExt1);

  EXPECT_EQ("val2", GetValue(kPref1, false));
  EXPECT_EQ("val4", GetValue(kPref2, false));
}

// Tests uninstalling an extension that was winning for all preferences.
TEST_F(ExtensionPrefValueMapTest, UninstallExtensionFromTop) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.RegisterExtension(kExt2, CreateTime(20), true);
  epvm_.RegisterExtension(kExt3, CreateTime(30), true);

  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  epvm_.SetExtensionPref(kExt2, kPref1, false, CreateVal("val2"));
  epvm_.SetExtensionPref(kExt3, kPref1, false, CreateVal("val3"));

  epvm_.SetExtensionPref(kExt1, kPref2, false, CreateVal("val4"));
  epvm_.SetExtensionPref(kExt3, kPref2, false, CreateVal("val5"));

  epvm_.UnregisterExtension(kExt3);

  EXPECT_EQ("val2", GetValue(kPref1, false));
  EXPECT_EQ("val4", GetValue(kPref2, false));
}

// Tests uninstalling an extension that was winning for only some preferences.
TEST_F(ExtensionPrefValueMapTest, UninstallExtensionFromMiddle) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.RegisterExtension(kExt2, CreateTime(20), true);
  epvm_.RegisterExtension(kExt3, CreateTime(30), true);

  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  epvm_.SetExtensionPref(kExt2, kPref1, false, CreateVal("val2"));
  epvm_.SetExtensionPref(kExt3, kPref1, false, CreateVal("val3"));

  epvm_.SetExtensionPref(kExt1, kPref2, false, CreateVal("val4"));
  epvm_.SetExtensionPref(kExt2, kPref2, false, CreateVal("val5"));

  epvm_.SetExtensionPref(kExt1, kPref3, false, CreateVal("val6"));

  epvm_.SetExtensionPref(kExt2, kPref4, false, CreateVal("val7"));

  epvm_.UnregisterExtension(kExt2);

  EXPECT_EQ("val3", GetValue(kPref1, false));
  EXPECT_EQ("val4", GetValue(kPref2, false));
  EXPECT_EQ("val6", GetValue(kPref3, false));
  EXPECT_EQ("", GetValue(kPref4, false));
}

// Tests triggering of notifications to registered observers.
TEST_F(ExtensionPrefValueMapTest, NotifyWhenNeeded) {
  using testing::_;
  using testing::Mock;
  using testing::StrEq;

  epvm_.RegisterExtension(kExt1, CreateTime(10), true);

  ExtensionPrefValueMapObserverMock observer;
  epvm_.AddObserver(&observer);

  EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1)));
  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  Mock::VerifyAndClearExpectations(&observer);

  // Write the same value again.
  EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))).Times(0);
  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  Mock::VerifyAndClearExpectations(&observer);

  // Override incognito value.
  EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1)));
  epvm_.SetExtensionPref(kExt1, kPref1, true, CreateVal("val2"));
  Mock::VerifyAndClearExpectations(&observer);

  // Override non-incognito value.
  EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1)));
  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val3"));
  Mock::VerifyAndClearExpectations(&observer);

  // Disable.
  EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1)));
  epvm_.SetExtensionState(kExt1, false);
  Mock::VerifyAndClearExpectations(&observer);

  // Enable.
  EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1)));
  epvm_.SetExtensionState(kExt1, true);
  Mock::VerifyAndClearExpectations(&observer);

  // Uninstall
  EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1)));
  epvm_.UnregisterExtension(kExt1);
  Mock::VerifyAndClearExpectations(&observer);

  epvm_.RemoveObserver(&observer);

  // Write new value --> no notification after removing observer.
  EXPECT_CALL(observer, OnPrefValueChanged(std::string(kPref1))).Times(0);
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val4"));
  Mock::VerifyAndClearExpectations(&observer);
}

// Tests disabling an extension.
TEST_F(ExtensionPrefValueMapTest, DisableExt) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);

  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  epvm_.SetExtensionState(kExt1, false);
  EXPECT_EQ("", GetValue(kPref1, false));
}

// Tests disabling and reenabling an extension.
TEST_F(ExtensionPrefValueMapTest, ReenableExt) {
  epvm_.RegisterExtension(kExt1, CreateTime(10), true);

  epvm_.SetExtensionPref(kExt1, kPref1, false, CreateVal("val1"));
  epvm_.SetExtensionState(kExt1, false);
  epvm_.SetExtensionState(kExt1, true);
  EXPECT_EQ("val1", GetValue(kPref1, false));
}

struct OverrideIncognitoTestCase {
  OverrideIncognitoTestCase(int val_ext1_regular,
                            int val_ext1_incognito,
                            int val_ext2_regular,
                            int val_ext2_incognito,
                            int effective_value_regular,
                            int effective_value_incognito)
      : val_ext1_regular_(val_ext1_regular),
        val_ext1_incognito_(val_ext1_incognito),
        val_ext2_regular_(val_ext2_regular),
        val_ext2_incognito_(val_ext2_incognito),
        effective_value_regular_(effective_value_regular),
        effective_value_incognito_(effective_value_incognito) {}

  int val_ext1_regular_;           // pref value of extension 1
  int val_ext1_incognito_;         // pref value of extension 1 incognito
  int val_ext2_regular_;           // pref value of extension 2
  int val_ext2_incognito_;         // pref value of extension 2 incognito
  int effective_value_regular_;    // desired winner regular
  int effective_value_incognito_;  // desired winner incognito
};

class ExtensionPrefValueMapTestIncognitoTests
    : public ExtensionPrefValueMapTestBase<
          testing::TestWithParam<OverrideIncognitoTestCase> > {
};

TEST_P(ExtensionPrefValueMapTestIncognitoTests, OverrideIncognito) {
  OverrideIncognitoTestCase test = GetParam();
  const char* strings[] = {
      "undefined",
      "val1",
      "val2",
      "val3",
      "val4"
  };

  epvm_.RegisterExtension(kExt1, CreateTime(10), true);
  epvm_.RegisterExtension(kExt2, CreateTime(20), true);
  if (test.val_ext1_regular_) {
    epvm_.SetExtensionPref(kExt1, kPref1, false,
                           CreateVal(strings[test.val_ext1_regular_]));
  }
  if (test.val_ext1_incognito_) {
    epvm_.SetExtensionPref(kExt1, kPref1, true,
                           CreateVal(strings[test.val_ext1_incognito_]));
  }
  if (test.val_ext2_regular_) {
    epvm_.SetExtensionPref(kExt2, kPref1, false,
                           CreateVal(strings[test.val_ext2_regular_]));
  }
  if (test.val_ext2_incognito_) {
    epvm_.SetExtensionPref(kExt2, kPref1, true,
                           CreateVal(strings[test.val_ext2_incognito_]));
  }
  std::string actual;
  EXPECT_EQ(strings[test.effective_value_regular_], GetValue(kPref1, false));
  EXPECT_EQ(strings[test.effective_value_incognito_], GetValue(kPref1, true));
  epvm_.UnregisterExtension(kExt1);
  epvm_.UnregisterExtension(kExt2);
}

INSTANTIATE_TEST_CASE_P(
    ExtensionPrefValueMapTestIncognitoTestsInstance,
    ExtensionPrefValueMapTestIncognitoTests,
    testing::Values(
        // e.g. (1, 0, 0, 4,  1, 4), means:
        // ext1 regular is set to "val1", ext2 incognito is set to "val4"
        // --> the winning regular value is "val1", the winning incognito
        //     value is "val4".
        OverrideIncognitoTestCase(1, 0, 0, 0,  1, 1),
        OverrideIncognitoTestCase(1, 2, 0, 0,  1, 2),
        OverrideIncognitoTestCase(1, 0, 3, 0,  3, 3),
        OverrideIncognitoTestCase(1, 0, 0, 4,  1, 4),
        // The last 3 in the following line is intentional!
        OverrideIncognitoTestCase(1, 2, 3, 0,  3, 3),
        OverrideIncognitoTestCase(1, 2, 0, 4,  1, 4),
        OverrideIncognitoTestCase(1, 2, 3, 4,  3, 4)));