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/extensions/extension_test_message_listener.h"
13 #include "chrome/browser/ui/extensions/application_launch.h"
14 #include "content/public/test/test_utils.h"
15 #include "extensions/browser/extension_prefs.h"
16 #include "extensions/browser/extension_registry.h"
17 #include "extensions/browser/extension_system.h"
18 #include "extensions/browser/test_extension_registry_observer.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 virtual ~NotificationObserver() {
40 message_center_
->RemoveObserver(this);
43 bool HasReceivedNotification() const {
44 return received_notifications_
.find(target_notification_id_
) !=
45 received_notifications_
.end();
48 // Runs the message loop and returns true if a notification is received.
49 // Immediately returns true if a notification has already been received.
50 bool WaitForNotification() {
51 if (HasReceivedNotification())
55 content::RunMessageLoop();
57 return HasReceivedNotification();
61 // MessageCenterObserver implementation:
62 virtual void OnNotificationAdded(
63 const std::string
& notification_id
) OVERRIDE
{
64 received_notifications_
.insert(notification_id
);
66 if (waiting_
&& HasReceivedNotification())
67 base::MessageLoopForUI::current()->Quit();
70 message_center::MessageCenter
* message_center_
;
71 std::set
<std::string
> received_notifications_
;
72 std::string target_notification_id_
;
78 class ExtensionStorageMonitorTest
: public ExtensionBrowserTest
{
80 ExtensionStorageMonitorTest() : storage_monitor_(NULL
) {}
83 // ExtensionBrowserTest overrides:
84 virtual void SetUpOnMainThread() OVERRIDE
{
85 ExtensionBrowserTest::SetUpOnMainThread();
90 ExtensionStorageMonitor
* monitor() {
91 CHECK(storage_monitor_
);
92 return storage_monitor_
;
95 int64
GetInitialExtensionThreshold() {
96 CHECK(storage_monitor_
);
97 return storage_monitor_
->initial_extension_threshold_
;
100 int64
GetInitialEphemeralThreshold() {
101 CHECK(storage_monitor_
);
102 return storage_monitor_
->initial_ephemeral_threshold_
;
105 void DisableForInstalledExtensions() {
106 CHECK(storage_monitor_
);
107 storage_monitor_
->enable_for_all_extensions_
= false;
110 const Extension
* InitWriteDataApp() {
111 base::FilePath path
= test_data_dir_
.AppendASCII(kWriteDataApp
);
112 const Extension
* extension
= InstallExtension(path
, 1);
113 EXPECT_TRUE(extension
);
117 const Extension
* InitWriteDataEphemeralApp() {
118 // The threshold for installed extensions should be higher than ephemeral
120 storage_monitor_
->initial_extension_threshold_
=
121 storage_monitor_
->initial_ephemeral_threshold_
* 4;
123 base::FilePath path
= test_data_dir_
.AppendASCII(kWriteDataApp
);
124 const Extension
* extension
= InstallEphemeralAppWithSourceAndFlags(
125 path
, 1, Manifest::INTERNAL
, Extension::NO_FLAGS
);
126 EXPECT_TRUE(extension
);
130 std::string
GetNotificationId(const std::string
& extension_id
) {
131 return monitor()->GetNotificationId(extension_id
);
134 bool IsStorageNotificationEnabled(const std::string
& extension_id
) {
135 return monitor()->IsStorageNotificationEnabled(extension_id
);
138 int64
GetNextStorageThreshold(const std::string
& extension_id
) {
139 return monitor()->GetNextStorageThreshold(extension_id
);
142 void WriteBytesExpectingNotification(const Extension
* extension
,
144 int64 previous_threshold
= GetNextStorageThreshold(extension
->id());
145 WriteBytes(extension
, num_bytes
, true);
146 EXPECT_GT(GetNextStorageThreshold(extension
->id()), previous_threshold
);
149 void WriteBytesNotExpectingNotification(const Extension
* extension
,
151 WriteBytes(extension
, num_bytes
, false);
154 void SimulateUninstallDialogAccept() {
155 // Ensure the uninstall dialog was shown and fake an accept.
156 ASSERT_TRUE(monitor()->uninstall_dialog_
.get());
157 monitor()->ExtensionUninstallAccepted();
161 void InitStorageMonitor() {
162 storage_monitor_
= ExtensionStorageMonitor::Get(profile());
163 ASSERT_TRUE(storage_monitor_
);
165 // Override thresholds so that we don't have to write a huge amount of data
166 // to trigger notifications in these tests.
167 storage_monitor_
->enable_for_all_extensions_
= true;
168 storage_monitor_
->initial_extension_threshold_
= kInitialUsageThreshold
;
169 storage_monitor_
->initial_ephemeral_threshold_
= kInitialUsageThreshold
;
171 // To ensure storage events are dispatched from QuotaManager immediately.
172 storage_monitor_
->observer_rate_
= 0;
175 // Write a number of bytes to persistent storage.
176 void WriteBytes(const Extension
* extension
,
178 bool expected_notification
) {
179 ExtensionTestMessageListener
launched_listener("launched", true);
180 ExtensionTestMessageListener
write_complete_listener(
181 "write_complete", false);
182 NotificationObserver
notification_observer(
183 GetNotificationId(extension
->id()));
185 OpenApplication(AppLaunchParams(
186 profile(), extension
, LAUNCH_CONTAINER_NONE
, NEW_WINDOW
));
187 ASSERT_TRUE(launched_listener
.WaitUntilSatisfied());
189 // Instruct the app to write |num_bytes| of data.
190 launched_listener
.Reply(base::IntToString(num_bytes
));
191 ASSERT_TRUE(write_complete_listener
.WaitUntilSatisfied());
193 if (expected_notification
) {
194 EXPECT_TRUE(notification_observer
.WaitForNotification());
196 base::RunLoop().RunUntilIdle();
197 EXPECT_FALSE(notification_observer
.HasReceivedNotification());
201 ExtensionStorageMonitor
* storage_monitor_
;
204 // Control - No notifications should be shown if usage remains under the
206 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, UnderThreshold
) {
207 const Extension
* extension
= InitWriteDataApp();
208 ASSERT_TRUE(extension
);
209 WriteBytesNotExpectingNotification(extension
, 1);
212 // Ensure a notification is shown when usage reaches the first threshold.
213 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, ExceedInitialThreshold
) {
214 const Extension
* extension
= InitWriteDataApp();
215 ASSERT_TRUE(extension
);
216 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
219 // Ensure a notification is shown when usage immediately exceeds double the
221 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, DoubleInitialThreshold
) {
222 const Extension
* extension
= InitWriteDataApp();
223 ASSERT_TRUE(extension
);
224 WriteBytesExpectingNotification(extension
,
225 GetInitialExtensionThreshold() * 2);
228 // Ensure that notifications are not fired if the next threshold has not been
230 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, ThrottleNotifications
) {
231 const Extension
* extension
= InitWriteDataApp();
232 ASSERT_TRUE(extension
);
234 // Exceed the first threshold.
235 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
237 // Stay within the next threshold.
238 WriteBytesNotExpectingNotification(extension
, 1);
241 // Verify that notifications are disabled when the user clicks the action button
242 // in the notification.
243 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, UserDisabledNotifications
) {
244 const Extension
* extension
= InitWriteDataApp();
245 ASSERT_TRUE(extension
);
246 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
248 EXPECT_TRUE(IsStorageNotificationEnabled(extension
->id()));
250 // Fake clicking the notification button to disable notifications.
251 message_center::MessageCenter::Get()->ClickOnNotificationButton(
252 GetNotificationId(extension
->id()),
253 ExtensionStorageMonitor::BUTTON_DISABLE_NOTIFICATION
);
255 EXPECT_FALSE(IsStorageNotificationEnabled(extension
->id()));
257 // Expect to receive no further notifications when usage continues to
259 int64 next_threshold
= GetNextStorageThreshold(extension
->id());
260 int64 next_data_size
= next_threshold
- GetInitialExtensionThreshold();
261 ASSERT_GT(next_data_size
, 0);
263 WriteBytesNotExpectingNotification(extension
, next_data_size
);
266 // Verify that thresholds for ephemeral apps are reset when they are
267 // promoted to regular installed apps.
268 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, EphemeralAppLowUsage
) {
269 const Extension
* extension
= InitWriteDataEphemeralApp();
270 ASSERT_TRUE(extension
);
271 WriteBytesExpectingNotification(extension
, GetInitialEphemeralThreshold());
273 // Store the number of bytes until the next threshold is reached.
274 int64 next_threshold
= GetNextStorageThreshold(extension
->id());
275 int64 next_data_size
= next_threshold
- GetInitialEphemeralThreshold();
276 ASSERT_GT(next_data_size
, 0);
277 EXPECT_GE(GetInitialExtensionThreshold(), next_threshold
);
279 // Promote the ephemeral app.
280 ExtensionService
* service
=
281 ExtensionSystem::Get(profile())->extension_service();
282 service
->PromoteEphemeralApp(extension
, false);
284 // The next threshold should now be equal to the initial threshold for
285 // extensions (which is higher than the initial threshold for ephemeral apps).
286 EXPECT_EQ(GetInitialExtensionThreshold(),
287 GetNextStorageThreshold(extension
->id()));
289 // Since the threshold was increased, a notification should not be
291 WriteBytesNotExpectingNotification(extension
, next_data_size
);
294 // Verify that thresholds for ephemeral apps are not reset when they are
295 // promoted to regular installed apps if their usage is higher than the initial
296 // threshold for installed extensions.
297 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, EphemeralAppWithHighUsage
) {
298 const Extension
* extension
= InitWriteDataEphemeralApp();
299 ASSERT_TRUE(extension
);
300 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
301 int64 saved_next_threshold
= GetNextStorageThreshold(extension
->id());
303 // Promote the ephemeral app.
304 ExtensionService
* service
=
305 ExtensionSystem::Get(profile())->extension_service();
306 service
->PromoteEphemeralApp(extension
, false);
308 // The next threshold should not have changed.
309 EXPECT_EQ(saved_next_threshold
, GetNextStorageThreshold(extension
->id()));
312 // Ensure that monitoring is disabled for installed extensions if
313 // |enable_for_all_extensions_| is false. This test can be removed if monitoring
314 // is eventually enabled for all extensions.
315 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
,
316 DisableForInstalledExtensions
) {
317 DisableForInstalledExtensions();
319 const Extension
* extension
= InitWriteDataApp();
320 ASSERT_TRUE(extension
);
321 WriteBytesNotExpectingNotification(extension
, GetInitialExtensionThreshold());
324 // Verify that notifications are disabled when the user clicks the action button
325 // in the notification.
326 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, UninstallExtension
) {
327 const Extension
* extension
= InitWriteDataApp();
328 ASSERT_TRUE(extension
);
329 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
331 // Fake clicking the notification button to uninstall.
332 message_center::MessageCenter::Get()->ClickOnNotificationButton(
333 GetNotificationId(extension
->id()),
334 ExtensionStorageMonitor::BUTTON_UNINSTALL
);
336 // Also fake accepting the uninstall.
337 TestExtensionRegistryObserver
observer(ExtensionRegistry::Get(profile()),
339 SimulateUninstallDialogAccept();
340 observer
.WaitForExtensionUninstalled();
343 } // namespace extensions