Home | History | Annotate | Download | only in notifications
      1 // Copyright (c) 2011 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/chromeos/notifications/desktop_notifications_unittest.h"
      6 
      7 #include "base/stringprintf.h"
      8 #include "base/utf_string_conversions.h"
      9 #include "chrome/browser/prefs/browser_prefs.h"
     10 #include "chrome/browser/prefs/pref_service.h"
     11 #include "content/common/desktop_notification_messages.h"
     12 
     13 namespace chromeos {
     14 
     15 // static
     16 std::string DesktopNotificationsTest::log_output_;
     17 
     18 class MockNotificationUI : public BalloonCollectionImpl::NotificationUI {
     19  public:
     20   virtual void Add(Balloon* balloon) {}
     21   virtual bool Update(Balloon* balloon) { return false; }
     22   virtual void Remove(Balloon* balloon) {}
     23   virtual void Show(Balloon* balloon) {}
     24   virtual void ResizeNotification(Balloon* balloon,
     25                                   const gfx::Size& size) {}
     26   virtual void SetActiveView(BalloonViewImpl* view) {}
     27 };
     28 
     29 MockBalloonCollection::MockBalloonCollection() {
     30   set_notification_ui(new MockNotificationUI());
     31 }
     32 
     33 void MockBalloonCollection::Add(const Notification& notification,
     34                                 Profile* profile) {
     35   // Swap in a logging proxy for the purpose of logging calls that
     36   // would be made into javascript, then pass this down to the
     37   // balloon collection.
     38   Notification test_notification(
     39       notification.origin_url(),
     40       notification.content_url(),
     41       notification.display_source(),
     42       notification.replace_id(),
     43       new LoggingNotificationProxy(notification.notification_id()));
     44   BalloonCollectionImpl::Add(test_notification, profile);
     45 }
     46 
     47 Balloon* MockBalloonCollection::MakeBalloon(const Notification& notification,
     48                                             Profile* profile) {
     49   // Start with a normal balloon but mock out the view.
     50   Balloon* balloon = BalloonCollectionImpl::MakeBalloon(notification, profile);
     51   balloon->set_view(new MockBalloonView(balloon));
     52   balloons_.insert(balloon);
     53   return balloon;
     54 }
     55 
     56 void MockBalloonCollection::OnBalloonClosed(Balloon* source) {
     57   balloons_.erase(source);
     58   BalloonCollectionImpl::OnBalloonClosed(source);
     59 }
     60 
     61 int MockBalloonCollection::UppermostVerticalPosition() {
     62   int min = 0;
     63   std::set<Balloon*>::iterator iter;
     64   for (iter = balloons_.begin(); iter != balloons_.end(); ++iter) {
     65     int pos = (*iter)->GetPosition().y();
     66     if (iter == balloons_.begin() || pos < min)
     67       min = pos;
     68   }
     69   return min;
     70 }
     71 
     72 DesktopNotificationsTest::DesktopNotificationsTest()
     73     : ui_thread_(BrowserThread::UI, &message_loop_) {
     74 }
     75 
     76 DesktopNotificationsTest::~DesktopNotificationsTest() {
     77 }
     78 
     79 void DesktopNotificationsTest::SetUp() {
     80   browser::RegisterLocalState(&local_state_);
     81   profile_.reset(new TestingProfile());
     82   balloon_collection_ = new MockBalloonCollection();
     83   ui_manager_.reset(new NotificationUIManager(&local_state_));
     84   ui_manager_->Initialize(balloon_collection_);
     85   balloon_collection_->set_space_change_listener(ui_manager_.get());
     86   service_.reset(new DesktopNotificationService(profile(), ui_manager_.get()));
     87   log_output_.clear();
     88 }
     89 
     90 void DesktopNotificationsTest::TearDown() {
     91   service_.reset(NULL);
     92   ui_manager_.reset(NULL);
     93   profile_.reset(NULL);
     94 }
     95 
     96 DesktopNotificationHostMsg_Show_Params
     97 DesktopNotificationsTest::StandardTestNotification() {
     98   DesktopNotificationHostMsg_Show_Params params;
     99   params.notification_id = 0;
    100   params.origin = GURL("http://www.google.com");
    101   params.is_html = false;
    102   params.icon_url = GURL("/icon.png");
    103   params.title = ASCIIToUTF16("Title");
    104   params.body = ASCIIToUTF16("Text");
    105   params.direction = WebKit::WebTextDirectionDefault;
    106   return params;
    107 }
    108 
    109 TEST_F(DesktopNotificationsTest, TestShow) {
    110   DesktopNotificationHostMsg_Show_Params params = StandardTestNotification();
    111   params.notification_id = 1;
    112   EXPECT_TRUE(service_->ShowDesktopNotification(
    113       params, 0, 0, DesktopNotificationService::PageNotification));
    114 
    115   MessageLoopForUI::current()->RunAllPending();
    116   EXPECT_EQ(1, balloon_collection_->count());
    117 
    118   DesktopNotificationHostMsg_Show_Params params2;
    119   params2.origin = GURL("http://www.google.com");
    120   params2.is_html = true;
    121   params2.contents_url = GURL("http://www.google.com/notification.html");
    122   params2.notification_id = 2;
    123 
    124   EXPECT_TRUE(service_->ShowDesktopNotification(
    125       params2, 0, 0, DesktopNotificationService::PageNotification));
    126   MessageLoopForUI::current()->RunAllPending();
    127   EXPECT_EQ(2, balloon_collection_->count());
    128 
    129   EXPECT_EQ("notification displayed\n"
    130             "notification displayed\n",
    131             log_output_);
    132 }
    133 
    134 TEST_F(DesktopNotificationsTest, TestClose) {
    135   DesktopNotificationHostMsg_Show_Params params = StandardTestNotification();
    136   params.notification_id = 1;
    137 
    138   // Request a notification; should open a balloon.
    139   EXPECT_TRUE(service_->ShowDesktopNotification(
    140       params, 0, 0, DesktopNotificationService::PageNotification));
    141   MessageLoopForUI::current()->RunAllPending();
    142   EXPECT_EQ(1, balloon_collection_->count());
    143 
    144   // Close all the open balloons.
    145   std::set<Balloon*> balloons = balloon_collection_->balloons();
    146   std::set<Balloon*>::iterator iter;
    147   for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
    148     (*iter)->OnClose(true);
    149   }
    150 
    151   // Verify that the balloon collection is now empty.
    152   EXPECT_EQ(0, balloon_collection_->count());
    153 
    154   EXPECT_EQ("notification displayed\n"
    155             "notification closed by user\n",
    156             log_output_);
    157 }
    158 
    159 TEST_F(DesktopNotificationsTest, TestCancel) {
    160   int process_id = 0;
    161   int route_id = 0;
    162   int notification_id = 1;
    163 
    164   DesktopNotificationHostMsg_Show_Params params = StandardTestNotification();
    165   params.notification_id = notification_id;
    166 
    167   // Request a notification; should open a balloon.
    168   EXPECT_TRUE(service_->ShowDesktopNotification(
    169       params, process_id, route_id,
    170       DesktopNotificationService::PageNotification));
    171   MessageLoopForUI::current()->RunAllPending();
    172   EXPECT_EQ(1, balloon_collection_->count());
    173 
    174   // Cancel the same notification
    175   service_->CancelDesktopNotification(process_id,
    176                                       route_id,
    177                                       notification_id);
    178   MessageLoopForUI::current()->RunAllPending();
    179   // Verify that the balloon collection is now empty.
    180   EXPECT_EQ(0, balloon_collection_->count());
    181 
    182   EXPECT_EQ("notification displayed\n"
    183             "notification closed by script\n",
    184             log_output_);
    185 }
    186 
    187 TEST_F(DesktopNotificationsTest, TestManyNotifications) {
    188   int process_id = 0;
    189   int route_id = 0;
    190 
    191   // Request lots of identical notifications.
    192   const int kLotsOfToasts = 20;
    193   for (int id = 1; id <= kLotsOfToasts; ++id) {
    194     SCOPED_TRACE(base::StringPrintf("Creation loop: id=%d", id));
    195     DesktopNotificationHostMsg_Show_Params params = StandardTestNotification();
    196     params.notification_id = id;
    197     EXPECT_TRUE(service_->ShowDesktopNotification(
    198         params, process_id, route_id,
    199         DesktopNotificationService::PageNotification));
    200   }
    201   MessageLoopForUI::current()->RunAllPending();
    202 
    203   // Build up an expected log of what should be happening.
    204   std::string expected_log;
    205   for (int i = 0; i < kLotsOfToasts; ++i) {
    206     expected_log.append("notification displayed\n");
    207   }
    208 
    209   EXPECT_EQ(kLotsOfToasts, balloon_collection_->count());
    210   EXPECT_EQ(expected_log, log_output_);
    211 
    212   // Cancel half of the notifications from the start
    213   int id;
    214   int cancelled = kLotsOfToasts / 2;
    215   for (id = 1;
    216        id <= cancelled;
    217        ++id) {
    218     SCOPED_TRACE(base::StringPrintf("Cancel half of notifications: id=%d", id));
    219     service_->CancelDesktopNotification(process_id, route_id, id);
    220     MessageLoopForUI::current()->RunAllPending();
    221     expected_log.append("notification closed by script\n");
    222     EXPECT_EQ(kLotsOfToasts - id,
    223               balloon_collection_->count());
    224     EXPECT_EQ(expected_log, log_output_);
    225   }
    226 
    227   // Now cancel the rest.  It should empty the balloon space.
    228   for (; id <= kLotsOfToasts; ++id) {
    229     SCOPED_TRACE(base::StringPrintf("Cancel loop: id=%d", id));
    230     service_->CancelDesktopNotification(process_id, route_id, id);
    231     expected_log.append("notification closed by script\n");
    232     MessageLoopForUI::current()->RunAllPending();
    233     EXPECT_EQ(expected_log, log_output_);
    234   }
    235 
    236   // Verify that the balloon collection is now empty.
    237   EXPECT_EQ(0, balloon_collection_->count());
    238 }
    239 
    240 TEST_F(DesktopNotificationsTest, TestEarlyDestruction) {
    241   // Create some toasts and then prematurely delete the notification service,
    242   // just to make sure nothing crashes/leaks.
    243   for (int id = 0; id <= 3; ++id) {
    244     SCOPED_TRACE(base::StringPrintf("Show Text loop: id=%d", id));
    245 
    246     EXPECT_TRUE(service_->ShowDesktopNotification(
    247         StandardTestNotification(), 0, 0,
    248         DesktopNotificationService::PageNotification));
    249   }
    250   service_.reset(NULL);
    251 }
    252 
    253 TEST_F(DesktopNotificationsTest, TestUserInputEscaping) {
    254   // Create a test script with some HTML; assert that it doesn't get into the
    255   // data:// URL that's produced for the balloon.
    256   DesktopNotificationHostMsg_Show_Params params = StandardTestNotification();
    257   params.title = ASCIIToUTF16("<script>window.alert('uh oh');</script>");
    258   params.body = ASCIIToUTF16("<i>this text is in italics</i>");
    259   params.notification_id = 1;
    260   EXPECT_TRUE(service_->ShowDesktopNotification(
    261       params, 0, 0, DesktopNotificationService::PageNotification));
    262 
    263   MessageLoopForUI::current()->RunAllPending();
    264   EXPECT_EQ(1, balloon_collection_->count());
    265   Balloon* balloon = (*balloon_collection_->balloons().begin());
    266   GURL data_url = balloon->notification().content_url();
    267   EXPECT_EQ(std::string::npos, data_url.spec().find("<script>"));
    268   EXPECT_EQ(std::string::npos, data_url.spec().find("<i>"));
    269 }
    270 
    271 }  // namespace chromeos
    272