Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / prefs / pref_hash_store_impl_unittest.cc
index 8232691..67b4148 100644 (file)
 #include "base/prefs/scoped_user_pref_update.h"
 #include "base/prefs/testing_pref_service.h"
 #include "base/values.h"
-#include "chrome/browser/prefs/pref_hash_store.h"
+#include "chrome/browser/prefs/pref_hash_store_impl.h"
+#include "chrome/browser/prefs/pref_hash_store_transaction.h"
 #include "chrome/common/pref_names.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-TEST(PrefHashStoreImplTest, TestCase) {
+class PrefHashStoreImplTest : public testing::Test {
+ public:
+  virtual void SetUp() OVERRIDE {
+    PrefHashStoreImpl::RegisterPrefs(local_state_.registry());
+  }
+
+ protected:
+  TestingPrefServiceSimple local_state_;
+};
+
+TEST_F(PrefHashStoreImplTest, AtomicHashStoreAndCheck) {
   base::StringValue string_1("string1");
   base::StringValue string_2("string2");
 
-  TestingPrefServiceSimple local_state;
-  PrefHashStoreImpl::RegisterPrefs(local_state.registry());
-
-  // 32 NULL bytes is the seed that was used to generate the legacy hash.
-  PrefHashStoreImpl pref_hash_store(
-      "store_id", std::string(32,0), "device_id", &local_state);
-
-  // Only NULL should be trusted in the absence of a hash.
-  EXPECT_EQ(PrefHashStore::UNTRUSTED_UNKNOWN_VALUE,
-            pref_hash_store.CheckValue("path1", &string_1));
-  EXPECT_EQ(PrefHashStore::TRUSTED_UNKNOWN_VALUE,
-            pref_hash_store.CheckValue("path1", NULL));
-
-  pref_hash_store.StoreHash("path1", &string_1);
-  EXPECT_EQ(PrefHashStore::UNCHANGED,
-            pref_hash_store.CheckValue("path1", &string_1));
-  EXPECT_EQ(PrefHashStore::CLEARED, pref_hash_store.CheckValue("path1", NULL));
-  pref_hash_store.StoreHash("path1", NULL);
-  EXPECT_EQ(PrefHashStore::UNCHANGED,
-            pref_hash_store.CheckValue("path1", NULL));
-  EXPECT_EQ(PrefHashStore::CHANGED,
-            pref_hash_store.CheckValue("path1", &string_2));
+  {
+    // 32 NULL bytes is the seed that was used to generate the legacy hash.
+    PrefHashStoreImpl pref_hash_store(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store.BeginTransaction());
+
+    // Only NULL should be trusted in the absence of a hash.
+    EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE,
+              transaction->CheckValue("path1", &string_1));
+    EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE,
+              transaction->CheckValue("path1", NULL));
+
+    transaction->StoreHash("path1", &string_1);
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckValue("path1", &string_1));
+    EXPECT_EQ(PrefHashStoreTransaction::CLEARED,
+              transaction->CheckValue("path1", NULL));
+    transaction->StoreHash("path1", NULL);
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckValue("path1", NULL));
+    EXPECT_EQ(PrefHashStoreTransaction::CHANGED,
+              transaction->CheckValue("path1", &string_2));
+
+    base::DictionaryValue dict;
+    dict.Set("a", new base::StringValue("foo"));
+    dict.Set("d", new base::StringValue("bad"));
+    dict.Set("b", new base::StringValue("bar"));
+    dict.Set("c", new base::StringValue("baz"));
+
+    {
+      // Manually shove in a legacy hash.
+      DictionaryPrefUpdate update(&local_state_,
+                                  prefs::kProfilePreferenceHashes);
+      base::DictionaryValue* child_dictionary = NULL;
+      ASSERT_TRUE(update->GetDictionary("store_id", &child_dictionary));
+      child_dictionary->SetString(
+          "path1",
+          "C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2");
+    }
+    EXPECT_EQ(PrefHashStoreTransaction::MIGRATED,
+              transaction->CheckValue("path1", &dict));
+    transaction->StoreHash("path1", &dict);
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckValue("path1", &dict));
+  }
+
+  {
+    // |pref_hash_store2| should trust its initial hashes dictionary and thus
+    // trust new unknown values.
+    PrefHashStoreImpl pref_hash_store2(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store2.BeginTransaction());
+    EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE,
+              transaction->CheckValue("new_path", &string_1));
+    EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE,
+              transaction->CheckValue("new_path", &string_2));
+    EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE,
+              transaction->CheckValue("new_path", NULL));
+  }
+
+  {
+    // Manually corrupt the hash of hashes for "store_id".
+    DictionaryPrefUpdate update(&local_state_, prefs::kProfilePreferenceHashes);
+    base::DictionaryValue* hash_of_hashes_dict = NULL;
+    ASSERT_TRUE(update->GetDictionaryWithoutPathExpansion(
+        internals::kHashOfHashesDict, &hash_of_hashes_dict));
+    hash_of_hashes_dict->SetString("store_id", std::string(64, 'A'));
+    // This shouldn't have increased the number of existing hash of hashes.
+    ASSERT_EQ(1U, hash_of_hashes_dict->size());
+  }
+
+  {
+    // |pref_hash_store3| should no longer trust its initial hashes dictionary
+    // and thus shouldn't trust non-NULL unknown values.
+    PrefHashStoreImpl pref_hash_store3(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store3.BeginTransaction());
+    EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE,
+              transaction->CheckValue("new_path", &string_1));
+    EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE,
+              transaction->CheckValue("new_path", &string_2));
+    EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE,
+              transaction->CheckValue("new_path", NULL));
+  }
+}
 
