OLD | NEW |
| (Empty) |
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_noti
fications_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 ~SyncedNotificationsShimTest() override; | |
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, sync_data, change_processor.Pass(), nullptr); | |
153 } | |
154 | |
155 // Starting sync should fire the sync started event, but only after both types | |
156 // have started. | |
157 TEST_F(SyncedNotificationsShimTest, StartSync) { | |
158 EXPECT_FALSE(shim()->IsSyncReady()); | |
159 StartSync(syncer::SYNCED_NOTIFICATIONS); | |
160 EXPECT_FALSE(shim()->IsSyncReady()); | |
161 EXPECT_FALSE(GetLastEvent()); | |
162 | |
163 StartSync(syncer::SYNCED_NOTIFICATION_APP_INFO); | |
164 EXPECT_TRUE(shim()->IsSyncReady()); | |
165 | |
166 scoped_ptr<Event> event = GetLastEvent(); | |
167 ASSERT_TRUE(event); | |
168 EXPECT_EQ(synced_notifications_private::OnSyncStartup::kEventName, | |
169 event->event_name); | |
170 | |
171 EXPECT_TRUE(notification_processor()->changes().empty()); | |
172 EXPECT_TRUE(app_info_processor()->changes().empty()); | |
173 } | |
174 | |
175 // A sync update should fire the OnDataChanges event with the updated | |
176 // notification. | |
177 TEST_F(SyncedNotificationsShimTest, ProcessSyncChangesSingleNotification) { | |
178 StartSync(); | |
179 syncer::SyncChangeList change_list; | |
180 change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key")); | |
181 shim()->ProcessSyncChanges(FROM_HERE, change_list); | |
182 scoped_ptr<Event> event = GetLastEvent(); | |
183 ASSERT_TRUE(event); | |
184 EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName, | |
185 event->event_name); | |
186 ASSERT_TRUE(event->event_args); | |
187 EXPECT_EQ(1U, event->event_args->GetSize()); | |
188 | |
189 base::ListValue* args = NULL; | |
190 ASSERT_TRUE(event->event_args->GetList(0, &args)); | |
191 EXPECT_EQ(1U, args->GetSize()); | |
192 base::DictionaryValue* sync_change_value = NULL; | |
193 ASSERT_TRUE(args->GetDictionary(0, &sync_change_value)); | |
194 scoped_ptr<synced_notifications_private::SyncChange> sync_change = | |
195 synced_notifications_private::SyncChange::FromValue(*sync_change_value); | |
196 ASSERT_TRUE(sync_change); | |
197 EXPECT_TRUE(ChangeSpecificsMatch(change_list[0], | |
198 sync_change->data.data_item)); | |
199 EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED, | |
200 sync_change->change_type); | |
201 } | |
202 | |
203 // Verify that multiple notification updates can be sent in one event. | |
204 TEST_F(SyncedNotificationsShimTest, ProcessSyncChangesMultipleNotification) { | |
205 StartSync(); | |
206 syncer::SyncChangeList change_list; | |
207 change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key")); | |
208 change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key2")); | |
209 shim()->ProcessSyncChanges(FROM_HERE, change_list); | |
210 scoped_ptr<Event> event = GetLastEvent(); | |
211 ASSERT_TRUE(event); | |
212 EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName, | |
213 event->event_name); | |
214 ASSERT_TRUE(event->event_args); | |
215 base::ListValue* args = NULL; | |
216 ASSERT_TRUE(event->event_args->GetList(0, &args)); | |
217 EXPECT_EQ(2U, args->GetSize()); | |
218 | |
219 base::DictionaryValue* sync_change_value = NULL; | |
220 ASSERT_TRUE(args->GetDictionary(0, &sync_change_value)); | |
221 scoped_ptr<synced_notifications_private::SyncChange> sync_change = | |
222 synced_notifications_private::SyncChange::FromValue(*sync_change_value); | |
223 ASSERT_TRUE(sync_change); | |
224 EXPECT_TRUE(ChangeSpecificsMatch(change_list[0], | |
225 sync_change->data.data_item)); | |
226 EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED, | |
227 sync_change->change_type); | |
228 | |
229 ASSERT_TRUE(args->GetDictionary(1, &sync_change_value)); | |
230 sync_change = | |
231 synced_notifications_private::SyncChange::FromValue(*sync_change_value); | |
232 ASSERT_TRUE(sync_change); | |
233 EXPECT_TRUE(ChangeSpecificsMatch(change_list[1], | |
234 sync_change->data.data_item)); | |
235 EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED, | |
236 sync_change->change_type); | |
237 } | |
238 | |
239 // Verify AppInfo updates trigger OnDataChanges events. | |
240 TEST_F(SyncedNotificationsShimTest, ProcessSyncChangeAppInfo) { | |
241 StartSync(); | |
242 syncer::SyncChangeList change_list; | |
243 change_list.push_back( | |
244 BuildChange(syncer::SYNCED_NOTIFICATION_APP_INFO, "key")); | |
245 shim()->ProcessSyncChanges(FROM_HERE, change_list); | |
246 scoped_ptr<Event> event = GetLastEvent(); | |
247 ASSERT_TRUE(event); | |
248 EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName, | |
249 event->event_name); | |
250 ASSERT_TRUE(event->event_args); | |
251 EXPECT_EQ(1U, event->event_args->GetSize()); | |
252 | |
253 base::ListValue* args = NULL; | |
254 ASSERT_TRUE(event->event_args->GetList(0, &args)); | |
255 EXPECT_EQ(1U, args->GetSize()); | |
256 base::DictionaryValue* sync_change_value = NULL; | |
257 ASSERT_TRUE(args->GetDictionary(0, &sync_change_value)); | |
258 scoped_ptr<synced_notifications_private::SyncChange> sync_change = | |
259 synced_notifications_private::SyncChange::FromValue(*sync_change_value); | |
260 ASSERT_TRUE(sync_change); | |
261 EXPECT_TRUE(ChangeSpecificsMatch(change_list[0], | |
262 sync_change->data.data_item)); | |
263 EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED, | |
264 sync_change->change_type); | |
265 } | |
266 | |
267 // Attempt to get the initial sync data both before and after sync starts. | |
268 TEST_F(SyncedNotificationsShimTest, GetInitialData) { | |
269 std::vector<linked_ptr<synced_notifications_private::SyncData> > data; | |
270 EXPECT_FALSE(shim()->GetInitialData( | |
271 synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data)); | |
272 EXPECT_FALSE(shim()->GetInitialData( | |
273 synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data)); | |
274 | |
275 StartSync(); | |
276 | |
277 EXPECT_TRUE(shim()->GetInitialData( | |
278 synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data)); | |
279 EXPECT_TRUE(data.empty()); | |
280 EXPECT_TRUE(shim()->GetInitialData( | |
281 synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data)); | |
282 EXPECT_TRUE(data.empty()); | |
283 | |
284 notification_processor()->data().push_back(BuildData( | |
285 syncer::SYNCED_NOTIFICATIONS, "notif_key")); | |
286 EXPECT_TRUE(shim()->GetInitialData( | |
287 synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data)); | |
288 EXPECT_EQ(1U, data.size()); | |
289 EXPECT_TRUE(DataSpecificsMatch(notification_processor()->data()[0], | |
290 data[0]->data_item)); | |
291 | |
292 data.clear(); | |
293 app_info_processor()->data().push_back(BuildData( | |
294 syncer::SYNCED_NOTIFICATION_APP_INFO, "app_key")); | |
295 EXPECT_TRUE(shim()->GetInitialData( | |
296 synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data)); | |
297 EXPECT_EQ(1U, data.size()); | |
298 EXPECT_TRUE(DataSpecificsMatch(app_info_processor()->data()[0], | |
299 data[0]->data_item)); | |
300 } | |
301 | |
302 // Verify that notification updates are properly handled. | |
303 TEST_F(SyncedNotificationsShimTest, UpdateNotification) { | |
304 syncer::SyncData data = | |
305 BuildData(syncer::SYNCED_NOTIFICATIONS, "notif_key"); | |
306 std::string serialized = | |
307 data.GetSpecifics().synced_notification().SerializeAsString(); | |
308 EXPECT_FALSE(shim()->UpdateNotification(serialized)); | |
309 | |
310 StartSync(); | |
311 | |
312 EXPECT_FALSE(shim()->UpdateNotification("gibberish")); | |
313 EXPECT_TRUE(notification_processor()->changes().empty()); | |
314 | |
315 EXPECT_TRUE(shim()->UpdateNotification(serialized)); | |
316 EXPECT_EQ(1U, notification_processor()->changes().size()); | |
317 EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, | |
318 notification_processor()->changes()[0].change_type()); | |
319 EXPECT_EQ(syncer::SYNCED_NOTIFICATIONS, | |
320 notification_processor()->changes()[0].sync_data().GetDataType()); | |
321 EXPECT_EQ(serialized, | |
322 notification_processor() | |
323 ->changes()[0] | |
324 .sync_data() | |
325 .GetSpecifics() | |
326 .synced_notification() | |
327 .SerializeAsString()); | |
328 } | |
329 | |
330 // Verify that SetRenderContext updates the datatype context properly. | |
331 TEST_F(SyncedNotificationsShimTest, SetRenderContext) { | |
332 const std::string kContext = "context"; | |
333 EXPECT_FALSE(shim()->SetRenderContext( | |
334 synced_notifications_private::REFRESH_REQUEST_REFRESH_NEEDED, kContext)); | |
335 EXPECT_FALSE(refresh_requested()); | |
336 | |
337 StartSync(); | |
338 | |
339 EXPECT_TRUE(shim()->SetRenderContext( | |
340 synced_notifications_private::REFRESH_REQUEST_REFRESH_NEEDED, kContext)); | |
341 EXPECT_EQ(kContext, notification_processor()->context()); | |
342 EXPECT_TRUE(refresh_requested()); | |
343 } | |
344 | |
345 } // namespace | |
OLD | NEW |