Home | History | Annotate | Download | only in synced_notifications_private
      1 // Copyright 2014 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/extensions/api/synced_notifications_private/synced_notifications_shim.h"
      6 
      7 #include "base/json/json_writer.h"
      8 #include "extensions/browser/event_router.h"
      9 #include "sync/api/fake_sync_change_processor.h"
     10 #include "sync/api/sync_change.h"
     11 #include "sync/api/sync_error_factory.h"
     12 #include "sync/protocol/sync.pb.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 
     15 using namespace extensions;
     16 using namespace extensions::api;
     17 
     18 namespace {
     19 
     20 // Builds a SyncData for the specified |type| based on |key|.
     21 syncer::SyncData BuildData(syncer::ModelType type, const std::string& key) {
     22   sync_pb::EntitySpecifics specifics;
     23   if (type == syncer::SYNCED_NOTIFICATIONS) {
     24     specifics.mutable_synced_notification()
     25         ->mutable_coalesced_notification()
     26         ->set_key(key);
     27   } else {
     28     specifics.mutable_synced_notification_app_info()
     29         ->add_synced_notification_app_info()
     30         ->add_app_id(key);
     31   }
     32   syncer::SyncData data = syncer::SyncData::CreateLocalData(
     33       key, key, specifics);
     34   return data;
     35 }
     36 
     37 // Builds a SyncChange with an update to the specified |type| based on |key|.
     38 syncer::SyncChange BuildChange(syncer::ModelType type, const std::string& key) {
     39   syncer::SyncChangeList change_list;
     40   syncer::SyncData data = BuildData(type, key);
     41   return syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data);
     42 }
     43 
     44 // Verifies that the specifics within |data| match the serialized specifics
     45 // within |serialized|.
     46 testing::AssertionResult DataSpecificsMatch(const syncer::SyncData& data,
     47                                             const std::string& serialized) {
     48   if (data.GetDataType() == syncer::SYNCED_NOTIFICATIONS) {
     49     const sync_pb::SyncedNotificationSpecifics& proto =
     50         data.GetSpecifics().synced_notification();
     51     if (serialized != proto.SerializeAsString())
     52       return testing::AssertionFailure() << "Notification specifics mismatch";
     53   } else {
     54     const sync_pb::SyncedNotificationAppInfoSpecifics& proto =
     55         data.GetSpecifics().synced_notification_app_info();
     56     if (serialized != proto.SerializeAsString())
     57       return testing::AssertionFailure() << "App info specifics mismatch";
     58   }
     59   return testing::AssertionSuccess();
     60 }
     61 
     62 // Verifies that the update within |change| matchs the serialized specifics
     63 // within |serialized|.
     64 testing::AssertionResult ChangeSpecificsMatch(const syncer::SyncChange& change,
     65                                               const std::string& serialized) {
     66   if (change.change_type() != syncer::SyncChange::ACTION_UPDATE)
     67     return testing::AssertionFailure() << "Invalid change type";
     68   return DataSpecificsMatch(change.sync_data(), serialized);
     69 }
     70 
     71 class SyncedNotificationsShimTest : public testing::Test {
     72  public:
     73   SyncedNotificationsShimTest();
     74   virtual ~SyncedNotificationsShimTest();
     75 
     76   // Starts sync for both sync types.
     77   void StartSync();
     78   // Starts sync for the specified datatype |type|.
     79   void StartSync(syncer::ModelType type);
     80 
     81   // Transfers ownership of the last event received.
     82   scoped_ptr<Event> GetLastEvent();
     83 
     84   // Records that a refresh has been requested.
     85   void RequestRefresh();
     86 
     87   SyncedNotificationsShim* shim() { return &shim_; }
     88   syncer::FakeSyncChangeProcessor* notification_processor() {
     89     return notification_processor_;
     90   }
     91   syncer::FakeSyncChangeProcessor* app_info_processor() {
     92     return app_info_processor_;
     93   }
     94   bool refresh_requested() const { return refresh_requested_; }
     95 
     96  private:
     97   void EventCallback(scoped_ptr<Event> event);
     98 
     99   // Shim being tested.
    100   SyncedNotificationsShim shim_;
    101 
    102   syncer::FakeSyncChangeProcessor* notification_processor_;
    103   syncer::FakeSyncChangeProcessor* app_info_processor_;
    104 
    105   // The last event fired by the shim.
    106   scoped_ptr<Event> last_event_fired_;
    107 
    108   // Whether a refresh has been requested;
    109   bool refresh_requested_;
    110 };
    111 
    112 SyncedNotificationsShimTest::SyncedNotificationsShimTest()
    113     : shim_(base::Bind(&SyncedNotificationsShimTest::EventCallback,
    114                        base::Unretained(this)),
    115             base::Bind(&SyncedNotificationsShimTest::RequestRefresh,
    116                        base::Unretained(this))),
    117       notification_processor_(NULL),
    118       app_info_processor_(NULL),
    119       refresh_requested_(false) {}
    120 
    121 SyncedNotificationsShimTest::~SyncedNotificationsShimTest() {}
    122 
    123 void SyncedNotificationsShimTest::EventCallback(scoped_ptr<Event> event) {
    124   ASSERT_FALSE(last_event_fired_);
    125   last_event_fired_ = event.Pass();
    126 }
    127 
    128 void SyncedNotificationsShimTest::RequestRefresh() {
    129   ASSERT_FALSE(refresh_requested_);
    130   refresh_requested_ = true;
    131 }
    132 
    133 scoped_ptr<Event> SyncedNotificationsShimTest::GetLastEvent() {
    134   return last_event_fired_.Pass();
    135 }
    136 
    137 void SyncedNotificationsShimTest::StartSync() {
    138   StartSync(syncer::SYNCED_NOTIFICATIONS);
    139   StartSync(syncer::SYNCED_NOTIFICATION_APP_INFO);
    140   GetLastEvent();
    141 }
    142 
    143 void SyncedNotificationsShimTest::StartSync(syncer::ModelType type) {
    144   scoped_ptr<syncer::FakeSyncChangeProcessor> change_processor(
    145       new syncer::FakeSyncChangeProcessor());
    146   if (type == syncer::SYNCED_NOTIFICATIONS)
    147     notification_processor_ = change_processor.get();
    148   else
    149     app_info_processor_ = change_processor.get();
    150   syncer::SyncDataList sync_data;
    151   shim_.MergeDataAndStartSyncing(
    152       type,
    153       sync_data,
    154       change_processor.PassAs<syncer::SyncChangeProcessor>(),
    155       scoped_ptr<syncer::SyncErrorFactory>());
    156 }
    157 
    158 // Starting sync should fire the sync started event, but only after both types
    159 // have started.
    160 TEST_F(SyncedNotificationsShimTest, StartSync) {
    161   EXPECT_FALSE(shim()->IsSyncReady());
    162   StartSync(syncer::SYNCED_NOTIFICATIONS);
    163   EXPECT_FALSE(shim()->IsSyncReady());
    164   EXPECT_FALSE(GetLastEvent());
    165 
    166   StartSync(syncer::SYNCED_NOTIFICATION_APP_INFO);
    167   EXPECT_TRUE(shim()->IsSyncReady());
    168 
    169   scoped_ptr<Event> event = GetLastEvent();
    170   ASSERT_TRUE(event);
    171   EXPECT_EQ(synced_notifications_private::OnSyncStartup::kEventName,
    172             event->event_name);
    173 
    174   EXPECT_TRUE(notification_processor()->changes().empty());
    175   EXPECT_TRUE(app_info_processor()->changes().empty());
    176 }
    177 
    178 // A sync update should fire the OnDataChanges event with the updated
    179 // notification.
    180 TEST_F(SyncedNotificationsShimTest, ProcessSyncChangesSingleNotification) {
    181   StartSync();
    182   syncer::SyncChangeList change_list;
    183   change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key"));
    184   shim()->ProcessSyncChanges(FROM_HERE, change_list);
    185   scoped_ptr<Event> event = GetLastEvent();
    186   ASSERT_TRUE(event);
    187   EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName,
    188             event->event_name);
    189   ASSERT_TRUE(event->event_args);
    190   EXPECT_EQ(1U, event->event_args->GetSize());
    191 
    192   base::ListValue* args = NULL;
    193   ASSERT_TRUE(event->event_args->GetList(0, &args));
    194   EXPECT_EQ(1U, args->GetSize());
    195   base::DictionaryValue* sync_change_value = NULL;
    196   ASSERT_TRUE(args->GetDictionary(0, &sync_change_value));
    197   scoped_ptr<synced_notifications_private::SyncChange> sync_change =
    198       synced_notifications_private::SyncChange::FromValue(*sync_change_value);
    199   ASSERT_TRUE(sync_change);
    200   EXPECT_TRUE(ChangeSpecificsMatch(change_list[0],
    201                                    sync_change->data.data_item));
    202   EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED,
    203             sync_change->change_type);
    204 }
    205 
    206 // Verify that multiple notification updates can be sent in one event.
    207 TEST_F(SyncedNotificationsShimTest, ProcessSyncChangesMultipleNotification) {
    208   StartSync();
    209   syncer::SyncChangeList change_list;
    210   change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key"));
    211   change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key2"));
    212   shim()->ProcessSyncChanges(FROM_HERE, change_list);
    213   scoped_ptr<Event> event = GetLastEvent();
    214   ASSERT_TRUE(event);
    215   EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName,
    216             event->event_name);
    217   ASSERT_TRUE(event->event_args);
    218   base::ListValue* args = NULL;
    219   ASSERT_TRUE(event->event_args->GetList(0, &args));
    220   EXPECT_EQ(2U, args->GetSize());
    221 
    222   base::DictionaryValue* sync_change_value = NULL;
    223   ASSERT_TRUE(args->GetDictionary(0, &sync_change_value));
    224   scoped_ptr<synced_notifications_private::SyncChange> sync_change =
    225       synced_notifications_private::SyncChange::FromValue(*sync_change_value);
    226   ASSERT_TRUE(sync_change);
    227   EXPECT_TRUE(ChangeSpecificsMatch(change_list[0],
    228                                    sync_change->data.data_item));
    229   EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED,
    230             sync_change->change_type);
    231 
    232   ASSERT_TRUE(args->GetDictionary(1, &sync_change_value));
    233   sync_change =
    234       synced_notifications_private::SyncChange::FromValue(*sync_change_value);
    235   ASSERT_TRUE(sync_change);
    236   EXPECT_TRUE(ChangeSpecificsMatch(change_list[1],
    237                                    sync_change->data.data_item));
    238   EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED,
    239             sync_change->change_type);
    240 }
    241 
    242 // Verify AppInfo updates trigger OnDataChanges events.
    243 TEST_F(SyncedNotificationsShimTest, ProcessSyncChangeAppInfo) {
    244   StartSync();
    245   syncer::SyncChangeList change_list;
    246   change_list.push_back(
    247       BuildChange(syncer::SYNCED_NOTIFICATION_APP_INFO, "key"));
    248   shim()->ProcessSyncChanges(FROM_HERE, change_list);
    249   scoped_ptr<Event> event = GetLastEvent();
    250   ASSERT_TRUE(event);
    251   EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName,
    252             event->event_name);
    253   ASSERT_TRUE(event->event_args);
    254   EXPECT_EQ(1U, event->event_args->GetSize());
    255 
    256   base::ListValue* args = NULL;
    257   ASSERT_TRUE(event->event_args->GetList(0, &args));
    258   EXPECT_EQ(1U, args->GetSize());
    259   base::DictionaryValue* sync_change_value = NULL;
    260   ASSERT_TRUE(args->GetDictionary(0, &sync_change_value));
    261   scoped_ptr<synced_notifications_private::SyncChange> sync_change =
    262       synced_notifications_private::SyncChange::FromValue(*sync_change_value);
    263   ASSERT_TRUE(sync_change);
    264   EXPECT_TRUE(ChangeSpecificsMatch(change_list[0],
    265                                    sync_change->data.data_item));
    266   EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED,
    267             sync_change->change_type);
    268 }
    269 
    270 // Attempt to get the initial sync data both before and after sync starts.
    271 TEST_F(SyncedNotificationsShimTest, GetInitialData) {
    272   std::vector<linked_ptr<synced_notifications_private::SyncData> > data;
    273   EXPECT_FALSE(shim()->GetInitialData(
    274       synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data));
    275   EXPECT_FALSE(shim()->GetInitialData(
    276       synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data));
    277 
    278   StartSync();
    279 
    280   EXPECT_TRUE(shim()->GetInitialData(
    281       synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data));
    282   EXPECT_TRUE(data.empty());
    283   EXPECT_TRUE(shim()->GetInitialData(
    284       synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data));
    285   EXPECT_TRUE(data.empty());
    286 
    287   notification_processor()->data().push_back(BuildData(
    288       syncer::SYNCED_NOTIFICATIONS, "notif_key"));
    289   EXPECT_TRUE(shim()->GetInitialData(
    290       synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data));
    291   EXPECT_EQ(1U, data.size());
    292   EXPECT_TRUE(DataSpecificsMatch(notification_processor()->data()[0],
    293                                  data[0]->data_item));
    294 
    295   data.clear();
    296   app_info_processor()->data().push_back(BuildData(
    297       syncer::SYNCED_NOTIFICATION_APP_INFO, "app_key"));
    298   EXPECT_TRUE(shim()->GetInitialData(
    299       synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data));
    300   EXPECT_EQ(1U, data.size());
    301   EXPECT_TRUE(DataSpecificsMatch(app_info_processor()->data()[0],
    302                                  data[0]->data_item));
    303 }
    304 
    305 // Verify that notification updates are properly handled.
    306 TEST_F(SyncedNotificationsShimTest, UpdateNotification) {
    307   syncer::SyncData data =
    308       BuildData(syncer::SYNCED_NOTIFICATIONS, "notif_key");
    309   std::string serialized =
    310       data.GetSpecifics().synced_notification().SerializeAsString();
    311   EXPECT_FALSE(shim()->UpdateNotification(serialized));
    312 
    313   StartSync();
    314 
    315   EXPECT_FALSE(shim()->UpdateNotification("gibberish"));
    316   EXPECT_TRUE(notification_processor()->changes().empty());
    317 
    318   EXPECT_TRUE(shim()->UpdateNotification(serialized));
    319   EXPECT_EQ(1U, notification_processor()->changes().size());
    320   EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE,
    321             notification_processor()->changes()[0].change_type());
    322   EXPECT_EQ(syncer::SYNCED_NOTIFICATIONS,
    323             notification_processor()->changes()[0].sync_data().GetDataType());
    324   EXPECT_EQ(serialized,
    325             notification_processor()
    326                 ->changes()[0]
    327                 .sync_data()
    328                 .GetSpecifics()
    329                 .synced_notification()
    330                 .SerializeAsString());
    331 }
    332 
    333 // Verify that SetRenderContext updates the datatype context properly.
    334 TEST_F(SyncedNotificationsShimTest, SetRenderContext) {
    335   const std::string kContext = "context";
    336   EXPECT_FALSE(shim()->SetRenderContext(
    337       synced_notifications_private::REFRESH_REQUEST_REFRESH_NEEDED, kContext));
    338   EXPECT_FALSE(refresh_requested());
    339 
    340   StartSync();
    341 
    342   EXPECT_TRUE(shim()->SetRenderContext(
    343       synced_notifications_private::REFRESH_REQUEST_REFRESH_NEEDED, kContext));
    344   EXPECT_EQ(kContext, notification_processor()->context());
    345   EXPECT_TRUE(refresh_requested());
    346 }
    347 
    348 }  // namespace
    349