1// Copyright (c) 2013 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 <string>
6
7#include "base/memory/scoped_ptr.h"
8#include "base/message_loop/message_loop.h"
9#include "base/strings/utf_string_conversions.h"
10#include "chrome/browser/notifications/notification.h"
11#include "chrome/browser/notifications/notification_test_util.h"
12#include "chrome/browser/notifications/notification_ui_manager.h"
13#include "chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h"
14#include "chrome/browser/notifications/sync_notifier/synced_notification.h"
15#include "chrome/browser/profiles/profile.h"
16#include "content/public/browser/browser_thread.h"
17#include "content/public/test/test_browser_thread.h"
18#include "testing/gtest/include/gtest/gtest.h"
19#include "third_party/skia/include/core/SkBitmap.h"
20#include "ui/message_center/message_center_util.h"
21#include "ui/message_center/notification_types.h"
22
23using syncer::SyncData;
24using notifier::SyncedNotification;
25using sync_pb::EntitySpecifics;
26using sync_pb::SyncedNotificationSpecifics;
27
28namespace {
29const int kNotificationPriority = static_cast<int>(
30    message_center::LOW_PRIORITY);
31
32bool UseRichNotifications() {
33  return message_center::IsRichNotificationEnabled();
34}
35
36}  // namespace
37
38namespace notifier {
39
40// Stub out the NotificationUIManager for unit testing.
41class StubNotificationUIManager : public NotificationUIManager {
42 public:
43  StubNotificationUIManager()
44      : notification_(GURL(),
45                      GURL(),
46                      base::string16(),
47                      base::string16(),
48                      new MockNotificationDelegate("stub")) {}
49  virtual ~StubNotificationUIManager() {}
50
51  // Adds a notification to be displayed. Virtual for unit test override.
52  virtual void Add(const Notification& notification, Profile* profile)
53      OVERRIDE {
54    // Make a deep copy of the notification that we can inspect.
55    notification_ = notification;
56    profile_ = profile;
57  }
58
59  virtual bool Update(const Notification& notification, Profile* profile)
60      OVERRIDE {
61    // Make a deep copy of the notification that we can inspect.
62    notification_ = notification;
63    profile_ = profile;
64    return true;
65  }
66
67  // Returns true if any notifications match the supplied ID, either currently
68  // displayed or in the queue.
69  virtual const Notification* FindById(const std::string& id) const OVERRIDE {
70    return (notification_.id() == id) ? &notification_ : NULL;
71  }
72
73  // Removes any notifications matching the supplied ID, either currently
74  // displayed or in the queue.  Returns true if anything was removed.
75  virtual bool CancelById(const std::string& notification_id) OVERRIDE {
76    dismissed_id_ = notification_id;
77    return true;
78  }
79
80  virtual std::set<std::string> GetAllIdsByProfileAndSourceOrigin(
81      Profile* profile,
82      const GURL& source) OVERRIDE {
83    std::set<std::string> notification_ids;
84    if (source == notification_.origin_url() &&
85        profile->IsSameProfile(profile_))
86      notification_ids.insert(notification_.notification_id());
87    return notification_ids;
88  }
89
90  // Removes notifications matching the |source_origin| (which could be an
91  // extension ID). Returns true if anything was removed.
92  virtual bool CancelAllBySourceOrigin(const GURL& source_origin) OVERRIDE {
93    return false;
94  }
95
96  // Removes notifications matching |profile|. Returns true if any were removed.
97  virtual bool CancelAllByProfile(Profile* profile) OVERRIDE {
98    return false;
99  }
100
101  // Cancels all pending notifications and closes anything currently showing.
102  // Used when the app is terminating.
103  virtual void CancelAll() OVERRIDE {}
104
105  // Test hook to get the notification so we can check it
106  const Notification& notification() const { return notification_; }
107
108  // Test hook to check the ID of the last notification cancelled.
109  std::string& dismissed_id() { return dismissed_id_; }
110
111 private:
112  DISALLOW_COPY_AND_ASSIGN(StubNotificationUIManager);
113  Notification notification_;
114  Profile* profile_;
115  std::string dismissed_id_;
116};
117
118class SyncedNotificationTest : public testing::Test {
119 public:
120  SyncedNotificationTest()
121      : ui_thread_(content::BrowserThread::UI, &message_loop_) {}
122  virtual ~SyncedNotificationTest() {}
123
124  // Methods from testing::Test.
125
126  virtual void SetUp() OVERRIDE {
127    sync_data1_ = CreateSyncData(kTitle1, kText1, kIconUrl1, kImageUrl1,
128                                 kAppId1, kKey1, kUnread);
129    sync_data2_ = CreateSyncData(kTitle2, kText2, kIconUrl2, kImageUrl2,
130                                 kAppId2, kKey2, kUnread);
131    // Notification 3 will have the same ID as notification1, but different
132    // data inside.
133    sync_data3_ = CreateSyncData(kTitle3, kText3, kIconUrl3, kImageUrl3,
134                                 kAppId1, kKey1, kUnread);
135    // Notification 4 will be the same as 1, but the read state will be 'read'.
136    sync_data4_ = CreateSyncData(kTitle1, kText1, kIconUrl1, kImageUrl1,
137                                 kAppId1, kKey1, kDismissed);
138
139    notification1_.reset(new SyncedNotification(sync_data1_));
140    notification2_.reset(new SyncedNotification(sync_data2_));
141    notification3_.reset(new SyncedNotification(sync_data3_));
142    notification4_.reset(new SyncedNotification(sync_data4_));
143  }
144
145  virtual void TearDown() OVERRIDE {
146  }
147
148  virtual void AddButtonBitmaps(SyncedNotification* notification,
149                                unsigned int how_many) {
150    for (unsigned int i = 0; i < how_many; ++i) {
151      notification->button_bitmaps_.push_back(gfx::Image());
152    }
153  }
154
155  scoped_ptr<SyncedNotification> notification1_;
156  scoped_ptr<SyncedNotification> notification2_;
157  scoped_ptr<SyncedNotification> notification3_;
158  scoped_ptr<SyncedNotification> notification4_;
159  syncer::SyncData sync_data1_;
160  syncer::SyncData sync_data2_;
161  syncer::SyncData sync_data3_;
162  syncer::SyncData sync_data4_;
163
164 private:
165  base::MessageLoopForIO message_loop_;
166  content::TestBrowserThread ui_thread_;
167
168  DISALLOW_COPY_AND_ASSIGN(SyncedNotificationTest);
169};
170
171// test simple accessors
172
173TEST_F(SyncedNotificationTest, GetAppIdTest) {
174  std::string found_app_id = notification1_->GetAppId();
175  std::string expected_app_id(kAppId1);
176
177  EXPECT_EQ(found_app_id, expected_app_id);
178}
179
180TEST_F(SyncedNotificationTest, GetKeyTest) {
181  std::string found_key = notification1_->GetKey();
182  std::string expected_key(kKey1);
183
184  EXPECT_EQ(expected_key, found_key);
185}
186
187TEST_F(SyncedNotificationTest, GetTitleTest) {
188  std::string found_title = notification1_->GetTitle();
189  std::string expected_title(kTitle1);
190
191  EXPECT_EQ(expected_title, found_title);
192}
193
194TEST_F(SyncedNotificationTest, GetIconURLTest) {
195  std::string found_icon_url = notification1_->GetAppIconUrl().spec();
196  std::string expected_icon_url(kIconUrl1);
197
198  EXPECT_EQ(expected_icon_url, found_icon_url);
199}
200
201TEST_F(SyncedNotificationTest, GetReadStateTest) {
202  SyncedNotification::ReadState found_state1 =
203      notification1_->GetReadState();
204  SyncedNotification::ReadState expected_state1(SyncedNotification::kUnread);
205
206  EXPECT_EQ(expected_state1, found_state1);
207
208  SyncedNotification::ReadState found_state2 =
209      notification4_->GetReadState();
210  SyncedNotification::ReadState expected_state2(SyncedNotification::kDismissed);
211
212  EXPECT_EQ(expected_state2, found_state2);
213}
214
215// TODO(petewil): Improve ctor to pass in an image and type so this test can
216// pass on actual data.
217TEST_F(SyncedNotificationTest, GetImageURLTest) {
218  GURL found_image_url = notification1_->GetImageUrl();
219  GURL expected_image_url = GURL(kImageUrl1);
220
221  EXPECT_EQ(expected_image_url, found_image_url);
222}
223
224// TODO(petewil): test with a multi-line message
225TEST_F(SyncedNotificationTest, GetTextTest) {
226  std::string found_text = notification1_->GetText();
227  std::string expected_text(kText1);
228
229  EXPECT_EQ(expected_text, found_text);
230}
231
232TEST_F(SyncedNotificationTest, GetCreationTimeTest) {
233  uint64 found_time = notification1_->GetCreationTime();
234  EXPECT_EQ(kFakeCreationTime, found_time);
235}
236
237TEST_F(SyncedNotificationTest, GetPriorityTest) {
238  double found_priority = notification1_->GetPriority();
239  EXPECT_EQ(static_cast<double>(kNotificationPriority), found_priority);
240}
241
242TEST_F(SyncedNotificationTest, GetButtonCountTest) {
243  int found_button_count = notification1_->GetButtonCount();
244  EXPECT_EQ(2, found_button_count);
245}
246
247TEST_F(SyncedNotificationTest, GetNotificationCountTest) {
248  int found_notification_count = notification1_->GetNotificationCount();
249  EXPECT_EQ(3, found_notification_count);
250}
251
252TEST_F(SyncedNotificationTest, GetDefaultDestinationDataTest) {
253    std::string default_destination_title =
254        notification1_->GetDefaultDestinationTitle();
255    GURL default_destination_icon_url =
256        notification1_->GetDefaultDestinationIconUrl();
257    GURL default_destination_url =
258        notification1_->GetDefaultDestinationUrl();
259    EXPECT_EQ(std::string(kDefaultDestinationTitle), default_destination_title);
260    EXPECT_EQ(GURL(kDefaultDestinationIconUrl),
261              default_destination_icon_url);
262    EXPECT_EQ(GURL(kDefaultDestinationUrl), default_destination_url);
263}
264
265TEST_F(SyncedNotificationTest, GetButtonDataTest) {
266    std::string button_one_title = notification1_->GetButtonTitle(0);
267    GURL button_one_icon_url = notification1_->GetButtonIconUrl(0);
268    GURL button_one_url = notification1_->GetButtonUrl(0);
269    std::string button_two_title = notification1_->GetButtonTitle(1);
270    GURL button_two_icon_url = notification1_->GetButtonIconUrl(1);
271    GURL button_two_url = notification1_->GetButtonUrl(1);
272    EXPECT_EQ(std::string(kButtonOneTitle), button_one_title);
273    EXPECT_EQ(GURL(kButtonOneIconUrl), button_one_icon_url);
274    EXPECT_EQ(GURL(kButtonOneUrl), button_one_url);
275    EXPECT_EQ(std::string(kButtonTwoTitle), button_two_title);
276    EXPECT_EQ(GURL(kButtonTwoIconUrl), button_two_icon_url);
277    EXPECT_EQ(GURL(kButtonTwoUrl), button_two_url);
278}
279
280TEST_F(SyncedNotificationTest, ContainedNotificationTest) {
281  std::string notification_title1 =
282      notification1_->GetContainedNotificationTitle(0);
283  std::string notification_title2 =
284      notification1_->GetContainedNotificationTitle(1);
285  std::string notification_title3 =
286      notification1_->GetContainedNotificationTitle(2);
287  std::string notification_message1 =
288      notification1_->GetContainedNotificationMessage(0);
289  std::string notification_message2 =
290      notification1_->GetContainedNotificationMessage(1);
291  std::string notification_message3 =
292      notification1_->GetContainedNotificationMessage(2);
293
294  EXPECT_EQ(std::string(kContainedTitle1), notification_title1);
295  EXPECT_EQ(std::string(kContainedTitle2), notification_title2);
296  EXPECT_EQ(std::string(kContainedTitle3), notification_title3);
297  EXPECT_EQ(std::string(kContainedMessage1), notification_message1);
298  EXPECT_EQ(std::string(kContainedMessage2), notification_message2);
299  EXPECT_EQ(std::string(kContainedMessage3), notification_message3);
300}
301
302// test that EqualsIgnoringReadState works as we expect
303TEST_F(SyncedNotificationTest, EqualsIgnoringReadStateTest) {
304  EXPECT_TRUE(notification1_->EqualsIgnoringReadState(*notification1_));
305  EXPECT_TRUE(notification2_->EqualsIgnoringReadState(*notification2_));
306  EXPECT_FALSE(notification1_->EqualsIgnoringReadState(*notification2_));
307  EXPECT_FALSE(notification1_->EqualsIgnoringReadState(*notification3_));
308  EXPECT_TRUE(notification1_->EqualsIgnoringReadState(*notification4_));
309}
310
311TEST_F(SyncedNotificationTest, UpdateTest) {
312  scoped_ptr<SyncedNotification> notification5;
313  notification5.reset(new SyncedNotification(sync_data1_));
314
315  // update with the sync data from notification2, and ensure they are equal.
316  notification5->Update(sync_data2_);
317  EXPECT_TRUE(notification5->EqualsIgnoringReadState(*notification2_));
318  EXPECT_EQ(notification5->GetReadState(), notification2_->GetReadState());
319  EXPECT_FALSE(notification5->EqualsIgnoringReadState(*notification1_));
320}
321
322TEST_F(SyncedNotificationTest, ShowTest) {
323
324  if (!UseRichNotifications())
325    return;
326
327  StubNotificationUIManager notification_manager;
328
329  // Call the method under test using the pre-populated data.
330  notification1_->Show(&notification_manager, NULL, NULL);
331
332  const Notification notification = notification_manager.notification();
333
334  // Check the base fields of the notification.
335  EXPECT_EQ(message_center::NOTIFICATION_TYPE_IMAGE, notification.type());
336  EXPECT_EQ(std::string(kTitle1), UTF16ToUTF8(notification.title()));
337  EXPECT_EQ(std::string(kText1), UTF16ToUTF8(notification.message()));
338  EXPECT_EQ(std::string(kExpectedOriginUrl), notification.origin_url().spec());
339  EXPECT_EQ(std::string(kKey1), UTF16ToUTF8(notification.replace_id()));
340
341  EXPECT_EQ(kFakeCreationTime, notification.timestamp().ToDoubleT());
342  EXPECT_EQ(kNotificationPriority, notification.priority());
343}
344
345TEST_F(SyncedNotificationTest, DismissTest) {
346
347  if (!UseRichNotifications())
348    return;
349
350  StubNotificationUIManager notification_manager;
351
352  // Call the method under test using a dismissed notification.
353  notification4_->Show(&notification_manager, NULL, NULL);
354
355  EXPECT_EQ(std::string(kKey1), notification_manager.dismissed_id());
356}
357
358TEST_F(SyncedNotificationTest, AddBitmapToFetchQueueTest) {
359  scoped_ptr<SyncedNotification> notification6;
360  notification6.reset(new SyncedNotification(sync_data1_));
361
362  // Add two bitmaps to the queue.
363  notification6->AddBitmapToFetchQueue(GURL(kIconUrl1));
364  notification6->AddBitmapToFetchQueue(GURL(kIconUrl2));
365
366  EXPECT_EQ(2, notification6->active_fetcher_count_);
367  EXPECT_EQ(GURL(kIconUrl1), notification6->fetchers_[0]->url());
368  EXPECT_EQ(GURL(kIconUrl2), notification6->fetchers_[1]->url());
369
370  notification6->AddBitmapToFetchQueue(GURL(kIconUrl2));
371  EXPECT_EQ(2, notification6->active_fetcher_count_);
372}
373
374TEST_F(SyncedNotificationTest, OnFetchCompleteTest) {
375  if (!UseRichNotifications())
376    return;
377
378  StubNotificationUIManager notification_manager;
379
380  // Set up the internal state that FetchBitmaps() would have set.
381  notification1_->notification_manager_ = &notification_manager;
382
383  // Add the bitmaps to the queue for us to match up.
384  notification1_->AddBitmapToFetchQueue(GURL(kIconUrl1));
385  notification1_->AddBitmapToFetchQueue(GURL(kImageUrl1));
386  notification1_->AddBitmapToFetchQueue(GURL(kButtonOneIconUrl));
387  notification1_->AddBitmapToFetchQueue(GURL(kButtonTwoIconUrl));
388
389  EXPECT_EQ(4, notification1_->active_fetcher_count_);
390
391  // Put some realistic looking bitmap data into the url_fetcher.
392  SkBitmap bitmap;
393
394  // Put a real bitmap into "bitmap".  2x2 bitmap of green 32 bit pixels.
395  bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
396  bitmap.allocPixels();
397  bitmap.eraseColor(SK_ColorGREEN);
398
399  // Allocate the button_bitmaps_ array as the calling function normally would.
400  AddButtonBitmaps(notification1_.get(), 2);
401
402  notification1_->OnFetchComplete(GURL(kIconUrl1), &bitmap);
403  EXPECT_EQ(3, notification1_->active_fetcher_count_);
404
405  // When we call OnFetchComplete on the last bitmap, show should be called.
406  notification1_->OnFetchComplete(GURL(kImageUrl1), &bitmap);
407  EXPECT_EQ(2, notification1_->active_fetcher_count_);
408
409  notification1_->OnFetchComplete(GURL(kButtonOneIconUrl), &bitmap);
410  EXPECT_EQ(1, notification1_->active_fetcher_count_);
411
412  notification1_->OnFetchComplete(GURL(kButtonTwoIconUrl), &bitmap);
413  EXPECT_EQ(0, notification1_->active_fetcher_count_);
414
415  // Since we check Show() thoroughly in its own test, we only check cursorily.
416  EXPECT_EQ(message_center::NOTIFICATION_TYPE_IMAGE,
417            notification_manager.notification().type());
418  EXPECT_EQ(std::string(kTitle1),
419            UTF16ToUTF8(notification_manager.notification().title()));
420  EXPECT_EQ(std::string(kText1),
421            UTF16ToUTF8(notification_manager.notification().message()));
422
423  // TODO(petewil): Check that the bitmap in the notification is what we expect.
424  // This fails today, the type info is different.
425  // EXPECT_TRUE(gfx::BitmapsAreEqual(
426  //     image, notification1_->GetAppIconBitmap()));
427}
428
429TEST_F(SyncedNotificationTest, QueueBitmapFetchJobsTest) {
430  if (!UseRichNotifications())
431    return;
432
433  StubNotificationUIManager notification_manager;
434
435  notification1_->QueueBitmapFetchJobs(&notification_manager, NULL, NULL);
436
437  // There should be 4 urls in the queue, icon, image, and two buttons.
438  EXPECT_EQ(4, notification1_->active_fetcher_count_);
439}
440
441// TODO(petewil): Add a test for a notification being read and or deleted.
442
443}  // namespace notifier
444