OLD | NEW |
---|---|
(Empty) | |
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 <map> | |
6 | |
7 #include "base/memory/scoped_ptr.h" | |
8 #include "chrome/browser/notifications/notification_ui_manager.h" | |
9 #include "chrome/browser/notifier/chrome_notifier_service.h" | |
10 #include "chrome/browser/notifier/synced_notification.h" | |
11 #include "sync/api/sync_change.h" | |
12 #include "sync/api/sync_change_processor.h" | |
13 #include "sync/api/sync_error_factory.h" | |
14 #include "sync/api/sync_error_factory_mock.h" | |
15 #include "sync/protocol/sync.pb.h" | |
16 #include "sync/protocol/synced_notification_specifics.pb.h" | |
17 #include "testing/gtest/include/gtest/gtest.h" | |
18 | |
19 using sync_pb::SyncedNotificationSpecifics; | |
20 using sync_pb::SyncedNotification; | |
21 using sync_pb::EntitySpecifics; | |
22 using sync_pb::SyncedNotificationRenderInfo_Layout_LayoutType_TITLE_AND_SUBTEXT; | |
23 using namespace syncer; | |
24 | |
25 namespace { | |
26 | |
27 const char kAppId1[] = "fboilmbenheemaomgaeehigklolhkhnf"; | |
28 const char kAppId2[] = "fbcmoldooppoahjhfflnmljoanccekpf"; | |
29 const char kAppId3[] = "fbcmoldooppoahjhfflnmljoanccek33"; | |
30 const char kAppId4[] = "fbcmoldooppoahjhfflnmljoanccek44"; | |
31 const char kAppId5[] = "fbcmoldooppoahjhfflnmljoanccek55"; | |
32 const char kAppId6[] = "fbcmoldooppoahjhfflnmljoanccek66"; | |
33 const char kAppId7[] = "fbcmoldooppoahjhfflnmljoanccek77"; | |
34 const char kCoalescingKey1[] = "foo"; | |
35 const char kCoalescingKey2[] = "bar"; | |
36 const char kCoalescingKey3[] = "bat"; | |
37 const char kCoalescingKey4[] = "baz"; | |
38 const char kCoalescingKey5[] = "foobar"; | |
39 const char kCoalescingKey6[] = "fu"; | |
40 const char kCoalescingKey7[] = "meta"; | |
41 const char kNotificationId1[] = "fboilmbenheemaomgaeehigklolhkhnf/foo"; | |
42 const char kNotificationId2[] = "fbcmoldooppoahjhfflnmljoanccekpf/bar"; | |
43 const char kNotificationId3[] = "fbcmoldooppoahjhfflnmljoanccek33/bat"; | |
44 const char kNotificationId4[] = "fbcmoldooppoahjhfflnmljoanccek44/baz"; | |
45 const char kNotificationId5[] = "fbcmoldooppoahjhfflnmljoanccek55/foobar"; | |
46 const char kNotificationId6[] = "fbcmoldooppoahjhfflnmljoanccek66/fu"; | |
47 const char kNotificationId7[] = "fbcmoldooppoahjhfflnmljoanccek77/meta"; | |
48 | |
49 const unsigned long long kFakeCreationTime = 42; | |
dcheng
2013/01/18 21:56:13
Use int64 from basictypes.h.
Pete Williamson
2013/01/23 01:45:55
Done.
| |
50 | |
51 // Extract notification id from syncer::SyncData. | |
52 std::string GetNotificationId(const SyncData& sync_data) { | |
53 SyncedNotificationSpecifics specifics = sync_data.GetSpecifics(). | |
54 synced_notification(); | |
55 std::string notification_id = specifics. | |
56 coalesced_notification().id().app_id(); | |
57 notification_id += "/"; | |
58 notification_id += specifics.coalesced_notification().id().coalescing_key(); | |
59 return notification_id; | |
60 } | |
61 | |
62 // Stub out the NotificationUIManager for unit testing. | |
63 class StubNotificationUIManager : public NotificationUIManager { | |
64 public: | |
65 StubNotificationUIManager() {} | |
66 virtual ~StubNotificationUIManager() {} | |
67 | |
68 // Creates an initialized UI manager. | |
69 static NotificationUIManager* Create(PrefService* local_state) { | |
70 return NULL; | |
71 } | |
dcheng
2013/01/18 21:56:13
Not needed.
Pete Williamson
2013/01/23 01:45:55
Done.
| |
72 | |
73 // Adds a notification to be displayed. Virtual for unit test override. | |
74 virtual void Add(const Notification& notification, Profile* profile) {} | |
75 | |
76 // Removes any notifications matching the supplied ID, either currently | |
77 // displayed or in the queue. Returns true if anything was removed. | |
78 virtual bool CancelById(const std::string& notification_id) { | |
79 return false; | |
80 } | |
81 | |
82 // Removes notifications matching the |source_origin| (which could be an | |
83 // extension ID). Returns true if anything was removed. | |
84 virtual bool CancelAllBySourceOrigin(const GURL& source_origin) { | |
85 return false; | |
86 } | |
87 | |
88 // Removes notifications matching |profile|. Returns true if any were removed. | |
89 virtual bool CancelAllByProfile(Profile* profile) { | |
90 return false; | |
91 } | |
92 | |
93 // Cancels all pending notifications and closes anything currently showing. | |
94 // Used when the app is terminating. | |
95 virtual void CancelAll() {}; | |
96 | |
97 // Temporary, while we have two implementations of Notifications UI Managers. | |
98 // One is older BalloonCollection-based and uses renderers to show | |
99 // notifications, another delegates to the new MessageCenter and uses native | |
100 // UI widgets. | |
101 // TODO(dimich): remove these eventually. | |
102 static bool DelegatesToMessageCenter() { | |
103 return false; | |
104 } | |
dcheng
2013/01/18 21:56:13
I don't think you need this.
Pete Williamson
2013/01/23 01:45:55
Done.
| |
105 | |
106 private: | |
107 DISALLOW_COPY_AND_ASSIGN(StubNotificationUIManager); | |
108 }; | |
109 | |
110 // Dummy SyncChangeProcessor used to help review what SyncChanges are pushed | |
111 // back up to Sync. | |
112 class TestChangeProcessor : public SyncChangeProcessor { | |
113 public: | |
114 TestChangeProcessor() { } | |
115 virtual ~TestChangeProcessor() { } | |
116 | |
117 // Store a copy of all the changes passed in so we can examine them later. | |
118 virtual SyncError ProcessSyncChanges( | |
119 const tracked_objects::Location& from_here, | |
120 const SyncChangeList& change_list) { | |
121 change_map_.erase(change_map_.begin(), change_map_.end()); | |
122 for (SyncChangeList::const_iterator iter = change_list.begin(); | |
123 iter != change_list.end(); ++iter) { | |
124 // Put the data into the change tracking map. | |
125 change_map_[GetNotificationId(iter->sync_data())] = *iter; | |
126 } | |
127 | |
128 return SyncError(); | |
129 } | |
130 | |
131 size_t change_list_size() { return change_map_.size(); } | |
132 | |
133 bool ContainsId(const std::string& id) { | |
134 return change_map_.find(id) != change_map_.end(); | |
135 } | |
136 | |
137 SyncChange GetChangeById(const std::string& id) { | |
138 DCHECK(ContainsId(id)); | |
dcheng
2013/01/18 21:56:13
Don't DCHECK/CHECK in testing code. It causes the
Pete Williamson
2013/01/23 01:45:55
Done.
| |
139 return change_map_[id]; | |
140 } | |
141 | |
142 private: | |
143 // Track the changes received in ProcessSyncChanges. | |
144 std::map<std::string, SyncChange> change_map_; | |
145 | |
146 DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor); | |
147 }; | |
148 | |
149 class SyncChangeProcessorDelegate : public SyncChangeProcessor { | |
150 public: | |
151 explicit SyncChangeProcessorDelegate(SyncChangeProcessor* recipient) | |
152 : recipient_(recipient) { | |
153 DCHECK(recipient_); | |
154 } | |
155 virtual ~SyncChangeProcessorDelegate() {} | |
156 | |
157 // syncer::SyncChangeProcessor implementation. | |
158 virtual SyncError ProcessSyncChanges( | |
159 const tracked_objects::Location& from_here, | |
160 const SyncChangeList& change_list) OVERRIDE { | |
161 return recipient_->ProcessSyncChanges(from_here, change_list); | |
162 } | |
163 | |
164 private: | |
165 // The recipient of all sync changes. | |
166 SyncChangeProcessor* recipient_; | |
167 | |
168 DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate); | |
169 }; | |
170 | |
171 } // namespace | |
172 | |
173 class ChromeNotifierServiceTest : public testing::Test { | |
174 | |
dcheng
2013/01/18 21:56:13
Extra newline. Please remove.
Pete Williamson
2013/01/23 01:45:55
Done.
| |
175 public: | |
176 ChromeNotifierServiceTest() | |
177 : sync_processor_(new TestChangeProcessor), | |
178 sync_processor_delegate_(new SyncChangeProcessorDelegate( | |
179 sync_processor_.get())) {} | |
180 ~ChromeNotifierServiceTest() {} | |
181 | |
182 // Methods from testing::Test. | |
183 virtual void SetUp() {} | |
184 virtual void TearDown() {} | |
185 | |
186 TestChangeProcessor* processor() { return sync_processor_.get(); } | |
187 | |
188 scoped_ptr<SyncChangeProcessor> PassProcessor() { | |
189 return sync_processor_delegate_.PassAs<SyncChangeProcessor>(); | |
190 } | |
191 | |
192 SyncedNotification* CreateNotification(const std::string& message, | |
193 const std::string& app_id, | |
194 const std::string& coalescing_key, | |
195 const std::string& external_id) { | |
196 SyncData sync_data = CreateSyncData(message, app_id, coalescing_key, | |
197 external_id); | |
198 // Set enough fields in sync_data, including specifics, for our tests | |
199 // to pass. | |
200 return new SyncedNotification(sync_data); | |
201 } | |
202 | |
203 // Helper to create syncer::SyncChange. | |
204 static SyncChange CreateSyncChange( | |
205 SyncChange::SyncChangeType type, | |
206 SyncedNotification* notification) { | |
207 // Take control of the notification to clean it up after we create data | |
208 // out of it. | |
209 scoped_ptr<SyncedNotification> scoped_notif(notification); | |
dcheng
2013/01/18 21:56:13
Don't use notif as an abbreviation please.
Pete Williamson
2013/01/23 01:45:55
Done.
| |
210 return SyncChange( | |
211 FROM_HERE, | |
212 type, | |
213 ChromeNotifierService::CreateSyncDataFromNotification(*notification)); | |
214 } | |
215 | |
216 // Helper to create syncer::SyncData. | |
217 static SyncData CreateSyncData(const std::string& message, | |
218 const std::string& app_id, | |
219 const std::string& coalescing_key, | |
220 const std::string& external_id) { | |
221 | |
dcheng
2013/01/18 21:56:13
Remove extra newline.
Pete Williamson
2013/01/23 01:45:55
Done.
| |
222 // CreateLocalData makes a copy of this, so this can safely live | |
223 // on the stack. | |
dcheng
2013/01/18 21:56:13
Not a helpful comment. You can remove all the comm
Pete Williamson
2013/01/23 01:45:55
I removed the rest, but I think this one is import
| |
224 EntitySpecifics entity_specifics; | |
225 | |
226 // Get a writeable pointer to the sync notifications specifics inside the | |
227 // entity specifics. | |
228 SyncedNotificationSpecifics* specifics = | |
229 entity_specifics.mutable_synced_notification(); | |
230 | |
231 // Fill out as much of SyncedNotificationSpecifics as the tests need. | |
232 | |
233 // Set the layout type to Title and Subtext. | |
234 specifics->mutable_coalesced_notification()-> | |
235 mutable_render_info()-> | |
236 mutable_layout()-> | |
237 set_layout_type( | |
238 SyncedNotificationRenderInfo_Layout_LayoutType_TITLE_AND_SUBTEXT); | |
239 // Set the APP_ID field in the ID. | |
240 specifics->mutable_coalesced_notification()-> | |
241 mutable_id()-> | |
242 set_app_id(app_id); | |
243 // Set the coalescing key in the ID. | |
244 specifics->mutable_coalesced_notification()-> | |
245 mutable_id()-> | |
246 set_coalescing_key(coalescing_key); | |
247 // Set the title (of a title and subtext). | |
248 specifics->mutable_coalesced_notification()-> | |
249 mutable_render_info()-> | |
250 mutable_layout()-> | |
251 mutable_title_and_subtext_data()-> | |
252 set_title(message); | |
253 // Set the creation time. | |
254 specifics->mutable_coalesced_notification()-> | |
255 set_creation_time_msec(kFakeCreationTime); | |
256 // Set the first notification, including external ID. | |
257 specifics->mutable_coalesced_notification()-> | |
258 add_notification(); | |
259 specifics->mutable_coalesced_notification()-> | |
260 mutable_notification(0)->set_external_id(external_id); | |
261 | |
262 SyncData sync_data = SyncData::CreateLocalData( | |
263 "syncer::SYNCED_NOTIFICATIONS", | |
264 "ChromeNotifierServiceUnitTest", | |
265 entity_specifics); | |
266 | |
267 return sync_data; | |
268 } | |
269 | |
270 private: | |
271 scoped_ptr<TestChangeProcessor> sync_processor_; | |
dcheng
2013/01/18 21:56:13
Nit: SyncChangeProcessor
Pete Williamson
2013/01/23 01:45:55
I presume you mean to change the type to SyncChang
dcheng
2013/01/23 18:39:43
Yes. This removes the PassAs<> cast in PassProcess
Pete Williamson
2013/01/24 01:48:11
Done.
| |
272 scoped_ptr<SyncChangeProcessorDelegate> sync_processor_delegate_; | |
dcheng
2013/01/18 21:56:13
Nit: SyncChangeProcessor
Pete Williamson
2013/01/24 01:48:11
Done.
| |
273 | |
274 DISALLOW_COPY_AND_ASSIGN(ChromeNotifierServiceTest); | |
275 }; | |
276 | |
277 // Create a Notification, convert it to SyncData and convert it back. | |
278 TEST_F(ChromeNotifierServiceTest, NotificationToSyncDataToNotification) { | |
279 // TODO(petewil): Add more properties to this test. | |
280 scoped_ptr<SyncedNotification> notif1( | |
dcheng
2013/01/18 21:56:13
Ditto to not abbreviating notification.
Pete Williamson
2013/01/23 01:45:55
Done.
| |
281 CreateNotification("1", kAppId1, kCoalescingKey1, "11")); | |
282 SyncData sync_data = | |
283 ChromeNotifierService::CreateSyncDataFromNotification(*notif1); | |
284 scoped_ptr<SyncedNotification> notif2( | |
285 ChromeNotifierService::CreateNotificationFromSyncData(sync_data)); | |
286 EXPECT_TRUE(notif2.get()); | |
287 EXPECT_TRUE(notif1->Equals(*notif2)); | |
288 } | |
289 | |
290 // Model assocation: We have no local data, and no remote data. | |
291 TEST_F(ChromeNotifierServiceTest, ModelAssocBothEmpty) { | |
292 StubNotificationUIManager notification_manager; | |
293 ChromeNotifierService notifier(NULL, ¬ification_manager); | |
294 | |
295 notifier.MergeDataAndStartSyncing( | |
296 SYNCED_NOTIFICATIONS, | |
297 SyncDataList(), // Empty. | |
298 PassProcessor(), | |
299 scoped_ptr<SyncErrorFactory>(new SyncErrorFactoryMock())); | |
300 | |
301 EXPECT_EQ(0U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); | |
302 EXPECT_EQ(0U, processor()->change_list_size());} | |
dcheng
2013/01/18 21:56:13
Formatting.
Pete Williamson
2013/01/23 01:45:55
Done.
| |
303 | |
304 // Process sync changes when there is no local data. | |
305 TEST_F(ChromeNotifierServiceTest, ProcessSyncChangesEmptyModel) { | |
306 // We initially have no data. | |
307 StubNotificationUIManager notification_manager; | |
308 ChromeNotifierService notifier(NULL, ¬ification_manager); | |
309 | |
310 notifier.MergeDataAndStartSyncing( | |
311 SYNCED_NOTIFICATIONS, | |
312 SyncDataList(), | |
313 PassProcessor(), | |
314 scoped_ptr<SyncErrorFactory>(new SyncErrorFactoryMock())); | |
315 | |
316 // Set up a bunch of ADDs. | |
317 SyncChangeList changes; | |
318 changes.push_back(CreateSyncChange( | |
319 SyncChange::ACTION_ADD, CreateNotification( | |
320 "1", kAppId1, kCoalescingKey1, "11"))); | |
321 changes.push_back(CreateSyncChange( | |
322 SyncChange::ACTION_ADD, CreateNotification( | |
323 "2", kAppId2, kCoalescingKey2, "22"))); | |
324 changes.push_back(CreateSyncChange( | |
325 SyncChange::ACTION_ADD, CreateNotification( | |
326 "3", kAppId3, kCoalescingKey3, "33"))); | |
327 | |
328 notifier.ProcessSyncChanges(FROM_HERE, changes); | |
329 | |
330 EXPECT_EQ(3U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); | |
dcheng
2013/01/18 21:56:13
May want to consider verifying that the entries in
Pete Williamson
2013/01/23 01:45:55
added TODO(petewil):
| |
331 } | |
332 | |
333 // Model has some notifications, some of them are local only. Sync has some | |
334 // notifications. No items match up. | |
335 TEST_F(ChromeNotifierServiceTest, LocalRemoteBothNonEmptyNoOverlap) { | |
336 StubNotificationUIManager notification_manager; | |
337 ChromeNotifierService notifier(NULL, ¬ification_manager); | |
338 | |
339 // Create some local fake data. | |
340 scoped_ptr<SyncedNotification> n1(CreateNotification( | |
341 "1", kAppId1, kCoalescingKey1, "11")); | |
342 notifier.Add(n1.Pass()); | |
343 scoped_ptr<SyncedNotification> n2(CreateNotification( | |
344 "2", kAppId2, kCoalescingKey2, "22")); | |
345 notifier.Add(n2.Pass()); | |
346 scoped_ptr<SyncedNotification> n3(CreateNotification( | |
347 "3", kAppId3, kCoalescingKey3, "33")); | |
348 notifier.Add(n3.Pass()); | |
349 | |
350 // Create some remote fake data. | |
351 SyncDataList initial_data; | |
352 SyncData s1 = CreateSyncData("4", kAppId4, kCoalescingKey4, "44"); | |
dcheng
2013/01/18 21:56:13
Why is this the only one created differently?
Pete Williamson
2013/01/23 01:45:55
This is remote data. Today remote data and local
dcheng
2013/01/23 18:39:43
Sorry, I guess that was ambiguous. I mean line 352
Pete Williamson
2013/01/24 01:48:11
Done.
| |
353 initial_data.push_back(s1); | |
354 initial_data.push_back(CreateSyncData("5", kAppId5, kCoalescingKey5, "55")); | |
355 initial_data.push_back(CreateSyncData("6", kAppId6, kCoalescingKey6, "66")); | |
356 initial_data.push_back(CreateSyncData("7", kAppId7, kCoalescingKey7, "77")); | |
357 | |
358 // Merge the local and remote data. | |
359 notifier.MergeDataAndStartSyncing( | |
360 SYNCED_NOTIFICATIONS, | |
361 initial_data, | |
362 PassProcessor(), | |
363 scoped_ptr<SyncErrorFactory>(new SyncErrorFactoryMock())); | |
364 | |
365 // Ensure the array now has all local and remote notifications. | |
dcheng
2013/01/18 21:56:13
Ensure the local store has records of all local an
Pete Williamson
2013/01/23 01:45:55
Done.
| |
366 EXPECT_EQ(7U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); | |
367 for (SyncDataList::const_iterator iter = initial_data.begin(); | |
368 iter != initial_data.end(); ++iter) { | |
369 scoped_ptr<SyncedNotification> notif1( | |
370 ChromeNotifierService::CreateNotificationFromSyncData(*iter)); | |
371 // TODO(petewil): Revisit this when we add version info to notifications. | |
372 const std::string& id = notif1->get_notification_id(); | |
373 const SyncedNotification* notif2 = notifier.FindNotificationById(id); | |
374 EXPECT_TRUE(NULL != notif2); | |
375 EXPECT_TRUE(notif1->Equals(*notif2)); | |
376 } | |
377 EXPECT_TRUE(notifier.FindNotificationById(kNotificationId1)); | |
378 EXPECT_TRUE(notifier.FindNotificationById(kNotificationId2)); | |
379 EXPECT_TRUE(notifier.FindNotificationById(kNotificationId3)); | |
380 | |
381 // Verify the changes made it up to the remote service. | |
382 EXPECT_EQ(3U, processor()->change_list_size()); | |
383 EXPECT_TRUE(processor()->ContainsId(kNotificationId1)); | |
384 EXPECT_EQ(SyncChange::ACTION_ADD, processor()->GetChangeById( | |
385 kNotificationId1).change_type()); | |
386 EXPECT_FALSE(processor()->ContainsId(kNotificationId4)); | |
387 EXPECT_TRUE(processor()->ContainsId(kNotificationId3)); | |
388 EXPECT_EQ(SyncChange::ACTION_ADD, processor()->GetChangeById( | |
389 kNotificationId3).change_type()); | |
390 } | |
391 | |
392 // TODO(petewil): There are more tests to add, such as when an item in | |
393 // the local store matches up with one from the server, with and without | |
394 // merge conflicts. | |
OLD | NEW |