blob: fd5b1b0bb15fcc22077c5559ef04f1fae635bd80 [file] [log] [blame]
// 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 <stdint.h>
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.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 "chromeos/dbus/power_manager/suspend.pb.h"
#include "components/user_manager/scoped_user_manager.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/extension_host_observer.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"
#include "ui/aura/test/test_screen.h"
#include "ui/display/screen.h"
namespace chromeos {
class ExtensionEventObserverTest : public ::testing::Test {
public:
ExtensionEventObserverTest()
: power_manager_client_(new FakePowerManagerClient()),
test_screen_(aura::TestScreen::Create(gfx::Size())),
fake_user_manager_(new FakeChromeUserManager()),
scoped_user_manager_enabler_(base::WrapUnique(fake_user_manager_)) {
DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient(
base::WrapUnique(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();
display::Screen::SetScreenInstance(test_screen_.get());
// Must be called from ::testing::Test::SetUp.
ASSERT_TRUE(profile_manager_->SetUp());
const char kUserProfile[] = "profile1@example.com";
const AccountId account_id(AccountId::FromUserEmail(kUserProfile));
fake_user_manager_->AddUser(account_id);
fake_user_manager_->LoginUser(account_id);
profile_ =
profile_manager_->CreateTestingProfile(account_id.GetUserEmail());
profile_manager_->SetLoggedIn(true);
}
void TearDown() override {
profile_ = NULL;
profile_manager_->DeleteAllTestingProfiles();
display::Screen::SetScreenInstance(nullptr);
::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")
.Build())
.Build())
.Build())
.Set("permissions", extensions::ListBuilder()
.Append(uses_gcm ? "gcm" : "")
.Build())
.Build())
.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_;
std::unique_ptr<ExtensionEventObserver> extension_event_observer_;
std::unique_ptr<ExtensionEventObserver::TestApi> test_api_;
// Owned by |profile_manager_|.
TestingProfile* profile_;
std::unique_ptr<TestingProfileManager> profile_manager_;
private:
std::unique_ptr<aura::TestScreen> test_screen_;
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 the CrosSettings test helper.
ScopedCrosSettingsTestHelper cros_settings_test_helper_;
// Owned by |scoped_user_manager_enabler_|.
FakeChromeUserManager* fake_user_manager_;
user_manager::ScopedUserManager 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(
power_manager::SuspendImminent_Reason_OTHER);
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(
power_manager::SuspendImminent_Reason_OTHER);
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_->OnBackgroundEventDispatched(
host, extensions::api::gcm::OnMessage::kEventName, kSuspendPushId);
power_manager_client_->SendSuspendImminent(
power_manager::SuspendImminent_Reason_OTHER);
EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback());
EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks());
extension_event_observer_->OnBackgroundEventAcked(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_->OnBackgroundEventDispatched(
host, extensions::api::gcm::OnMessage::kEventName, kDarkSuspendPushId);
EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback());
EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks());
extension_event_observer_->OnBackgroundEventAcked(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_->OnBackgroundEventDispatched(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(
power_manager::SuspendImminent_Reason_OTHER);
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_t kNonPushRequestId = 5170725;
extension_event_observer_->OnNetworkRequestStarted(host, kNonPushRequestId);
power_manager_client_->SendSuspendImminent(
power_manager::SuspendImminent_Reason_OTHER);
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_t kNetworkRequestId = 78917089;
power_manager_client_->SendDarkSuspendImminent();
extension_event_observer_->OnBackgroundEventDispatched(
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_->OnBackgroundEventAcked(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_t kNetworkId = 791605;
extension_event_observer_->OnBackgroundEventDispatched(
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(
power_manager::SuspendImminent_Reason_OTHER);
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_->OnBackgroundEventDispatched(
host, extensions::api::gcm::OnMessage::kEventName, kPushId);
power_manager_client_->SendSuspendImminent(
power_manager::SuspendImminent_Reason_OTHER);
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