+TEST_F(PrefHashStoreImplTest, SplitHashStoreAndCheck) {
   base::DictionaryValue dict;
-  dict.Set("a", new base::StringValue("foo"));
-  dict.Set("d", new base::StringValue("bad"));
-  dict.Set("b", new base::StringValue("bar"));
-  dict.Set("c", new base::StringValue("baz"));
+  dict.Set("a", new base::StringValue("to be replaced"));
+  dict.Set("b", new base::StringValue("same"));
+  dict.Set("o", new base::StringValue("old"));
+
+  base::DictionaryValue modified_dict;
+  modified_dict.Set("a", new base::StringValue("replaced"));
+  modified_dict.Set("b", new base::StringValue("same"));
+  modified_dict.Set("c", new base::StringValue("new"));
+
+  base::DictionaryValue empty_dict;
+
+  std::vector<std::string> invalid_keys;
+
+  {
+    PrefHashStoreImpl pref_hash_store(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store.BeginTransaction());
+
+    // No hashes stored yet and hashes dictionary is empty (and thus not
+    // trusted).
+    EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE,
+              transaction->CheckSplitValue("path1", &dict, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+
+    transaction->StoreSplitHash("path1", &dict);
+
+    // Verify match post storage.
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckSplitValue("path1", &dict, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+
+    // Verify new path is still unknown.
+    EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE,
+              transaction->CheckSplitValue("path2", &dict, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+
+    // Verify NULL or empty dicts are declared as having been cleared.
+    EXPECT_EQ(PrefHashStoreTransaction::CLEARED,
+              transaction->CheckSplitValue("path1", NULL, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+    EXPECT_EQ(PrefHashStoreTransaction::CLEARED,
+              transaction->CheckSplitValue("path1", &empty_dict,
+                                              &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+
+    // Verify changes are properly detected.
+    EXPECT_EQ(PrefHashStoreTransaction::CHANGED,
+              transaction->CheckSplitValue("path1", &modified_dict,
+                                              &invalid_keys));
+    std::vector<std::string> expected_invalid_keys1;
+    expected_invalid_keys1.push_back("a");
+    expected_invalid_keys1.push_back("c");
+    EXPECT_EQ(expected_invalid_keys1, invalid_keys);
+    invalid_keys.clear();
+
+    // Verify |dict| still matches post check.
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckSplitValue("path1", &dict, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+
+    // Store hash for |modified_dict|.
+    transaction->StoreSplitHash("path1", &modified_dict);
+
+    // Verify |modified_dict| is now the one that verifies correctly.
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckSplitValue("path1", &modified_dict,
+                                              &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+
+    // Verify old dict no longer matches.
+    EXPECT_EQ(PrefHashStoreTransaction::CHANGED,
+              transaction->CheckSplitValue("path1", &dict, &invalid_keys));
+    std::vector<std::string> expected_invalid_keys2;
+    expected_invalid_keys2.push_back("a");
+    expected_invalid_keys2.push_back("o");
+    EXPECT_EQ(expected_invalid_keys2, invalid_keys);
+    invalid_keys.clear();
+  }
 
   {
-    // Manually shove in a legacy hash.
-    DictionaryPrefUpdate update(&local_state, prefs::kProfilePreferenceHashes);
-    base::DictionaryValue* child_dictionary = NULL;
-    ASSERT_TRUE(update->GetDictionary("store_id", &child_dictionary));
-    child_dictionary->SetString(
-        "path1",
-        "C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2");
-  }
-  EXPECT_EQ(PrefHashStore::MIGRATED,
-            pref_hash_store.CheckValue("path1", &dict));
-  pref_hash_store.StoreHash("path1", &dict);
-  EXPECT_EQ(PrefHashStore::UNCHANGED,
-            pref_hash_store.CheckValue("path1", &dict));
-
-  // |pref_hash_store2| should trust its initial hashes dictionary and thus
-  // trust new unknown values.
-  PrefHashStoreImpl pref_hash_store2(
-      "store_id", std::string(32, 0), "device_id", &local_state);
-  EXPECT_EQ(PrefHashStore::TRUSTED_UNKNOWN_VALUE,
-            pref_hash_store2.CheckValue("new_path", &string_1));
-  EXPECT_EQ(PrefHashStore::TRUSTED_UNKNOWN_VALUE,
-            pref_hash_store2.CheckValue("new_path", &string_2));
-  EXPECT_EQ(PrefHashStore::TRUSTED_UNKNOWN_VALUE,
-            pref_hash_store2.CheckValue("new_path", NULL));
+    // |pref_hash_store2| should trust its initial hashes dictionary and thus
+    // trust new unknown values.
+    PrefHashStoreImpl pref_hash_store2(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store2.BeginTransaction());
+    EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE,
+              transaction->CheckSplitValue("new_path", &dict, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+  }
 
   {
     // Manually corrupt the hash of hashes for "store_id".
-    DictionaryPrefUpdate update(&local_state, prefs::kProfilePreferenceHashes);
+    DictionaryPrefUpdate update(&local_state_, prefs::kProfilePreferenceHashes);
     base::DictionaryValue* hash_of_hashes_dict = NULL;
     ASSERT_TRUE(update->GetDictionaryWithoutPathExpansion(
-        internals::kHashOfHashesPref, &hash_of_hashes_dict));
+        internals::kHashOfHashesDict, &hash_of_hashes_dict));
     hash_of_hashes_dict->SetString("store_id", std::string(64, 'A'));
     // This shouldn't have increased the number of existing hash of hashes.
     ASSERT_EQ(1U, hash_of_hashes_dict->size());
   }
 
-  // |pref_hash_store3| should no longer trust its initial hashes dictionary and
-  // thus shouldn't trust non-NULL unknown values.
-  PrefHashStoreImpl pref_hash_store3(
-      "store_id", std::string(32, 0), "device_id", &local_state);
-  EXPECT_EQ(PrefHashStore::UNTRUSTED_UNKNOWN_VALUE,
-            pref_hash_store3.CheckValue("new_path", &string_1));
-  EXPECT_EQ(PrefHashStore::UNTRUSTED_UNKNOWN_VALUE,
-            pref_hash_store3.CheckValue("new_path", &string_2));
-  EXPECT_EQ(PrefHashStore::TRUSTED_UNKNOWN_VALUE,
-            pref_hash_store3.CheckValue("new_path", NULL));
+  {
+    // |pref_hash_store3| should no longer trust its initial hashes dictionary
+    // and thus shouldn't trust unknown values.
+    PrefHashStoreImpl pref_hash_store3(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store3.BeginTransaction());
+    EXPECT_EQ(PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE,
+              transaction->CheckSplitValue("new_path", &dict, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+  }
+}
+
+TEST_F(PrefHashStoreImplTest, EmptyAndNULLSplitDict) {
+  base::DictionaryValue empty_dict;
+
+  std::vector<std::string> invalid_keys;
+
+  {
+    PrefHashStoreImpl pref_hash_store(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store.BeginTransaction());
+
+    // Store hashes for a random dict to be overwritten below.
+    base::DictionaryValue initial_dict;
+    initial_dict.Set("a", new base::StringValue("foo"));
+    transaction->StoreSplitHash("path1", &initial_dict);
+
+    // Verify stored empty dictionary matches NULL and empty dictionary back.
+    transaction->StoreSplitHash("path1", &empty_dict);
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckSplitValue("path1", NULL, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckSplitValue("path1", &empty_dict,
+                                              &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+
+    // Same when storing NULL directly.
+    transaction->StoreSplitHash("path1", NULL);
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckSplitValue("path1", NULL, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckSplitValue("path1", &empty_dict,
+                                              &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+  }
+
+  {
+    // |pref_hash_store2| should trust its initial hashes dictionary (and thus
+    // trust new unknown values) even though the last action done on local_state
+    // was to clear the hashes for path1 by setting its value to NULL (this is a
+    // regression test ensuring that the internal action of clearing some hashes
+    // does update the stored hash of hashes).
+    PrefHashStoreImpl pref_hash_store2(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store2.BeginTransaction());
+
+    base::DictionaryValue tested_dict;
+    tested_dict.Set("a", new base::StringValue("foo"));
+    tested_dict.Set("b", new base::StringValue("bar"));
+    EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE,
+              transaction->CheckSplitValue("new_path", &tested_dict,
+                                           &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+  }
+}
+
+// Test that the PrefHashStore returns TRUSTED_UNKNOWN_VALUE when checking for
+// a split preference even if there is an existing atomic preference's hash
+// stored. There is no point providing a migration path for preferences
+// switching strategies after their initial release as split preferences are
+// turned into split preferences specifically because the atomic hash isn't
+// considered useful.
+TEST_F(PrefHashStoreImplTest, TrustedUnknownSplitValueFromExistingAtomic) {
+  base::StringValue string("string1");
+
+  base::DictionaryValue dict;
+  dict.Set("a", new base::StringValue("foo"));
+  dict.Set("d", new base::StringValue("bad"));
+  dict.Set("b", new base::StringValue("bar"));
+  dict.Set("c", new base::StringValue("baz"));
+
+  {
+    PrefHashStoreImpl pref_hash_store(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store.BeginTransaction());
+
+    transaction->StoreHash("path1", &string);
+    EXPECT_EQ(PrefHashStoreTransaction::UNCHANGED,
+              transaction->CheckValue("path1", &string));
+  }
+
+  {
+    // Load a new |pref_hash_store2| in which the hashes dictionary is trusted.
+    PrefHashStoreImpl pref_hash_store2(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store2.BeginTransaction());
+    std::vector<std::string> invalid_keys;
+    EXPECT_EQ(PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE,
+              transaction->CheckSplitValue("path1", &dict, &invalid_keys));
+    EXPECT_TRUE(invalid_keys.empty());
+  }
+}
+
+TEST_F(PrefHashStoreImplTest, GetCurrentVersion) {
+  COMPILE_ASSERT(PrefHashStoreImpl::VERSION_LATEST == 2,
+                 new_versions_should_be_tested_here);
+  {
+    PrefHashStoreImpl pref_hash_store(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+
+    // VERSION_UNINITIALIZED when no hashes are stored.
+    EXPECT_EQ(PrefHashStoreImpl::VERSION_UNINITIALIZED,
+              pref_hash_store.GetCurrentVersion());
+
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store.BeginTransaction());
+    base::StringValue string_value("foo");
+    transaction->StoreHash("path1", &string_value);
+  }
+  {
+    PrefHashStoreImpl pref_hash_store(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+
+    // VERSION_LATEST after storing a hash.
+    EXPECT_EQ(PrefHashStoreImpl::VERSION_LATEST,
+              pref_hash_store.GetCurrentVersion());
+  }
+  {
+    // Manually clear the version number.
+    DictionaryPrefUpdate update(&local_state_, prefs::kProfilePreferenceHashes);
+    base::DictionaryValue* store_versions_dict = NULL;
+    ASSERT_TRUE(update->GetDictionaryWithoutPathExpansion(
+        internals::kStoreVersionsDict, &store_versions_dict));
+    scoped_ptr<base::Value> stored_value;
+    store_versions_dict->Remove("store_id", &stored_value);
+    int stored_version;
+    EXPECT_TRUE(stored_value->GetAsInteger(&stored_version));
+    EXPECT_EQ(PrefHashStoreImpl::VERSION_LATEST, stored_version);
+    // There should be no versions left in the versions dictionary.
+    EXPECT_TRUE(store_versions_dict->empty());
+  }
+  {
+    PrefHashStoreImpl pref_hash_store(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+
+    // VERSION_PRE_MIGRATION when no version is present for "store_id".
+    EXPECT_EQ(PrefHashStoreImpl::VERSION_PRE_MIGRATION,
+              pref_hash_store.GetCurrentVersion());
+
+    scoped_ptr<PrefHashStoreTransaction> transaction(
+        pref_hash_store.BeginTransaction());
+  }
+  {
+    PrefHashStoreImpl pref_hash_store(
+        "store_id", std::string(32, 0), "device_id", &local_state_);
+
+    // Back to VERSION_LATEST after performing a transaction from
+    // VERSION_PRE_MIGRATION (the presence of an existing hash should be
+    // sufficient, no need for the transaction itself to perform any work).
+    EXPECT_EQ(PrefHashStoreImpl::VERSION_LATEST,
+              pref_hash_store.GetCurrentVersion());
+  }
 }