1 // Copyright 2014 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.
7 #include "base/run_loop.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "chrome/browser/extensions/extension_browsertest.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/extension_storage_monitor.h"
12 #include "chrome/browser/ui/extensions/application_launch.h"
13 #include "content/public/test/test_utils.h"
14 #include "extensions/browser/extension_prefs.h"
15 #include "extensions/browser/extension_registry.h"
16 #include "extensions/browser/extension_system.h"
17 #include "extensions/browser/test_extension_registry_observer.h"
18 #include "extensions/test/extension_test_message_listener.h"
19 #include "ui/message_center/message_center.h"
20 #include "ui/message_center/message_center_observer.h"
22 namespace extensions {
26 const int kInitialUsageThreshold = 500;
28 const char kWriteDataApp[] = "storage_monitor/write_data";
30 class NotificationObserver : public message_center::MessageCenterObserver {
32 explicit NotificationObserver(const std::string& target_notification)
33 : message_center_(message_center::MessageCenter::Get()),
34 target_notification_id_(target_notification),
36 message_center_->AddObserver(this);
39 ~NotificationObserver() override { message_center_->RemoveObserver(this); }
41 bool HasReceivedNotification() const {
42 return received_notifications_.find(target_notification_id_) !=
43 received_notifications_.end();
46 // Runs the message loop and returns true if a notification is received.
47 // Immediately returns true if a notification has already been received.
48 bool WaitForNotification() {
49 if (HasReceivedNotification())
53 content::RunMessageLoop();
55 return HasReceivedNotification();
59 // MessageCenterObserver implementation:
60 void OnNotificationAdded(const std::string& notification_id) override {
61 received_notifications_.insert(notification_id);
63 if (waiting_ && HasReceivedNotification())
64 base::MessageLoopForUI::current()->Quit();
67 message_center::MessageCenter* message_center_;
68 std::set<std::string> received_notifications_;
69 std::string target_notification_id_;
75 class ExtensionStorageMonitorTest : public ExtensionBrowserTest {
77 ExtensionStorageMonitorTest() : storage_monitor_(NULL) {}
80 // ExtensionBrowserTest overrides:
81 void SetUpOnMainThread() override {
82 ExtensionBrowserTest::SetUpOnMainThread();
87 ExtensionStorageMonitor* monitor() {
88 CHECK(storage_monitor_);
89 return storage_monitor_;
92 int64 GetInitialExtensionThreshold() {
93 CHECK(storage_monitor_);
94 return storage_monitor_->initial_extension_threshold_;
97 int64 GetInitialEphemeralThreshold() {
98 CHECK(storage_monitor_);
99 return storage_monitor_->initial_ephemeral_threshold_;
102 void DisableForInstalledExtensions() {
103 CHECK(storage_monitor_);
104 storage_monitor_->enable_for_all_extensions_ = false;
107 const Extension* InitWriteDataApp() {
108 base::FilePath path = test_data_dir_.AppendASCII(kWriteDataApp);
109 const Extension* extension = InstallExtension(path, 1);
110 EXPECT_TRUE(extension);
114 const Extension* InitWriteDataEphemeralApp() {
115 // The threshold for installed extensions should be higher than ephemeral
117 storage_monitor_->initial_extension_threshold_ =
118 storage_monitor_->initial_ephemeral_threshold_ * 4;
120 base::FilePath path = test_data_dir_.AppendASCII(kWriteDataApp);
121 const Extension* extension = InstallEphemeralAppWithSourceAndFlags(
122 path, 1, Manifest::INTERNAL, Extension::NO_FLAGS);
123 EXPECT_TRUE(extension);
127 std::string GetNotificationId(const std::string& extension_id) {
128 return monitor()->GetNotificationId(extension_id);
131 bool IsStorageNotificationEnabled(const std::string& extension_id) {
132 return monitor()->IsStorageNotificationEnabled(extension_id);
135 int64 GetNextStorageThreshold(const std::string& extension_id) {
136 return monitor()->GetNextStorageThreshold(extension_id);
139 void WriteBytesExpectingNotification(const Extension* extension,
141 int64 previous_threshold = GetNextStorageThreshold(extension->id());
142 WriteBytes(extension, num_bytes, true);
143 EXPECT_GT(GetNextStorageThreshold(extension->id()), previous_threshold);
146 void WriteBytesNotExpectingNotification(const Extension* extension,
148 WriteBytes(extension, num_bytes, false);
151 void SimulateUninstallDialogAccept() {
152 // Ensure the uninstall dialog was shown and fake an accept.
153 ASSERT_TRUE(monitor()->uninstall_dialog_.get());
154 monitor()->ExtensionUninstallAccepted();
158 void InitStorageMonitor() {
159 storage_monitor_ = ExtensionStorageMonitor::Get(profile());
160 ASSERT_TRUE(storage_monitor_);
162 // Override thresholds so that we don't have to write a huge amount of data
163 // to trigger notifications in these tests.
164 storage_monitor_->enable_for_all_extensions_ = true;
165 storage_monitor_->initial_extension_threshold_ = kInitialUsageThreshold;
166 storage_monitor_->initial_ephemeral_threshold_ = kInitialUsageThreshold;
168 // To ensure storage events are dispatched from QuotaManager immediately.
169 storage_monitor_->observer_rate_ = base::TimeDelta();
172 // Write a number of bytes to persistent storage.
173 void WriteBytes(const Extension* extension,
175 bool expected_notification) {
176 ExtensionTestMessageListener launched_listener("launched", true);
177 ExtensionTestMessageListener write_complete_listener(
178 "write_complete", false);
179 NotificationObserver notification_observer(
180 GetNotificationId(extension->id()));
182 OpenApplication(AppLaunchParams(
183 profile(), extension, LAUNCH_CONTAINER_NONE, NEW_WINDOW));
184 ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
186 // Instruct the app to write |num_bytes| of data.
187 launched_listener.Reply(base::IntToString(num_bytes));
188 ASSERT_TRUE(write_complete_listener.WaitUntilSatisfied());
190 if (expected_notification) {
191 EXPECT_TRUE(notification_observer.WaitForNotification());
193 base::RunLoop().RunUntilIdle();
194 EXPECT_FALSE(notification_observer.HasReceivedNotification());
198 ExtensionStorageMonitor* storage_monitor_;
201 // Control - No notifications should be shown if usage remains under the
203 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UnderThreshold) {
204 const Extension* extension = InitWriteDataApp();
205 ASSERT_TRUE(extension);
206 WriteBytesNotExpectingNotification(extension, 1);
209 // Ensure a notification is shown when usage reaches the first threshold.
210 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, ExceedInitialThreshold) {
211 const Extension* extension = InitWriteDataApp();
212 ASSERT_TRUE(extension);
213 WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold());
216 // Ensure a notification is shown when usage immediately exceeds double the
218 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, DoubleInitialThreshold) {
219 const Extension* extension = InitWriteDataApp();
220 ASSERT_TRUE(extension);
221 WriteBytesExpectingNotification(extension,
222 GetInitialExtensionThreshold() * 2);
225 // Ensure that notifications are not fired if the next threshold has not been
227 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, ThrottleNotifications) {
228 const Extension* extension = InitWriteDataApp();
229 ASSERT_TRUE(extension);
231 // Exceed the first threshold.
232 WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold());
234 // Stay within the next threshold.
235 WriteBytesNotExpectingNotification(extension, 1);
238 // Verify that notifications are disabled when the user clicks the action button
239 // in the notification.
240 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UserDisabledNotifications) {
241 const Extension* extension = InitWriteDataApp();
242 ASSERT_TRUE(extension);
243 WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold());
245 EXPECT_TRUE(IsStorageNotificationEnabled(extension->id()));
247 // Fake clicking the notification button to disable notifications.
248 message_center::MessageCenter::Get()->ClickOnNotificationButton(
249 GetNotificationId(extension->id()),
250 ExtensionStorageMonitor::BUTTON_DISABLE_NOTIFICATION);
252 EXPECT_FALSE(IsStorageNotificationEnabled(extension->id()));
254 // Expect to receive no further notifications when usage continues to
256 int64 next_threshold = GetNextStorageThreshold(extension->id());
257 int64 next_data_size = next_threshold - GetInitialExtensionThreshold();
258 ASSERT_GT(next_data_size, 0);
260 WriteBytesNotExpectingNotification(extension, next_data_size);
263 // Verify that thresholds for ephemeral apps are reset when they are
264 // promoted to regular installed apps.
265 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, EphemeralAppLowUsage) {
266 const Extension* extension = InitWriteDataEphemeralApp();
267 ASSERT_TRUE(extension);
268 WriteBytesExpectingNotification(extension, GetInitialEphemeralThreshold());
270 // Store the number of bytes until the next threshold is reached.
271 int64 next_threshold = GetNextStorageThreshold(extension->id());
272 int64 next_data_size = next_threshold - GetInitialEphemeralThreshold();
273 ASSERT_GT(next_data_size, 0);
274 EXPECT_GE(GetInitialExtensionThreshold(), next_threshold);
276 // Promote the ephemeral app.
277 ExtensionService* service =
278 ExtensionSystem::Get(profile())->extension_service();
279 service->PromoteEphemeralApp(extension, false);
281 // The next threshold should now be equal to the initial threshold for
282 // extensions (which is higher than the initial threshold for ephemeral apps).
283 EXPECT_EQ(GetInitialExtensionThreshold(),
284 GetNextStorageThreshold(extension->id()));
286 // Since the threshold was increased, a notification should not be
288 WriteBytesNotExpectingNotification(extension, next_data_size);
291 // Verify that thresholds for ephemeral apps are not reset when they are
292 // promoted to regular installed apps if their usage is higher than the initial
293 // threshold for installed extensions.
294 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, EphemeralAppWithHighUsage) {
295 const Extension* extension = InitWriteDataEphemeralApp();
296 ASSERT_TRUE(extension);
297 WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold());
298 int64 saved_next_threshold = GetNextStorageThreshold(extension->id());
300 // Promote the ephemeral app.
301 ExtensionService* service =
302 ExtensionSystem::Get(profile())->extension_service();
303 service->PromoteEphemeralApp(extension, false);
305 // The next threshold should not have changed.
306 EXPECT_EQ(saved_next_threshold, GetNextStorageThreshold(extension->id()));
309 // Ensure that monitoring is disabled for installed extensions if
310 // |enable_for_all_extensions_| is false. This test can be removed if monitoring
311 // is eventually enabled for all extensions.
312 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest,
313 DisableForInstalledExtensions) {
314 DisableForInstalledExtensions();
316 const Extension* extension = InitWriteDataApp();
317 ASSERT_TRUE(extension);
318 WriteBytesNotExpectingNotification(extension, GetInitialExtensionThreshold());
321 // Verify that notifications are disabled when the user clicks the action button
322 // in the notification.
323 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UninstallExtension) {
324 const Extension* extension = InitWriteDataApp();
325 ASSERT_TRUE(extension);
326 WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold());
328 // Fake clicking the notification button to uninstall.
329 message_center::MessageCenter::Get()->ClickOnNotificationButton(
330 GetNotificationId(extension->id()),
331 ExtensionStorageMonitor::BUTTON_UNINSTALL);
333 // Also fake accepting the uninstall.
334 TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()),
336 SimulateUninstallDialogAccept();
337 observer.WaitForExtensionUninstalled();
340 } // namespace extensions