Home | History | Annotate | Download | only in invalidation
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/invalidation/invalidator_storage.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "base/message_loop/message_loop_proxy.h"
     10 #include "base/prefs/pref_service.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/string_util.h"
     13 #include "chrome/common/pref_names.h"
     14 #include "chrome/test/base/testing_pref_service_syncable.h"
     15 #include "sync/internal_api/public/base/invalidation_test_util.h"
     16 #include "testing/gmock/include/gmock/gmock.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 using syncer::InvalidationStateMap;
     20 
     21 namespace {
     22 
     23 const char kSourceKey[] = "source";
     24 const char kNameKey[] = "name";
     25 const char kMaxVersionKey[] = "max-version";
     26 const char kPayloadKey[] = "payload";
     27 const char kCurrentAckHandleKey[] = "current-ack";
     28 const char kExpectedAckHandleKey[] = "expected-ack";
     29 
     30 const int kChromeSyncSourceId = 1004;
     31 
     32 void GenerateAckHandlesTestHelper(syncer::AckHandleMap* output,
     33                                   const syncer::AckHandleMap& input) {
     34   *output = input;
     35 }
     36 
     37 }  // namespace
     38 
     39 namespace invalidation {
     40 
     41 class InvalidatorStorageTest : public testing::Test {
     42  public:
     43   InvalidatorStorageTest()
     44       : kBookmarksId_(kChromeSyncSourceId, "BOOKMARK"),
     45         kPreferencesId_(kChromeSyncSourceId, "PREFERENCE"),
     46         kAppNotificationsId_(kChromeSyncSourceId, "APP_NOTIFICATION"),
     47         kAutofillId_(kChromeSyncSourceId, "AUTOFILL") {}
     48 
     49   virtual void SetUp() {
     50     InvalidatorStorage::RegisterProfilePrefs(pref_service_.registry());
     51   }
     52 
     53  protected:
     54   TestingPrefServiceSyncable pref_service_;
     55 
     56   const invalidation::ObjectId kBookmarksId_;
     57   const invalidation::ObjectId kPreferencesId_;
     58   const invalidation::ObjectId kAppNotificationsId_;
     59   const invalidation::ObjectId kAutofillId_;
     60 
     61   base::MessageLoop loop_;
     62 };
     63 
     64 // Set invalidation states for various keys and verify that they are written and
     65 // read back correctly.
     66 TEST_F(InvalidatorStorageTest, SetMaxVersionAndPayload) {
     67   InvalidatorStorage storage(&pref_service_);
     68 
     69   InvalidationStateMap expected_states;
     70   EXPECT_EQ(expected_states, storage.GetAllInvalidationStates());
     71 
     72   expected_states[kBookmarksId_].version = 2;
     73   expected_states[kBookmarksId_].payload = "hello";
     74   storage.SetMaxVersionAndPayload(kBookmarksId_, 2, "hello");
     75   EXPECT_EQ(expected_states, storage.GetAllInvalidationStates());
     76 
     77   expected_states[kPreferencesId_].version = 5;
     78   storage.SetMaxVersionAndPayload(kPreferencesId_, 5, std::string());
     79   EXPECT_EQ(expected_states, storage.GetAllInvalidationStates());
     80 
     81   expected_states[kAppNotificationsId_].version = 3;
     82   expected_states[kAppNotificationsId_].payload = "world";
     83   storage.SetMaxVersionAndPayload(kAppNotificationsId_, 3, "world");
     84   EXPECT_EQ(expected_states, storage.GetAllInvalidationStates());
     85 
     86   expected_states[kAppNotificationsId_].version = 4;
     87   expected_states[kAppNotificationsId_].payload = "again";
     88   storage.SetMaxVersionAndPayload(kAppNotificationsId_, 4, "again");
     89   EXPECT_EQ(expected_states, storage.GetAllInvalidationStates());
     90 }
     91 
     92 // Forgetting an entry should cause that entry to be deleted.
     93 TEST_F(InvalidatorStorageTest, Forget) {
     94   InvalidatorStorage storage(&pref_service_);
     95   EXPECT_TRUE(storage.GetAllInvalidationStates().empty());
     96 
     97   InvalidationStateMap expected_states;
     98   expected_states[kBookmarksId_].version = 2;
     99   expected_states[kBookmarksId_].payload = "a";
    100   expected_states[kPreferencesId_].version = 5;
    101   expected_states[kPreferencesId_].payload = "b";
    102   storage.SetMaxVersionAndPayload(kBookmarksId_, 2, "a");
    103   storage.SetMaxVersionAndPayload(kPreferencesId_, 5, "b");
    104   EXPECT_EQ(expected_states, storage.GetAllInvalidationStates());
    105 
    106   expected_states.erase(kPreferencesId_);
    107   syncer::ObjectIdSet to_forget;
    108   to_forget.insert(kPreferencesId_);
    109   storage.Forget(to_forget);
    110   EXPECT_EQ(expected_states, storage.GetAllInvalidationStates());
    111 }
    112 
    113 // Clearing the storage should erase all version map entries, bootstrap data,
    114 // and the client ID.
    115 TEST_F(InvalidatorStorageTest, Clear) {
    116   InvalidatorStorage storage(&pref_service_);
    117   EXPECT_TRUE(storage.GetAllInvalidationStates().empty());
    118   EXPECT_TRUE(storage.GetBootstrapData().empty());
    119   EXPECT_TRUE(storage.GetInvalidatorClientId().empty());
    120 
    121   storage.SetInvalidatorClientId("fake_id");
    122   EXPECT_EQ("fake_id", storage.GetInvalidatorClientId());
    123 
    124   storage.SetBootstrapData("test");
    125   EXPECT_EQ("test", storage.GetBootstrapData());
    126 
    127   {
    128     InvalidationStateMap expected_states;
    129     expected_states[kAppNotificationsId_].version = 3;
    130     storage.SetMaxVersionAndPayload(kAppNotificationsId_, 3, std::string());
    131     EXPECT_EQ(expected_states, storage.GetAllInvalidationStates());
    132   }
    133 
    134   storage.Clear();
    135 
    136   EXPECT_TRUE(storage.GetAllInvalidationStates().empty());
    137   EXPECT_TRUE(storage.GetBootstrapData().empty());
    138   EXPECT_TRUE(storage.GetInvalidatorClientId().empty());
    139 }
    140 
    141 TEST_F(InvalidatorStorageTest, SerializeEmptyMap) {
    142   InvalidationStateMap empty_map;
    143   base::ListValue list;
    144   InvalidatorStorage::SerializeToList(empty_map, &list);
    145   EXPECT_TRUE(list.empty());
    146 }
    147 
    148 // Make sure we don't choke on a variety of malformed input.
    149 TEST_F(InvalidatorStorageTest, DeserializeFromListInvalidFormat) {
    150   InvalidationStateMap map;
    151   base::ListValue list_with_invalid_format;
    152   DictionaryValue* value;
    153 
    154   // The various cases below use distinct values to make it easier to track down
    155   // failures.
    156   value = new DictionaryValue();
    157   list_with_invalid_format.Append(value);
    158 
    159   value = new DictionaryValue();
    160   value->SetString("completely", "invalid");
    161   list_with_invalid_format.Append(value);
    162 
    163   // Missing two required fields
    164   value = new DictionaryValue();
    165   value->SetString(kSourceKey, "10");
    166   list_with_invalid_format.Append(value);
    167 
    168   value = new DictionaryValue();
    169   value->SetString(kNameKey, "missing source and version");
    170   list_with_invalid_format.Append(value);
    171 
    172   value = new DictionaryValue();
    173   value->SetString(kMaxVersionKey, "3");
    174   list_with_invalid_format.Append(value);
    175 
    176   // Missing one required field
    177   value = new DictionaryValue();
    178   value->SetString(kSourceKey, "14");
    179   value->SetString(kNameKey, "missing version");
    180   list_with_invalid_format.Append(value);
    181 
    182   value = new DictionaryValue();
    183   value->SetString(kSourceKey, "233");
    184   value->SetString(kMaxVersionKey, "5");
    185   list_with_invalid_format.Append(value);
    186 
    187   value = new DictionaryValue();
    188   value->SetString(kNameKey, "missing source");
    189   value->SetString(kMaxVersionKey, "25");
    190   list_with_invalid_format.Append(value);
    191 
    192   // Invalid values in fields
    193   value = new DictionaryValue();
    194   value->SetString(kSourceKey, "a");
    195   value->SetString(kNameKey, "bad source");
    196   value->SetString(kMaxVersionKey, "12");
    197   list_with_invalid_format.Append(value);
    198 
    199   value = new DictionaryValue();
    200   value->SetString(kSourceKey, "1");
    201   value->SetString(kNameKey, "bad max version");
    202   value->SetString(kMaxVersionKey, "a");
    203   list_with_invalid_format.Append(value);
    204 
    205   // And finally something that should work.
    206   invalidation::ObjectId valid_id(42, "this should work");
    207   value = new DictionaryValue();
    208   value->SetString(kSourceKey, "42");
    209   value->SetString(kNameKey, valid_id.name());
    210   value->SetString(kMaxVersionKey, "20");
    211   list_with_invalid_format.Append(value);
    212 
    213   InvalidatorStorage::DeserializeFromList(list_with_invalid_format, &map);
    214 
    215   EXPECT_EQ(1U, map.size());
    216   EXPECT_EQ(20, map[valid_id].version);
    217 }
    218 
    219 // Tests behavior when there are duplicate entries for a single key. The value
    220 // of the last entry with that key should be used in the version map.
    221 TEST_F(InvalidatorStorageTest, DeserializeFromListWithDuplicates) {
    222   InvalidationStateMap map;
    223   base::ListValue list;
    224   DictionaryValue* value;
    225 
    226   value = new DictionaryValue();
    227   value->SetString(kSourceKey, base::IntToString(kBookmarksId_.source()));
    228   value->SetString(kNameKey, kBookmarksId_.name());
    229   value->SetString(kMaxVersionKey, "20");
    230   list.Append(value);
    231   value = new DictionaryValue();
    232   value->SetString(kSourceKey, base::IntToString(kAutofillId_.source()));
    233   value->SetString(kNameKey, kAutofillId_.name());
    234   value->SetString(kMaxVersionKey, "10");
    235   list.Append(value);
    236   value = new DictionaryValue();
    237   value->SetString(kSourceKey, base::IntToString(kBookmarksId_.source()));
    238   value->SetString(kNameKey, kBookmarksId_.name());
    239   value->SetString(kMaxVersionKey, "15");
    240   list.Append(value);
    241 
    242   InvalidatorStorage::DeserializeFromList(list, &map);
    243   EXPECT_EQ(2U, map.size());
    244   EXPECT_EQ(10, map[kAutofillId_].version);
    245   EXPECT_EQ(15, map[kBookmarksId_].version);
    246 }
    247 
    248 TEST_F(InvalidatorStorageTest, DeserializeFromEmptyList) {
    249   InvalidationStateMap map;
    250   base::ListValue list;
    251   InvalidatorStorage::DeserializeFromList(list, &map);
    252   EXPECT_TRUE(map.empty());
    253 }
    254 
    255 // Tests that deserializing a well-formed value results in the expected state
    256 // map.
    257 TEST_F(InvalidatorStorageTest, DeserializeFromListBasic) {
    258   InvalidationStateMap map;
    259   base::ListValue list;
    260   DictionaryValue* value;
    261   syncer::AckHandle ack_handle_1 = syncer::AckHandle::CreateUnique();
    262   syncer::AckHandle ack_handle_2 = syncer::AckHandle::CreateUnique();
    263 
    264   value = new DictionaryValue();
    265   value->SetString(kSourceKey,
    266                    base::IntToString(kAppNotificationsId_.source()));
    267   value->SetString(kNameKey, kAppNotificationsId_.name());
    268   value->SetString(kMaxVersionKey, "20");
    269   value->SetString(kPayloadKey, "testing");
    270   value->Set(kCurrentAckHandleKey, ack_handle_1.ToValue().release());
    271   value->Set(kExpectedAckHandleKey, ack_handle_2.ToValue().release());
    272   list.Append(value);
    273 
    274   InvalidatorStorage::DeserializeFromList(list, &map);
    275   EXPECT_EQ(1U, map.size());
    276   EXPECT_EQ(20, map[kAppNotificationsId_].version);
    277   EXPECT_EQ("testing", map[kAppNotificationsId_].payload);
    278   EXPECT_THAT(map[kAppNotificationsId_].current, Eq(ack_handle_1));
    279   EXPECT_THAT(map[kAppNotificationsId_].expected, Eq(ack_handle_2));
    280 }
    281 
    282 // Tests that deserializing well-formed values when optional parameters are
    283 // omitted works.
    284 TEST_F(InvalidatorStorageTest, DeserializeFromListMissingOptionalValues) {
    285   InvalidationStateMap map;
    286   base::ListValue list;
    287   DictionaryValue* value;
    288   syncer::AckHandle ack_handle = syncer::AckHandle::CreateUnique();
    289 
    290   // Payload missing because of an upgrade from a previous browser version that
    291   // didn't set the field.
    292   value = new DictionaryValue();
    293   value->SetString(kSourceKey, base::IntToString(kAutofillId_.source()));
    294   value->SetString(kNameKey, kAutofillId_.name());
    295   value->SetString(kMaxVersionKey, "10");
    296   list.Append(value);
    297   // A crash between SetMaxVersion() and a callback from GenerateAckHandles()
    298   // could result in this state.
    299   value = new DictionaryValue();
    300   value->SetString(kSourceKey, base::IntToString(kBookmarksId_.source()));
    301   value->SetString(kNameKey, kBookmarksId_.name());
    302   value->SetString(kMaxVersionKey, "15");
    303   value->SetString(kPayloadKey, "hello");
    304   list.Append(value);
    305   // Never acknowledged, so current ack handle is unset.
    306   value = new DictionaryValue();
    307   value->SetString(kSourceKey, base::IntToString(kPreferencesId_.source()));
    308   value->SetString(kNameKey, kPreferencesId_.name());
    309   value->SetString(kMaxVersionKey, "20");
    310   value->SetString(kPayloadKey, "world");
    311   value->Set(kExpectedAckHandleKey, ack_handle.ToValue().release());
    312   list.Append(value);
    313 
    314   InvalidatorStorage::DeserializeFromList(list, &map);
    315   EXPECT_EQ(3U, map.size());
    316 
    317   EXPECT_EQ(10, map[kAutofillId_].version);
    318   EXPECT_EQ("", map[kAutofillId_].payload);
    319   EXPECT_FALSE(map[kAutofillId_].current.IsValid());
    320   EXPECT_FALSE(map[kAutofillId_].expected.IsValid());
    321 
    322   EXPECT_EQ(15, map[kBookmarksId_].version);
    323   EXPECT_EQ("hello", map[kBookmarksId_].payload);
    324   EXPECT_FALSE(map[kBookmarksId_].current.IsValid());
    325   EXPECT_FALSE(map[kBookmarksId_].expected.IsValid());
    326 
    327   EXPECT_EQ(20, map[kPreferencesId_].version);
    328   EXPECT_EQ("world", map[kPreferencesId_].payload);
    329   EXPECT_FALSE(map[kPreferencesId_].current.IsValid());
    330   EXPECT_THAT(map[kPreferencesId_].expected, Eq(ack_handle));
    331 }
    332 
    333 // Tests for legacy deserialization code.
    334 TEST_F(InvalidatorStorageTest, DeserializeMapOutOfRange) {
    335   InvalidationStateMap map;
    336   base::DictionaryValue dict_with_out_of_range_type;
    337 
    338   dict_with_out_of_range_type.SetString(
    339       base::IntToString(syncer::TOP_LEVEL_FOLDER), "100");
    340   dict_with_out_of_range_type.SetString(
    341       base::IntToString(syncer::BOOKMARKS), "5");
    342 
    343   InvalidatorStorage::DeserializeMap(&dict_with_out_of_range_type, &map);
    344 
    345   EXPECT_EQ(1U, map.size());
    346   EXPECT_EQ(5, map[kBookmarksId_].version);
    347 }
    348 
    349 TEST_F(InvalidatorStorageTest, DeserializeMapInvalidFormat) {
    350   InvalidationStateMap map;
    351   base::DictionaryValue dict_with_invalid_format;
    352 
    353   dict_with_invalid_format.SetString("whoops", "5");
    354   dict_with_invalid_format.SetString("ohnoes", "whoops");
    355   dict_with_invalid_format.SetString(
    356       base::IntToString(syncer::BOOKMARKS), "ohnoes");
    357   dict_with_invalid_format.SetString(
    358       base::IntToString(syncer::AUTOFILL), "10");
    359 
    360   InvalidatorStorage::DeserializeMap(&dict_with_invalid_format, &map);
    361 
    362   EXPECT_EQ(1U, map.size());
    363   EXPECT_EQ(10, map[kAutofillId_].version);
    364 }
    365 
    366 TEST_F(InvalidatorStorageTest, DeserializeMapEmptyDictionary) {
    367   InvalidationStateMap map;
    368   base::DictionaryValue dict;
    369   InvalidatorStorage::DeserializeMap(&dict, &map);
    370   EXPECT_TRUE(map.empty());
    371 }
    372 
    373 TEST_F(InvalidatorStorageTest, DeserializeMapBasic) {
    374   InvalidationStateMap map;
    375   base::DictionaryValue dict;
    376 
    377   dict.SetString(base::IntToString(syncer::AUTOFILL), "10");
    378   dict.SetString(base::IntToString(syncer::BOOKMARKS), "15");
    379 
    380   InvalidatorStorage::DeserializeMap(&dict, &map);
    381   EXPECT_EQ(2U, map.size());
    382   EXPECT_EQ(10, map[kAutofillId_].version);
    383   EXPECT_EQ(15, map[kBookmarksId_].version);
    384 }
    385 
    386 // Test that the migration code for the legacy preference works as expected.
    387 // Migration should happen on construction of InvalidatorStorage.
    388 TEST_F(InvalidatorStorageTest, MigrateLegacyPreferences) {
    389   base::DictionaryValue* legacy_dict = new DictionaryValue;
    390   legacy_dict->SetString(base::IntToString(syncer::AUTOFILL), "10");
    391   legacy_dict->SetString(base::IntToString(syncer::BOOKMARKS), "32");
    392   legacy_dict->SetString(base::IntToString(syncer::PREFERENCES), "54");
    393   pref_service_.SetUserPref(prefs::kSyncMaxInvalidationVersions, legacy_dict);
    394   InvalidatorStorage storage(&pref_service_);
    395 
    396   // Legacy pref should be cleared.
    397   const base::DictionaryValue* dict =
    398       pref_service_.GetDictionary(prefs::kSyncMaxInvalidationVersions);
    399   EXPECT_TRUE(dict->empty());
    400 
    401   // Validate the new pref is set correctly.
    402   InvalidationStateMap map;
    403   const base::ListValue* list =
    404       pref_service_.GetList(prefs::kInvalidatorMaxInvalidationVersions);
    405   InvalidatorStorage::DeserializeFromList(*list, &map);
    406 
    407   EXPECT_EQ(3U, map.size());
    408   EXPECT_EQ(10, map[kAutofillId_].version);
    409   EXPECT_EQ(32, map[kBookmarksId_].version);
    410   EXPECT_EQ(54, map[kPreferencesId_].version);
    411 }
    412 
    413 TEST_F(InvalidatorStorageTest, SetGetNotifierClientId) {
    414   InvalidatorStorage storage(&pref_service_);
    415   const std::string client_id("fK6eDzAIuKqx9A4+93bljg==");
    416 
    417   storage.SetInvalidatorClientId(client_id);
    418   EXPECT_EQ(client_id, storage.GetInvalidatorClientId());
    419 }
    420 
    421 TEST_F(InvalidatorStorageTest, SetGetBootstrapData) {
    422   InvalidatorStorage storage(&pref_service_);
    423   const std::string mess("n\0tK\0\0l\344", 8);
    424   ASSERT_FALSE(IsStringUTF8(mess));
    425 
    426   storage.SetBootstrapData(mess);
    427   EXPECT_EQ(mess, storage.GetBootstrapData());
    428 }
    429 
    430 // Test that we correctly generate ack handles, acknowledge them, and persist
    431 // them.
    432 TEST_F(InvalidatorStorageTest, GenerateAckHandlesAndAcknowledge) {
    433   InvalidatorStorage storage(&pref_service_);
    434   syncer::ObjectIdSet ids;
    435   InvalidationStateMap state_map;
    436   syncer::AckHandleMap ack_handle_map;
    437   syncer::AckHandleMap::const_iterator it;
    438 
    439   // Test that it works as expected if the key doesn't already exist in the map,
    440   // e.g. the first invalidation received for the object ID was not for a
    441   // specific version.
    442   ids.insert(kAutofillId_);
    443   storage.GenerateAckHandles(
    444       ids, base::MessageLoopProxy::current(),
    445       base::Bind(&GenerateAckHandlesTestHelper, &ack_handle_map));
    446   loop_.RunUntilIdle();
    447   EXPECT_EQ(1U, ack_handle_map.size());
    448   it = ack_handle_map.find(kAutofillId_);
    449   // Android STL appears to be buggy and causes gtest's IsContainerTest<> to
    450   // treat an iterator as a STL container so we use != instead of ASSERT_NE.
    451   ASSERT_TRUE(ack_handle_map.end() != it);
    452   EXPECT_TRUE(it->second.IsValid());
    453   state_map[kAutofillId_].expected = it->second;
    454   EXPECT_EQ(state_map, storage.GetAllInvalidationStates());
    455 
    456   storage.Acknowledge(kAutofillId_, it->second);
    457   state_map[kAutofillId_].current = it->second;
    458   EXPECT_EQ(state_map, storage.GetAllInvalidationStates());
    459 
    460   ids.clear();
    461 
    462   // Test that it works as expected if the key already exists.
    463   state_map[kBookmarksId_].version = 11;
    464   state_map[kBookmarksId_].payload = "hello";
    465   storage.SetMaxVersionAndPayload(kBookmarksId_, 11, "hello");
    466   EXPECT_EQ(state_map, storage.GetAllInvalidationStates());
    467   ids.insert(kBookmarksId_);
    468   storage.GenerateAckHandles(
    469       ids, base::MessageLoopProxy::current(),
    470       base::Bind(&GenerateAckHandlesTestHelper, &ack_handle_map));
    471   loop_.RunUntilIdle();
    472   EXPECT_EQ(1U, ack_handle_map.size());
    473   it = ack_handle_map.find(kBookmarksId_);
    474   ASSERT_TRUE(ack_handle_map.end() != it);
    475   EXPECT_TRUE(it->second.IsValid());
    476   state_map[kBookmarksId_].expected = it->second;
    477   EXPECT_EQ(state_map, storage.GetAllInvalidationStates());
    478 
    479   storage.Acknowledge(kBookmarksId_, it->second);
    480   state_map[kBookmarksId_].current = it->second;
    481   EXPECT_EQ(state_map, storage.GetAllInvalidationStates());
    482 
    483   // Finally, test that the ack handles are updated if we're asked to generate
    484   // another ack handle for the same object ID.
    485   state_map[kBookmarksId_].version = 12;
    486   state_map[kBookmarksId_].payload = "world";
    487   storage.SetMaxVersionAndPayload(kBookmarksId_, 12, "world");
    488   EXPECT_EQ(state_map, storage.GetAllInvalidationStates());
    489   ids.insert(kBookmarksId_);
    490   storage.GenerateAckHandles(
    491       ids, base::MessageLoopProxy::current(),
    492       base::Bind(&GenerateAckHandlesTestHelper, &ack_handle_map));
    493   loop_.RunUntilIdle();
    494   EXPECT_EQ(1U, ack_handle_map.size());
    495   it = ack_handle_map.find(kBookmarksId_);
    496   ASSERT_TRUE(ack_handle_map.end() != it);
    497   EXPECT_TRUE(it->second.IsValid());
    498   state_map[kBookmarksId_].expected = it->second;
    499   EXPECT_EQ(state_map, storage.GetAllInvalidationStates());
    500 
    501   storage.Acknowledge(kBookmarksId_, it->second);
    502   state_map[kBookmarksId_].current = it->second;
    503   EXPECT_EQ(state_map, storage.GetAllInvalidationStates());
    504 }
    505 
    506 }  // namespace invalidation
    507