summaryrefslogtreecommitdiffstats
path: root/chrome/browser/notifications
diff options
context:
space:
mode:
authorjohnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-11 17:40:31 +0000
committerjohnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-11 17:40:31 +0000
commit8b7135d6aa44d68307bfb7ff5e6441fcf1c5231b (patch)
tree39f24246b28900218dded1cfe462ebe400ee3a6d /chrome/browser/notifications
parent4a3dab23cadf0cbe68bd82c06fd970ae8b1d7e10 (diff)
downloadchromium_src-8b7135d6aa44d68307bfb7ff5e6441fcf1c5231b.zip
chromium_src-8b7135d6aa44d68307bfb7ff5e6441fcf1c5231b.tar.gz
chromium_src-8b7135d6aa44d68307bfb7ff5e6441fcf1c5231b.tar.bz2
Unit tests for desktop notifications.
* Allow non-windows BalloonCollections to create non-viewable balloons (so that non-viewing aspects are cross-platform testable) * Makes NotificationObjectProxy overridable by a mock object which logs instead of calls to JS * Move Layout code from private to protected in BalloonCollection to enable verification of position code. BUG=none TEST=these Review URL: http://codereview.chromium.org/371041 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31670 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/notifications')
-rw-r--r--chrome/browser/notifications/balloon_collection.h32
-rw-r--r--chrome/browser/notifications/balloon_collection_linux.cc6
-rw-r--r--chrome/browser/notifications/balloon_collection_mac.mm6
-rw-r--r--chrome/browser/notifications/desktop_notifications_unittest.cc250
-rw-r--r--chrome/browser/notifications/desktop_notifications_unittest.h138
-rw-r--r--chrome/browser/notifications/notification_object_proxy.h11
6 files changed, 414 insertions, 29 deletions
diff --git a/chrome/browser/notifications/balloon_collection.h b/chrome/browser/notifications/balloon_collection.h
index 8241f19..918f993 100644
--- a/chrome/browser/notifications/balloon_collection.h
+++ b/chrome/browser/notifications/balloon_collection.h
@@ -70,18 +70,6 @@ class BalloonCollectionImpl : public BalloonCollection,
virtual void OnBalloonClosed(Balloon* source);
protected:
- // Creates a new balloon. Overridable by unit tests. The caller is
- // responsible for freeing the pointer returned.
- virtual Balloon* MakeBalloon(const Notification& notification,
- Profile* profile);
-
- private:
- // The number of balloons being displayed.
- int count() const { return balloons_.size(); }
-
- // Adjusts the positions of the balloons (e.g., when one is closed).
- void PositionBalloons(bool is_reposition);
-
// Calculates layout values for the balloons including
// the scaling, the max/min sizes, and the upper left corner of each.
class Layout {
@@ -92,10 +80,10 @@ class BalloonCollectionImpl : public BalloonCollection,
void OnDisplaySettingsChanged();
// TODO(johnnyg): Scale the size to account for the system font factor.
- int min_balloon_width() const { return kBalloonMinWidth; }
- int max_balloon_width() const { return kBalloonMaxWidth; }
- int min_balloon_height() const { return kBalloonMinHeight; }
- int max_balloon_height() const { return kBalloonMaxHeight; }
+ static int min_balloon_width() { return kBalloonMinWidth; }
+ static int max_balloon_width() { return kBalloonMaxWidth; }
+ static int min_balloon_height() { return kBalloonMinHeight; }
+ static int max_balloon_height() { return kBalloonMaxHeight; }
// Returns both the total space available and the maximum
// allowed per balloon.
@@ -140,6 +128,18 @@ class BalloonCollectionImpl : public BalloonCollection,
DISALLOW_COPY_AND_ASSIGN(Layout);
};
+ // Creates a new balloon. Overridable by unit tests. The caller is
+ // responsible for freeing the pointer returned.
+ virtual Balloon* MakeBalloon(const Notification& notification,
+ Profile* profile);
+
+ private:
+ // The number of balloons being displayed.
+ int count() const { return balloons_.size(); }
+
+ // Adjusts the positions of the balloons (e.g., when one is closed).
+ void PositionBalloons(bool is_reposition);
+
// Non-owned pointer to an object listening for space changes.
BalloonSpaceChangeListener* space_change_listener_;
diff --git a/chrome/browser/notifications/balloon_collection_linux.cc b/chrome/browser/notifications/balloon_collection_linux.cc
index 49ed818..e1439f2 100644
--- a/chrome/browser/notifications/balloon_collection_linux.cc
+++ b/chrome/browser/notifications/balloon_collection_linux.cc
@@ -4,14 +4,12 @@
#include "chrome/browser/notifications/balloon_collection.h"
-#include "base/gfx/size.h"
#include "base/logging.h"
Balloon* BalloonCollectionImpl::MakeBalloon(const Notification& notification,
Profile* profile) {
- // TODO(johnnyg): http://crbug.com/23954. Part of future Linux support.
- NOTIMPLEMENTED();
- return NULL;
+ // TODO(johnnyg): http://crbug.com/23954. Hook up to views.
+ return new Balloon(notification, profile, this);
}
bool BalloonCollectionImpl::Layout::RefreshSystemMetrics() {
diff --git a/chrome/browser/notifications/balloon_collection_mac.mm b/chrome/browser/notifications/balloon_collection_mac.mm
index 6197b49..2bff15e 100644
--- a/chrome/browser/notifications/balloon_collection_mac.mm
+++ b/chrome/browser/notifications/balloon_collection_mac.mm
@@ -4,14 +4,12 @@
#include "chrome/browser/notifications/balloon_collection.h"
-#include "base/gfx/size.h"
#include "base/logging.h"
Balloon* BalloonCollectionImpl::MakeBalloon(const Notification& notification,
Profile* profile) {
- // TODO(johnnyg): http://crbug.com/23066. Part of future Mac support.
- NOTIMPLEMENTED();
- return NULL;
+ // TODO(johnnyg): http://crbug.com/23066. Hook up to views.
+ return new Balloon(notification, profile, this);
}
bool BalloonCollectionImpl::Layout::RefreshSystemMetrics() {
diff --git a/chrome/browser/notifications/desktop_notifications_unittest.cc b/chrome/browser/notifications/desktop_notifications_unittest.cc
new file mode 100644
index 0000000..819e70f
--- /dev/null
+++ b/chrome/browser/notifications/desktop_notifications_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/notifications/desktop_notifications_unittest.h"
+
+// static
+const int MockBalloonCollection::kMockBalloonSpace = 5;
+
+// static
+std::string DesktopNotificationsTest::log_output_;
+
+void LoggingNotificationProxy::Display() {
+ DesktopNotificationsTest::log("notification displayed\n");
+}
+
+void LoggingNotificationProxy::Error() {
+ DesktopNotificationsTest::log("notification error\n");
+}
+
+void LoggingNotificationProxy::Close(bool by_user) {
+ if (by_user)
+ DesktopNotificationsTest::log("notification closed by user\n");
+ else
+ DesktopNotificationsTest::log("notification closed by script\n");
+}
+
+void MockBalloonCollection::Add(const Notification& notification,
+ Profile* profile) {
+ // Swap in the logging proxy for the purpose of logging calls that
+ // would be made into javascript, then pass this down to the
+ // balloon collection.
+ Notification test_notification(notification.origin_url(),
+ notification.content_url(),
+ log_proxy_.get());
+ BalloonCollectionImpl::Add(test_notification, profile);
+}
+
+bool MockBalloonCollection::Remove(const Notification& notification) {
+ Notification test_notification(notification.origin_url(),
+ notification.content_url(),
+ log_proxy_.get());
+ return BalloonCollectionImpl::Remove(test_notification);
+}
+
+Balloon* MockBalloonCollection::MakeBalloon(const Notification& notification,
+ Profile* profile) {
+ // Start with a normal balloon but mock out the view.
+ Balloon* balloon = BalloonCollectionImpl::MakeBalloon(notification, profile);
+ balloon->set_view(new MockBalloonView(balloon));
+ balloons_.insert(balloon);
+ return balloon;
+}
+
+void MockBalloonCollection::OnBalloonClosed(Balloon* source) {
+ balloons_.erase(source);
+ BalloonCollectionImpl::OnBalloonClosed(source);
+}
+
+int MockBalloonCollection::UppermostVerticalPosition() {
+ int min = 0;
+ std::set<Balloon*>::iterator iter;
+ for (iter = balloons_.begin(); iter != balloons_.end(); ++iter) {
+ int pos = (*iter)->position().y();
+ if (iter == balloons_.begin() || pos < min)
+ min = pos;
+ }
+ return min;
+}
+
+DesktopNotificationsTest::DesktopNotificationsTest() :
+ ui_thread_(ChromeThread::UI, &message_loop_) {
+}
+
+DesktopNotificationsTest::~DesktopNotificationsTest() {
+}
+
+void DesktopNotificationsTest::SetUp() {
+ profile_.reset(new TestingProfile());
+ balloon_collection_ = new MockBalloonCollection();
+ ui_manager_.reset(new NotificationUIManager());
+ ui_manager_->Initialize(balloon_collection_);
+ balloon_collection_->set_space_change_listener(ui_manager_.get());
+ service_.reset(new DesktopNotificationService(profile(), ui_manager_.get()));
+ log_output_.clear();
+}
+
+void DesktopNotificationsTest::TearDown() {
+ profile_.reset(NULL);
+ ui_manager_.reset(NULL);
+ service_.reset(NULL);
+}
+
+TEST_F(DesktopNotificationsTest, TestShow) {
+ EXPECT_TRUE(service_->ShowDesktopNotificationText(
+ GURL("http://www.google.com"),
+ GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"),
+ 0, 0, DesktopNotificationService::PageNotification, 1));
+ MessageLoopForUI::current()->RunAllPending();
+ EXPECT_EQ(1, balloon_collection_->count());
+
+ EXPECT_TRUE(service_->ShowDesktopNotification(
+ GURL("http://www.google.com"),
+ GURL("http://www.google.com/notification.html"),
+ 0, 0, DesktopNotificationService::PageNotification, 2));
+ MessageLoopForUI::current()->RunAllPending();
+ EXPECT_EQ(2, balloon_collection_->count());
+
+ EXPECT_EQ("notification displayed\n"
+ "notification displayed\n",
+ log_output_);
+}
+
+TEST_F(DesktopNotificationsTest, TestClose) {
+ // Request a notification; should open a balloon.
+ EXPECT_TRUE(service_->ShowDesktopNotificationText(
+ GURL("http://www.google.com"),
+ GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"),
+ 0, 0, DesktopNotificationService::PageNotification, 1));
+ MessageLoopForUI::current()->RunAllPending();
+ EXPECT_EQ(1, balloon_collection_->count());
+
+ // Close all the open balloons.
+ std::set<Balloon*> balloons = balloon_collection_->balloons();
+ std::set<Balloon*>::iterator iter;
+ for (iter = balloons.begin(); iter != balloons.end(); ++iter) {
+ (*iter)->OnClose(true);
+ }
+
+ // Verify that the balloon collection is now empty.
+ EXPECT_EQ(0, balloon_collection_->count());
+
+ EXPECT_EQ("notification displayed\n"
+ "notification closed by user\n",
+ log_output_);
+}
+
+TEST_F(DesktopNotificationsTest, TestCancel) {
+ int process_id = 0;
+ int route_id = 0;
+ int notification_id = 1;
+ // Request a notification; should open a balloon.
+ EXPECT_TRUE(service_->ShowDesktopNotificationText(
+ GURL("http://www.google.com"),
+ GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"),
+ process_id, route_id, DesktopNotificationService::PageNotification,
+ notification_id));
+ MessageLoopForUI::current()->RunAllPending();
+ EXPECT_EQ(1, balloon_collection_->count());
+
+ // Cancel the same notification
+ service_->CancelDesktopNotification(process_id,
+ route_id,
+ notification_id);
+ MessageLoopForUI::current()->RunAllPending();
+ // Verify that the balloon collection is now empty.
+ EXPECT_EQ(0, balloon_collection_->count());
+
+ EXPECT_EQ("notification displayed\n"
+ "notification closed by script\n",
+ log_output_);
+}
+
+#if defined(OS_WIN)
+TEST_F(DesktopNotificationsTest, TestPositioning) {
+ std::string expected_log;
+ // Create some toasts. After each but the first, make sure there
+ // is a minimum separation between the toasts.
+ int last_top = 0;
+ for (int id = 0; id <= 3; ++id) {
+ EXPECT_TRUE(service_->ShowDesktopNotificationText(
+ GURL("http://www.google.com"),
+ GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"),
+ 0, 0, DesktopNotificationService::PageNotification, id));
+ expected_log.append("notification displayed\n");
+ int top = balloon_collection_->UppermostVerticalPosition();
+ if (id > 0)
+ EXPECT_LE(top, last_top - balloon_collection_->MinHeight());
+ last_top = top;
+ }
+
+ EXPECT_EQ(expected_log, log_output_);
+}
+#endif
+
+TEST_F(DesktopNotificationsTest, TestQueueing) {
+ int process_id = 0;
+ int route_id = 0;
+
+ // Request lots of identical notifications.
+ const int kLotsOfToasts = 20;
+ for (int id = 1; id <= kLotsOfToasts; ++id) {
+ EXPECT_TRUE(service_->ShowDesktopNotificationText(
+ GURL("http://www.google.com"),
+ GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"),
+ process_id, route_id,
+ DesktopNotificationService::PageNotification, id));
+ }
+ MessageLoopForUI::current()->RunAllPending();
+
+ // Build up an expected log of what should be happening.
+ std::string expected_log;
+ for (int i = 0; i < balloon_collection_->max_balloon_count(); ++i) {
+ expected_log.append("notification displayed\n");
+ }
+
+ // The max number that our balloon collection can hold should be
+ // shown.
+ EXPECT_EQ(balloon_collection_->max_balloon_count(),
+ balloon_collection_->count());
+ EXPECT_EQ(expected_log, log_output_);
+
+ // Cancel the notifications from the start; the balloon space should
+ // remain full.
+ int id;
+ for (id = 1;
+ id <= kLotsOfToasts - balloon_collection_->max_balloon_count();
+ ++id) {
+ service_->CancelDesktopNotification(process_id, route_id, id);
+ MessageLoopForUI::current()->RunAllPending();
+ expected_log.append("notification closed by script\n");
+ expected_log.append("notification displayed\n");
+ EXPECT_EQ(balloon_collection_->max_balloon_count(),
+ balloon_collection_->count());
+ EXPECT_EQ(expected_log, log_output_);
+ }
+
+ // Now cancel the rest. It should empty the balloon space.
+ for (; id <= kLotsOfToasts; ++id) {
+ service_->CancelDesktopNotification(process_id, route_id, id);
+ expected_log.append("notification closed by script\n");
+ MessageLoopForUI::current()->RunAllPending();
+ EXPECT_EQ(expected_log, log_output_);
+ }
+
+ // Verify that the balloon collection is now empty.
+ EXPECT_EQ(0, balloon_collection_->count());
+}
+
+TEST_F(DesktopNotificationsTest, TestEarlyDestruction) {
+ // Create some toasts and then prematurely delete the notification service,
+ // just to make sure nothing crashes/leaks.
+ for (int id = 0; id <= 3; ++id) {
+ EXPECT_TRUE(service_->ShowDesktopNotificationText(
+ GURL("http://www.google.com"),
+ GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"),
+ 0, 0, DesktopNotificationService::PageNotification, id));
+ }
+ service_.reset(NULL);
+}
diff --git a/chrome/browser/notifications/desktop_notifications_unittest.h b/chrome/browser/notifications/desktop_notifications_unittest.h
new file mode 100644
index 0000000..91c65c8
--- /dev/null
+++ b/chrome/browser/notifications/desktop_notifications_unittest.h
@@ -0,0 +1,138 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NOTIFICATIONS_DESKTOP_NOTIFICATIONS_UNITTEST_H_
+#define CHROME_BROWSER_NOTIFICATIONS_DESKTOP_NOTIFICATIONS_UNITTEST_H_
+
+#include <set>
+#include <string>
+
+#include "base/message_loop.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/notifications/balloon.h"
+#include "chrome/browser/notifications/balloon_collection.h"
+#include "chrome/browser/notifications/desktop_notification_service.h"
+#include "chrome/browser/notifications/notification.h"
+#include "chrome/browser/notifications/notification_object_proxy.h"
+#include "chrome/browser/notifications/notification_ui_manager.h"
+#include "chrome/browser/notifications/notifications_prefs_cache.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/api/public/WebNotificationPresenter.h"
+
+// Mock implementation of Javascript object proxy which logs events that
+// would have been fired on it.
+class LoggingNotificationProxy : public NotificationObjectProxy {
+ public:
+ LoggingNotificationProxy() :
+ NotificationObjectProxy(0, 0, 0, false) {}
+
+ // NotificationObjectProxy override
+ virtual void Display();
+ virtual void Error();
+ virtual void Close(bool by_user);
+};
+
+// Test version of a balloon view which doesn't do anything
+// viewable, but does know how to close itself the same as a regular
+// BalloonView.
+class MockBalloonView : public BalloonView {
+ public:
+ explicit MockBalloonView(Balloon * balloon) :
+ balloon_(balloon) {}
+ void Show(Balloon* balloon) {}
+ void RepositionToBalloon() {}
+ void Close(bool by_user) { balloon_->OnClose(by_user); }
+
+ private:
+ // Non-owned pointer.
+ Balloon* balloon_;
+};
+
+// Test version of the balloon collection which counts the number
+// of notifications that are added to it.
+class MockBalloonCollection : public BalloonCollectionImpl {
+ public:
+ MockBalloonCollection() :
+ log_proxy_(new LoggingNotificationProxy()) {}
+
+ // Our mock collection has an area large enough for a fixed number
+ // of balloons.
+ static const int kMockBalloonSpace;
+ int max_balloon_count() const { return kMockBalloonSpace; }
+
+ // BalloonCollectionImpl overrides
+ virtual void Add(const Notification& notification,
+ Profile* profile);
+ virtual bool Remove(const Notification& notification);
+ virtual bool HasSpace() const { return count() < kMockBalloonSpace; }
+ virtual Balloon* MakeBalloon(const Notification& notification,
+ Profile* profile);
+ virtual void OnBalloonClosed(Balloon* source);
+
+ // Number of balloons being shown.
+ std::set<Balloon*>& balloons() { return balloons_; }
+ int count() const { return balloons_.size(); }
+
+ // Returns the highest y-coordinate of all the balloons in the collection.
+ int UppermostVerticalPosition();
+
+ // Returns the minimum height of a balloon.
+ int MinHeight() { return Layout::min_balloon_height(); }
+
+ private:
+ std::set<Balloon*> balloons_;
+ scoped_refptr<LoggingNotificationProxy> log_proxy_;
+};
+
+class DesktopNotificationsTest : public testing::Test {
+ public:
+ DesktopNotificationsTest();
+ ~DesktopNotificationsTest();
+
+ static void log(const std::string& message) {
+ log_output_.append(message);
+ }
+
+ Profile* profile() { return profile_.get(); }
+
+ protected:
+ // testing::Test overrides
+ virtual void SetUp();
+ virtual void TearDown();
+
+ void AllowOrigin(const GURL& origin) {
+ service_->GrantPermission(origin);
+ }
+
+ void DenyOrigin(const GURL& origin) {
+ service_->DenyPermission(origin);
+ }
+
+ int HasPermission(const GURL& origin) {
+ return service_->prefs_cache()->HasPermission(origin);
+ }
+
+ // Create a message loop to allow notifications code to post tasks,
+ // and a thread so that notifications code runs on the expected thread.
+ MessageLoopForUI message_loop_;
+ ChromeThread ui_thread_;
+
+ // Test profile.
+ scoped_ptr<TestingProfile> profile_;
+
+ // Mock balloon collection -- owned by the NotificationUIManager
+ MockBalloonCollection* balloon_collection_;
+
+ // Real UI manager.
+ scoped_ptr<NotificationUIManager> ui_manager_;
+
+ // Real DesktopNotificationService
+ scoped_ptr<DesktopNotificationService> service_;
+
+ // Contains the cumulative output of the unit test.
+ static std::string log_output_;
+};
+
+#endif
diff --git a/chrome/browser/notifications/notification_object_proxy.h b/chrome/browser/notifications/notification_object_proxy.h
index ff71bc3..1ff17aa 100644
--- a/chrome/browser/notifications/notification_object_proxy.h
+++ b/chrome/browser/notifications/notification_object_proxy.h
@@ -24,15 +24,15 @@ class NotificationObjectProxy :
int notification_id, bool worker);
// To be called when the desktop notification is actually shown.
- void Display();
+ virtual void Display();
// To be called when the desktop notification cannot be shown due to an
// error.
- void Error();
+ virtual void Error();
// To be called when the desktop notification is closed. If closed by a
// user explicitly (as opposed to timeout/script), |by_user| should be true.
- void Close(bool by_user);
+ virtual void Close(bool by_user);
// Compares two proxies by ids to decide if they are equal.
bool IsSame(const NotificationObjectProxy& other) const {
@@ -42,11 +42,12 @@ class NotificationObjectProxy :
worker_ == other.worker_);
}
- private:
+ protected:
friend class base::RefCountedThreadSafe<NotificationObjectProxy>;
- ~NotificationObjectProxy() {}
+ virtual ~NotificationObjectProxy() {}
+ private:
// Called on UI thread to schedule a message for sending.
void DeliverMessage(IPC::Message* message);