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