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/app_launch_params.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 "extensions/common/constants.h"
20 #include "extensions/test/extension_test_message_listener.h"
21 #include "ui/message_center/message_center.h"
22 #include "ui/message_center/message_center_observer.h"
24 namespace extensions
{
28 const int kInitialUsageThreshold
= 500;
30 const char kWriteDataApp
[] = "storage_monitor/write_data";
32 class NotificationObserver
: public message_center::MessageCenterObserver
{
34 explicit NotificationObserver(const std::string
& target_notification
)
35 : message_center_(message_center::MessageCenter::Get()),
36 target_notification_id_(target_notification
),
38 message_center_
->AddObserver(this);
41 ~NotificationObserver() override
{ 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 void OnNotificationAdded(const std::string
& notification_id
) override
{
63 received_notifications_
.insert(notification_id
);
65 if (waiting_
&& HasReceivedNotification())
66 base::MessageLoopForUI::current()->Quit();
69 message_center::MessageCenter
* message_center_
;
70 std::set
<std::string
> received_notifications_
;
71 std::string target_notification_id_
;
77 class ExtensionStorageMonitorTest
: public ExtensionBrowserTest
{
79 ExtensionStorageMonitorTest() : storage_monitor_(NULL
) {}
82 // ExtensionBrowserTest overrides:
83 void SetUpOnMainThread() override
{
84 ExtensionBrowserTest::SetUpOnMainThread();
89 ExtensionStorageMonitor
* monitor() {
90 CHECK(storage_monitor_
);
91 return storage_monitor_
;
94 int64
GetInitialExtensionThreshold() {
95 CHECK(storage_monitor_
);
96 return storage_monitor_
->initial_extension_threshold_
;
99 int64
GetInitialEphemeralThreshold() {
100 CHECK(storage_monitor_
);
101 return storage_monitor_
->initial_ephemeral_threshold_
;
104 void DisableForInstalledExtensions() {
105 CHECK(storage_monitor_
);
106 storage_monitor_
->enable_for_all_extensions_
= false;
109 const Extension
* InitWriteDataApp() {
110 base::FilePath path
= test_data_dir_
.AppendASCII(kWriteDataApp
);
111 const Extension
* extension
= InstallExtension(path
, 1);
112 EXPECT_TRUE(extension
);
116 const Extension
* InitWriteDataEphemeralApp() {
117 // The threshold for installed extensions should be higher than ephemeral
119 storage_monitor_
->initial_extension_threshold_
=
120 storage_monitor_
->initial_ephemeral_threshold_
* 4;
122 base::FilePath path
= test_data_dir_
.AppendASCII(kWriteDataApp
);
123 const Extension
* extension
= InstallEphemeralAppWithSourceAndFlags(
124 path
, 1, Manifest::INTERNAL
, Extension::NO_FLAGS
);
125 EXPECT_TRUE(extension
);
129 std::string
GetNotificationId(const std::string
& extension_id
) {
130 return monitor()->GetNotificationId(extension_id
);
133 bool IsStorageNotificationEnabled(const std::string
& extension_id
) {
134 return monitor()->IsStorageNotificationEnabled(extension_id
);
137 int64
GetNextStorageThreshold(const std::string
& extension_id
) {
138 return monitor()->GetNextStorageThreshold(extension_id
);
141 void WriteBytesExpectingNotification(const Extension
* extension
,
143 int64 previous_threshold
= GetNextStorageThreshold(extension
->id());
144 WriteBytes(extension
, num_bytes
, true);
145 EXPECT_GT(GetNextStorageThreshold(extension
->id()), previous_threshold
);
148 void WriteBytesNotExpectingNotification(const Extension
* extension
,
150 WriteBytes(extension
, num_bytes
, false);
153 void SimulateUninstallDialogAccept() {
154 // Ensure the uninstall dialog was shown and fake an accept.
155 ASSERT_TRUE(monitor()->uninstall_dialog_
.get());
156 monitor()->ExtensionUninstallAccepted();
160 void InitStorageMonitor() {
161 storage_monitor_
= ExtensionStorageMonitor::Get(profile());
162 ASSERT_TRUE(storage_monitor_
);
164 // Override thresholds so that we don't have to write a huge amount of data
165 // to trigger notifications in these tests.
166 storage_monitor_
->enable_for_all_extensions_
= true;
167 storage_monitor_
->initial_extension_threshold_
= kInitialUsageThreshold
;
168 storage_monitor_
->initial_ephemeral_threshold_
= kInitialUsageThreshold
;
170 // To ensure storage events are dispatched from QuotaManager immediately.
171 storage_monitor_
->observer_rate_
= base::TimeDelta();
174 // Write a number of bytes to persistent storage.
175 void WriteBytes(const Extension
* extension
,
177 bool expected_notification
) {
178 ExtensionTestMessageListener
launched_listener("launched", true);
179 ExtensionTestMessageListener
write_complete_listener(
180 "write_complete", false);
181 NotificationObserver
notification_observer(
182 GetNotificationId(extension
->id()));
184 OpenApplication(AppLaunchParams(profile(), extension
, LAUNCH_CONTAINER_NONE
,
185 NEW_WINDOW
, extensions::SOURCE_TEST
));
186 ASSERT_TRUE(launched_listener
.WaitUntilSatisfied());
188 // Instruct the app to write |num_bytes| of data.
189 launched_listener
.Reply(base::IntToString(num_bytes
));
190 ASSERT_TRUE(write_complete_listener
.WaitUntilSatisfied());
192 if (expected_notification
) {
193 EXPECT_TRUE(notification_observer
.WaitForNotification());
195 base::RunLoop().RunUntilIdle();
196 EXPECT_FALSE(notification_observer
.HasReceivedNotification());
200 ExtensionStorageMonitor
* storage_monitor_
;
203 // Control - No notifications should be shown if usage remains under the
205 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, UnderThreshold
) {
206 const Extension
* extension
= InitWriteDataApp();
207 ASSERT_TRUE(extension
);
208 WriteBytesNotExpectingNotification(extension
, 1);
211 // Ensure a notification is shown when usage reaches the first threshold.
212 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, ExceedInitialThreshold
) {
213 const Extension
* extension
= InitWriteDataApp();
214 ASSERT_TRUE(extension
);
215 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
218 // Ensure a notification is shown when usage immediately exceeds double the
220 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, DoubleInitialThreshold
) {
221 const Extension
* extension
= InitWriteDataApp();
222 ASSERT_TRUE(extension
);
223 WriteBytesExpectingNotification(extension
,
224 GetInitialExtensionThreshold() * 2);
227 // Ensure that notifications are not fired if the next threshold has not been
229 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, ThrottleNotifications
) {
230 const Extension
* extension
= InitWriteDataApp();
231 ASSERT_TRUE(extension
);
233 // Exceed the first threshold.
234 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
236 // Stay within the next threshold.
237 WriteBytesNotExpectingNotification(extension
, 1);
240 // Verify that notifications are disabled when the user clicks the action button
241 // in the notification.
242 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, UserDisabledNotifications
) {
243 const Extension
* extension
= InitWriteDataApp();
244 ASSERT_TRUE(extension
);
245 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
247 EXPECT_TRUE(IsStorageNotificationEnabled(extension
->id()));
249 // Fake clicking the notification button to disable notifications.
250 message_center::MessageCenter::Get()->ClickOnNotificationButton(
251 GetNotificationId(extension
->id()),
252 ExtensionStorageMonitor::BUTTON_DISABLE_NOTIFICATION
);
254 EXPECT_FALSE(IsStorageNotificationEnabled(extension
->id()));
256 // Expect to receive no further notifications when usage continues to
258 int64 next_threshold
= GetNextStorageThreshold(extension
->id());
259 int64 next_data_size
= next_threshold
- GetInitialExtensionThreshold();
260 ASSERT_GT(next_data_size
, 0);
262 WriteBytesNotExpectingNotification(extension
, next_data_size
);
265 // Verify that thresholds for ephemeral apps are reset when they are
266 // promoted to regular installed apps.
267 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, EphemeralAppLowUsage
) {
268 const Extension
* extension
= InitWriteDataEphemeralApp();
269 ASSERT_TRUE(extension
);
270 WriteBytesExpectingNotification(extension
, GetInitialEphemeralThreshold());
272 // Store the number of bytes until the next threshold is reached.
273 int64 next_threshold
= GetNextStorageThreshold(extension
->id());
274 int64 next_data_size
= next_threshold
- GetInitialEphemeralThreshold();
275 ASSERT_GT(next_data_size
, 0);
276 EXPECT_GE(GetInitialExtensionThreshold(), next_threshold
);
278 // Promote the ephemeral app.
279 ExtensionService
* service
=
280 ExtensionSystem::Get(profile())->extension_service();
281 service
->PromoteEphemeralApp(extension
, false);
283 // The next threshold should now be equal to the initial threshold for
284 // extensions (which is higher than the initial threshold for ephemeral apps).
285 EXPECT_EQ(GetInitialExtensionThreshold(),
286 GetNextStorageThreshold(extension
->id()));
288 // Since the threshold was increased, a notification should not be
290 WriteBytesNotExpectingNotification(extension
, next_data_size
);
293 // Verify that thresholds for ephemeral apps are not reset when they are
294 // promoted to regular installed apps if their usage is higher than the initial
295 // threshold for installed extensions.
296 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, EphemeralAppWithHighUsage
) {
297 const Extension
* extension
= InitWriteDataEphemeralApp();
298 ASSERT_TRUE(extension
);
299 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
300 int64 saved_next_threshold
= GetNextStorageThreshold(extension
->id());
302 // Promote the ephemeral app.
303 ExtensionService
* service
=
304 ExtensionSystem::Get(profile())->extension_service();
305 service
->PromoteEphemeralApp(extension
, false);
307 // The next threshold should not have changed.
308 EXPECT_EQ(saved_next_threshold
, GetNextStorageThreshold(extension
->id()));
311 // Ensure that monitoring is disabled for installed extensions if
312 // |enable_for_all_extensions_| is false. This test can be removed if monitoring
313 // is eventually enabled for all extensions.
314 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
,
315 DisableForInstalledExtensions
) {
316 DisableForInstalledExtensions();
318 const Extension
* extension
= InitWriteDataApp();
319 ASSERT_TRUE(extension
);
320 WriteBytesNotExpectingNotification(extension
, GetInitialExtensionThreshold());
323 // Verify that notifications are disabled when the user clicks the action button
324 // in the notification.
325 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest
, UninstallExtension
) {
326 const Extension
* extension
= InitWriteDataApp();
327 ASSERT_TRUE(extension
);
328 WriteBytesExpectingNotification(extension
, GetInitialExtensionThreshold());
330 // Fake clicking the notification button to uninstall.
331 message_center::MessageCenter::Get()->ClickOnNotificationButton(
332 GetNotificationId(extension
->id()),
333 ExtensionStorageMonitor::BUTTON_UNINSTALL
);
335 // Also fake accepting the uninstall.
336 TestExtensionRegistryObserver
observer(ExtensionRegistry::Get(profile()),
338 SimulateUninstallDialogAccept();
339 observer
.WaitForExtensionUninstalled();
342 } // namespace extensions