Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(383)

Side by Side Diff: chrome/browser/notifier/chrome_notifier_service_unittest.cc

Issue 11745024: Synced Notification Sync Change Processor (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: SyncedNotifications - first round of CR comment fixes. Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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, &notification_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, &notification_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, &notification_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.
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698