Index: chrome/browser/chromeos/power/extension_event_observer_unittest.cc |
diff --git a/chrome/browser/chromeos/power/extension_event_observer_unittest.cc b/chrome/browser/chromeos/power/extension_event_observer_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..acc30b7140c56a5470c3ac9393436ef195c58c74 |
--- /dev/null |
+++ b/chrome/browser/chromeos/power/extension_event_observer_unittest.cc |
@@ -0,0 +1,319 @@ |
+// Copyright 2015 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/chromeos/power/extension_event_observer.h" |
+ |
+#include <string> |
+ |
+#include "base/macros.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
+#include "chrome/browser/chromeos/login/users/fake_user_manager.h" |
+#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" |
+#include "chrome/browser/chromeos/settings/cros_settings.h" |
+#include "chrome/browser/chromeos/settings/device_settings_service.h" |
+#include "chrome/common/extensions/api/gcm.h" |
+#include "chrome/test/base/testing_browser_process.h" |
+#include "chrome/test/base/testing_profile.h" |
+#include "chrome/test/base/testing_profile_manager.h" |
+#include "chromeos/dbus/dbus_thread_manager.h" |
+#include "chromeos/dbus/fake_power_manager_client.h" |
+#include "content/public/test/test_browser_thread_bundle.h" |
+#include "content/public/test/test_renderer_host.h" |
+#include "extensions/browser/extension_host.h" |
+#include "extensions/browser/process_manager.h" |
+#include "extensions/common/extension.h" |
+#include "extensions/common/extension_builder.h" |
+#include "extensions/common/manifest_handlers/background_info.h" |
+#include "extensions/common/value_builder.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace chromeos { |
+ |
+class ExtensionEventObserverTest : public ::testing::Test { |
+ public: |
+ ExtensionEventObserverTest() |
+ : power_manager_client_(new FakePowerManagerClient()), |
+ fake_user_manager_(new FakeUserManager()), |
+ scoped_user_manager_enabler_(fake_user_manager_) { |
+ DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient( |
+ make_scoped_ptr(power_manager_client_)); |
+ |
+ profile_manager_.reset( |
+ new TestingProfileManager(TestingBrowserProcess::GetGlobal())); |
+ |
+ extension_event_observer_.reset(new ExtensionEventObserver()); |
+ test_api_ = extension_event_observer_->CreateTestApi(); |
+ } |
+ |
+ ~ExtensionEventObserverTest() override { |
+ extension_event_observer_.reset(); |
+ profile_manager_.reset(); |
+ DBusThreadManager::Shutdown(); |
+ } |
+ |
+ // ::testing::Test overrides. |
+ void SetUp() override { |
+ ::testing::Test::SetUp(); |
+ |
+ // Must be called from ::testing::Test::SetUp. |
+ ASSERT_TRUE(profile_manager_->SetUp()); |
+ |
+ const char kUserProfile[] = "profile1@example.com"; |
+ fake_user_manager_->AddUser(kUserProfile); |
+ fake_user_manager_->LoginUser(kUserProfile); |
+ profile_ = profile_manager_->CreateTestingProfile(kUserProfile); |
+ |
+ profile_manager_->SetLoggedIn(true); |
+ } |
+ void TearDown() override { |
+ profile_ = NULL; |
+ profile_manager_->DeleteAllTestingProfiles(); |
+ |
+ ::testing::Test::TearDown(); |
+ } |
+ |
+ protected: |
+ scoped_refptr<extensions::Extension> CreateApp(const std::string& name, |
+ bool uses_gcm) { |
+ scoped_refptr<extensions::Extension> app = |
+ extensions::ExtensionBuilder() |
+ .SetManifest( |
+ extensions::DictionaryBuilder() |
+ .Set("name", name) |
+ .Set("version", "1.0.0") |
+ .Set("manifest_version", 2) |
+ .Set("app", |
+ extensions::DictionaryBuilder().Set( |
+ "background", |
+ extensions::DictionaryBuilder().Set( |
+ "scripts", extensions::ListBuilder().Append( |
+ "background.js")))) |
+ .Set("permissions", extensions::ListBuilder().Append( |
+ uses_gcm ? "gcm" : ""))) |
+ .Build(); |
+ |
+ created_apps_.push_back(app); |
+ |
+ return app; |
+ } |
+ |
+ extensions::ExtensionHost* CreateHostForApp(Profile* profile, |
+ extensions::Extension* app) { |
+ extensions::ProcessManager::Get(profile)->CreateBackgroundHost( |
+ app, extensions::BackgroundInfo::GetBackgroundURL(app)); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ return extensions::ProcessManager::Get(profile) |
+ ->GetBackgroundHostForExtension(app->id()); |
+ } |
+ |
+ // Owned by DBusThreadManager. |
+ FakePowerManagerClient* power_manager_client_; |
+ |
+ scoped_ptr<ExtensionEventObserver> extension_event_observer_; |
+ scoped_ptr<ExtensionEventObserver::TestApi> test_api_; |
+ |
+ // Owned by |profile_manager_|. |
+ TestingProfile* profile_; |
+ scoped_ptr<TestingProfileManager> profile_manager_; |
+ |
+ private: |
+ content::TestBrowserThreadBundle browser_thread_bundle_; |
+ |
+ // Needed to ensure we don't end up creating actual RenderViewHosts |
+ // and RenderProcessHosts. |
+ content::RenderViewHostTestEnabler render_view_host_test_enabler_; |
+ |
+ // Chrome OS needs extra services to run in the following order. |
+ ScopedTestDeviceSettingsService test_device_settings_service_; |
+ ScopedTestCrosSettings test_cros_settings_; |
+ |
+ // Owned by |scoped_user_manager_enabler_|. |
+ FakeUserManager* fake_user_manager_; |
+ ScopedUserManagerEnabler scoped_user_manager_enabler_; |
+ |
+ std::vector<scoped_refptr<extensions::Extension>> created_apps_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ExtensionEventObserverTest); |
+}; |
+ |
+// Tests that the ExtensionEventObserver reports readiness for suspend when |
+// there is nothing interesting going on. |
+TEST_F(ExtensionEventObserverTest, BasicSuspendAndDarkSuspend) { |
+ power_manager_client_->SendSuspendImminent(); |
+ EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ power_manager_client_->SendDarkSuspendImminent(); |
+ EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+} |
+ |
+// Tests that the ExtensionEventObserver properly handles a canceled suspend |
+// attempt. |
+TEST_F(ExtensionEventObserverTest, CanceledSuspend) { |
+ power_manager_client_->SendSuspendImminent(); |
+ EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ power_manager_client_->SendSuspendDone(); |
+ EXPECT_FALSE(test_api_->MaybeRunSuspendReadinessCallback()); |
+} |
+ |
+// Tests that the ExtensionEventObserver delays suspends and dark suspends while |
+// there is a push message pending for an app that uses GCM. |
+TEST_F(ExtensionEventObserverTest, PushMessagesDelaySuspend) { |
+ scoped_refptr<extensions::Extension> gcm_app = |
+ CreateApp("DelaysSuspendForPushMessages", true /* uses_gcm */); |
+ extensions::ExtensionHost* host = CreateHostForApp(profile_, gcm_app.get()); |
+ ASSERT_TRUE(host); |
+ EXPECT_TRUE(test_api_->WillDelaySuspendForExtensionHost(host)); |
+ |
+ // Test that a push message received before a suspend attempt delays the |
+ // attempt. |
+ const int kSuspendPushId = 23874; |
+ extension_event_observer_->OnExtensionMessageDispatched( |
+ host, extensions::api::gcm::OnMessage::kEventName, kSuspendPushId); |
+ power_manager_client_->SendSuspendImminent(); |
+ |
+ EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ extension_event_observer_->OnExtensionMessageAcked(host, kSuspendPushId); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ // Now test receiving the suspend attempt before the push message. |
+ const int kDarkSuspendPushId = 56674; |
+ power_manager_client_->SendDarkSuspendImminent(); |
+ extension_event_observer_->OnExtensionMessageDispatched( |
+ host, extensions::api::gcm::OnMessage::kEventName, kDarkSuspendPushId); |
+ |
+ EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ extension_event_observer_->OnExtensionMessageAcked(host, kDarkSuspendPushId); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ // Test that non-push messages do not delay the suspend. |
+ const int kNonPushId = 5687; |
+ power_manager_client_->SendDarkSuspendImminent(); |
+ extension_event_observer_->OnExtensionMessageDispatched(host, "FakeMessage", |
+ kNonPushId); |
+ |
+ EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+} |
+ |
+// Tests that messages sent for apps that don't use GCM are ignored. |
+TEST_F(ExtensionEventObserverTest, IgnoresNonGCMApps) { |
+ scoped_refptr<extensions::Extension> app = CreateApp("Non-GCM", false); |
+ extensions::ExtensionHost* host = CreateHostForApp(profile_, app.get()); |
+ ASSERT_TRUE(host); |
+ |
+ EXPECT_FALSE(test_api_->WillDelaySuspendForExtensionHost(host)); |
+ |
+ power_manager_client_->SendSuspendImminent(); |
+ EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+} |
+ |
+// Tests that network requests started by an app while it is processing a push |
+// message delay any suspend attempt. |
+TEST_F(ExtensionEventObserverTest, NetworkRequestsMayDelaySuspend) { |
+ scoped_refptr<extensions::Extension> app = CreateApp("NetworkRequests", true); |
+ extensions::ExtensionHost* host = CreateHostForApp(profile_, app.get()); |
+ ASSERT_TRUE(host); |
+ EXPECT_TRUE(test_api_->WillDelaySuspendForExtensionHost(host)); |
+ |
+ // Test that network requests started while there is no pending push message |
+ // are ignored. |
+ const uint64 kNonPushRequestId = 5170725; |
+ extension_event_observer_->OnNetworkRequestStarted(host, kNonPushRequestId); |
+ power_manager_client_->SendSuspendImminent(); |
+ |
+ EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ // Test that network requests started while a push message is pending delay |
+ // the suspend even after the push message has been acked. |
+ const int kPushMessageId = 178674; |
+ const uint64 kNetworkRequestId = 78917089; |
+ power_manager_client_->SendDarkSuspendImminent(); |
+ extension_event_observer_->OnExtensionMessageDispatched( |
+ host, extensions::api::gcm::OnMessage::kEventName, kPushMessageId); |
+ |
+ EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ extension_event_observer_->OnNetworkRequestStarted(host, kNetworkRequestId); |
+ extension_event_observer_->OnExtensionMessageAcked(host, kPushMessageId); |
+ EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ extension_event_observer_->OnNetworkRequestDone(host, kNetworkRequestId); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+} |
+ |
+// Tests that any outstanding push messages or network requests for an |
+// ExtensionHost that is destroyed do not end up blocking system suspend. |
+TEST_F(ExtensionEventObserverTest, DeletedExtensionHostDoesNotBlockSuspend) { |
+ scoped_refptr<extensions::Extension> app = |
+ CreateApp("DeletedExtensionHost", true); |
+ |
+ // The easiest way to delete an extension host is to delete the Profile it is |
+ // associated with so we create a new Profile here. |
+ const char kProfileName[] = "DeletedExtensionHostProfile"; |
+ Profile* new_profile = profile_manager_->CreateTestingProfile(kProfileName); |
+ |
+ extensions::ExtensionHost* host = CreateHostForApp(new_profile, app.get()); |
+ ASSERT_TRUE(host); |
+ EXPECT_TRUE(test_api_->WillDelaySuspendForExtensionHost(host)); |
+ |
+ const int kPushId = 156178; |
+ const uint64 kNetworkId = 791605; |
+ extension_event_observer_->OnExtensionMessageDispatched( |
+ host, extensions::api::gcm::OnMessage::kEventName, kPushId); |
+ extension_event_observer_->OnNetworkRequestStarted(host, kNetworkId); |
+ |
+ // Now delete the Profile. This has the side-effect of also deleting all the |
+ // ExtensionHosts. |
+ profile_manager_->DeleteTestingProfile(kProfileName); |
+ |
+ power_manager_client_->SendSuspendImminent(); |
+ EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+} |
+ |
+// Tests that the ExtensionEventObserver does not delay suspend attempts when it |
+// is disabled. |
+TEST_F(ExtensionEventObserverTest, DoesNotDelaySuspendWhenDisabled) { |
+ scoped_refptr<extensions::Extension> app = |
+ CreateApp("NoDelayWhenDisabled", true); |
+ extensions::ExtensionHost* host = CreateHostForApp(profile_, app.get()); |
+ ASSERT_TRUE(host); |
+ EXPECT_TRUE(test_api_->WillDelaySuspendForExtensionHost(host)); |
+ |
+ // Test that disabling the suspend delay while a suspend is pending will cause |
+ // the ExtensionEventObserver to immediately report readiness. |
+ const int kPushId = 416753; |
+ extension_event_observer_->OnExtensionMessageDispatched( |
+ host, extensions::api::gcm::OnMessage::kEventName, kPushId); |
+ power_manager_client_->SendSuspendImminent(); |
+ EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ extension_event_observer_->SetShouldDelaySuspend(false); |
+ EXPECT_FALSE(test_api_->MaybeRunSuspendReadinessCallback()); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+ |
+ // Test that the ExtensionEventObserver does not delay suspend attempts when |
+ // it is disabled. |
+ power_manager_client_->SendDarkSuspendImminent(); |
+ EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); |
+} |
+ |
+} // namespace chromeos |