diff options
author | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-20 12:18:26 +0000 |
---|---|---|
committer | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-20 12:18:26 +0000 |
commit | ee3f482917d9cdd169faf921dfef6455c33713a7 (patch) | |
tree | 98c3ba25792cb5a179f9af7df93241203b6fb3f1 /chrome/browser/extensions/api/power | |
parent | b4beba41bff7c2f1e9437c0931a577c8d0daf49d (diff) | |
download | chromium_src-ee3f482917d9cdd169faf921dfef6455c33713a7.zip chromium_src-ee3f482917d9cdd169faf921dfef6455c33713a7.tar.gz chromium_src-ee3f482917d9cdd169faf921dfef6455c33713a7.tar.bz2 |
Add chrome.power extension API.
This moves the chrome.experimental.power API to
chrome.power, makes minor changes to its public interface
(adding a "level" parameter to requestKeepAwake() and
removing the callbacks from both methods), adds unit tests
for it, and adds a sample "Keep Awake" extension meant to
replace the existing Chrome OS-specific extension at
http://goo.gl/CrUzi.
BUG=178944
TBR=sky@chromium.org
Review URL: https://chromiumcodereview.appspot.com/12576018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@189253 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/api/power')
-rw-r--r-- | chrome/browser/extensions/api/power/power_api.cc | 26 | ||||
-rw-r--r-- | chrome/browser/extensions/api/power/power_api.h | 38 | ||||
-rw-r--r-- | chrome/browser/extensions/api/power/power_api_manager.cc | 109 | ||||
-rw-r--r-- | chrome/browser/extensions/api/power/power_api_manager.h | 79 | ||||
-rw-r--r-- | chrome/browser/extensions/api/power/power_api_unittest.cc | 282 |
5 files changed, 534 insertions, 0 deletions
diff --git a/chrome/browser/extensions/api/power/power_api.cc b/chrome/browser/extensions/api/power/power_api.cc new file mode 100644 index 0000000..e82e4b5 --- /dev/null +++ b/chrome/browser/extensions/api/power/power_api.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2013 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/extensions/api/power/power_api.h" + +#include "chrome/browser/extensions/api/power/power_api_manager.h" +#include "chrome/common/extensions/api/power.h" + +namespace extensions { + +bool PowerRequestKeepAwakeFunction::RunImpl() { + scoped_ptr<api::power::RequestKeepAwake::Params> params( + api::power::RequestKeepAwake::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + EXTENSION_FUNCTION_VALIDATE(params->level != api::power::LEVEL_NONE); + PowerApiManager::GetInstance()->AddRequest(extension_id(), params->level); + return true; +} + +bool PowerReleaseKeepAwakeFunction::RunImpl() { + PowerApiManager::GetInstance()->RemoveRequest(extension_id()); + return true; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/power/power_api.h b/chrome/browser/extensions/api/power/power_api.h new file mode 100644 index 0000000..fcde9c5 --- /dev/null +++ b/chrome/browser/extensions/api/power/power_api.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_H_ + +#include "chrome/browser/extensions/extension_function.h" + +namespace extensions { + +// Implementation of the chrome.power.requestKeepAwake API. +class PowerRequestKeepAwakeFunction : public SyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("power.requestKeepAwake", POWER_REQUESTKEEPAWAKE) + + protected: + virtual ~PowerRequestKeepAwakeFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +// Implementation of the chrome.power.releaseKeepAwake API. +class PowerReleaseKeepAwakeFunction : public SyncExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("power.releaseKeepAwake", POWER_RELEASEKEEPAWAKE) + + protected: + virtual ~PowerReleaseKeepAwakeFunction() {} + + // ExtensionFunction: + virtual bool RunImpl() OVERRIDE; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_H_ diff --git a/chrome/browser/extensions/api/power/power_api_manager.cc b/chrome/browser/extensions/api/power/power_api_manager.cc new file mode 100644 index 0000000..1a1c37b --- /dev/null +++ b/chrome/browser/extensions/api/power/power_api_manager.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2013 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/extensions/api/power/power_api_manager.h" + +#include "base/bind.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/extensions/extension.h" +#include "content/public/browser/notification_service.h" + +namespace extensions { + +namespace { + +const char kPowerSaveBlockerReason[] = "extension"; + +content::PowerSaveBlocker::PowerSaveBlockerType +LevelToPowerSaveBlockerType(api::power::Level level) { + switch (level) { + case api::power::LEVEL_SYSTEM: + return content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension; + case api::power::LEVEL_DISPLAY: // fallthrough + case api::power::LEVEL_NONE: + return content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep; + } + NOTREACHED() << "Unhandled level " << level; + return content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep; +} + +} // namespace + +// static +PowerApiManager* PowerApiManager::GetInstance() { + return Singleton<PowerApiManager>::get(); +} + +void PowerApiManager::AddRequest(const std::string& extension_id, + api::power::Level level) { + extension_levels_[extension_id] = level; + UpdatePowerSaveBlocker(); +} + +void PowerApiManager::RemoveRequest(const std::string& extension_id) { + extension_levels_.erase(extension_id); + UpdatePowerSaveBlocker(); +} + +void PowerApiManager::SetCreateBlockerFunctionForTesting( + CreateBlockerFunction function) { + create_blocker_function_ = !function.is_null() ? function : + base::Bind(&content::PowerSaveBlocker::Create); +} + +void PowerApiManager::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case chrome::NOTIFICATION_EXTENSION_UNLOADED: + RemoveRequest(content::Details<extensions::UnloadedExtensionInfo>( + details)->extension->id()); + UpdatePowerSaveBlocker(); + break; + case chrome::NOTIFICATION_APP_TERMINATING: + power_save_blocker_.reset(); + break; + default: + NOTREACHED() << "Unexpected notification " << type; + } +} + +PowerApiManager::PowerApiManager() + : create_blocker_function_(base::Bind(&content::PowerSaveBlocker::Create)), + current_level_(api::power::LEVEL_SYSTEM) { + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, + content::NotificationService::AllSources()); + registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, + content::NotificationService::AllSources()); +} + +PowerApiManager::~PowerApiManager() {} + +void PowerApiManager::UpdatePowerSaveBlocker() { + if (extension_levels_.empty()) { + power_save_blocker_.reset(); + return; + } + + api::power::Level new_level = api::power::LEVEL_SYSTEM; + for (ExtensionLevelMap::const_iterator it = extension_levels_.begin(); + it != extension_levels_.end(); ++it) { + if (it->second == api::power::LEVEL_DISPLAY) + new_level = it->second; + } + + // If the level changed and we need to create a new blocker, do a swap + // to ensure that there isn't a brief period where power management is + // unblocked. + if (!power_save_blocker_ || new_level != current_level_) { + content::PowerSaveBlocker::PowerSaveBlockerType type = + LevelToPowerSaveBlockerType(new_level); + scoped_ptr<content::PowerSaveBlocker> new_blocker( + create_blocker_function_.Run(type, kPowerSaveBlockerReason)); + power_save_blocker_.swap(new_blocker); + current_level_ = new_level; + } +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/power/power_api_manager.h b/chrome/browser/extensions/api/power/power_api_manager.h new file mode 100644 index 0000000..48ed9ee --- /dev/null +++ b/chrome/browser/extensions/api/power/power_api_manager.h @@ -0,0 +1,79 @@ +// Copyright (c) 2013 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_MANAGER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_MANAGER_H_ + +#include <map> +#include <string> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "chrome/common/extensions/api/power.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/power_save_blocker.h" + +namespace extensions { + +class PowerApiManager : public content::NotificationObserver { + public: + typedef base::Callback<scoped_ptr<content::PowerSaveBlocker>( + content::PowerSaveBlocker::PowerSaveBlockerType, + const std::string&)> CreateBlockerFunction; + + static PowerApiManager* GetInstance(); + + // Adds an extension lock at |level| for |extension_id|, replacing the + // extension's existing lock, if any. + void AddRequest(const std::string& extension_id, api::power::Level level); + + // Removes an extension lock for an extension. Calling this for an + // extension id without a lock will do nothing. + void RemoveRequest(const std::string& extension_id); + + // Replaces the function that will be called to create PowerSaveBlocker + // objects. Passing an empty callback will revert to the default. + void SetCreateBlockerFunctionForTesting(CreateBlockerFunction function); + + // Overridden from content::NotificationObserver. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + private: + friend struct DefaultSingletonTraits<PowerApiManager>; + + PowerApiManager(); + virtual ~PowerApiManager(); + + // Updates |power_save_blocker_| and |current_level_| after iterating + // over |extension_levels_|. + void UpdatePowerSaveBlocker(); + + content::NotificationRegistrar registrar_; + + // Function that should be called to create PowerSaveBlocker objects. + // Tests can change this to record what would've been done instead of + // actually changing the system power-saving settings. + CreateBlockerFunction create_blocker_function_; + + scoped_ptr<content::PowerSaveBlocker> power_save_blocker_; + + // Current level used by |power_save_blocker_|. Meaningless if + // |power_save_blocker_| is NULL. + api::power::Level current_level_; + + // Map from extension ID to the corresponding level for each extension + // that has an outstanding request. + typedef std::map<std::string, api::power::Level> ExtensionLevelMap; + ExtensionLevelMap extension_levels_; + + DISALLOW_COPY_AND_ASSIGN(PowerApiManager); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_POWER_POWER_API_MANAGER_H_ diff --git a/chrome/browser/extensions/api/power/power_api_unittest.cc b/chrome/browser/extensions/api/power/power_api_unittest.cc new file mode 100644 index 0000000..7c2ce9a --- /dev/null +++ b/chrome/browser/extensions/api/power/power_api_unittest.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2013 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/extensions/api/power/power_api.h" + +#include <deque> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/extensions/api/power/power_api_manager.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "content/public/browser/power_save_blocker.h" + +namespace utils = extension_function_test_utils; + +namespace extensions { + +namespace { + +// Args commonly passed to PowerSaveBlockerStubManager::CallFunction(). +const char kDisplayArgs[] = "[\"display\"]"; +const char kSystemArgs[] = "[\"system\"]"; +const char kEmptyArgs[] = "[]"; + +// Different actions that can be performed as a result of a +// PowerSaveBlocker being created or destroyed. +enum Request { + BLOCK_APP_SUSPENSION, + UNBLOCK_APP_SUSPENSION, + BLOCK_DISPLAY_SLEEP, + UNBLOCK_DISPLAY_SLEEP, + // Returned by PowerSaveBlockerStubManager::PopFirstRequest() when no + // requests are present. + NONE, +}; + +// Stub implementation of content::PowerSaveBlocker that just runs a +// callback on destruction. +class PowerSaveBlockerStub : public content::PowerSaveBlocker { + public: + explicit PowerSaveBlockerStub(base::Closure unblock_callback) + : unblock_callback_(unblock_callback) { + } + + virtual ~PowerSaveBlockerStub() { + unblock_callback_.Run(); + } + + private: + base::Closure unblock_callback_; + + DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStub); +}; + +// Manages PowerSaveBlockerStub objects. Tests can instantiate this class +// to make PowerApiManager's calls to create PowerSaveBlockers record the +// actions that would've been performed instead of actually blocking and +// unblocking power management. +class PowerSaveBlockerStubManager { + public: + PowerSaveBlockerStubManager() : weak_ptr_factory_(this) { + // Use base::Unretained since callbacks with return values can't use + // weak pointers. + PowerApiManager::GetInstance()->SetCreateBlockerFunctionForTesting( + base::Bind(&PowerSaveBlockerStubManager::CreateStub, + base::Unretained(this))); + } + + ~PowerSaveBlockerStubManager() { + PowerApiManager::GetInstance()->SetCreateBlockerFunctionForTesting( + PowerApiManager::CreateBlockerFunction()); + } + + // Removes and returns the first item from |requests_|. Returns NONE if + // |requests_| is empty. + Request PopFirstRequest() { + if (requests_.empty()) + return NONE; + + Request request = requests_.front(); + requests_.pop_front(); + return request; + } + + private: + // Creates a new PowerSaveBlockerStub of type |type|. + scoped_ptr<content::PowerSaveBlocker> CreateStub( + content::PowerSaveBlocker::PowerSaveBlockerType type, + const std::string& reason) { + Request unblock_request = NONE; + switch (type) { + case content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension: + requests_.push_back(BLOCK_APP_SUSPENSION); + unblock_request = UNBLOCK_APP_SUSPENSION; + break; + case content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep: + requests_.push_back(BLOCK_DISPLAY_SLEEP); + unblock_request = UNBLOCK_DISPLAY_SLEEP; + break; + } + return scoped_ptr<content::PowerSaveBlocker>( + new PowerSaveBlockerStub( + base::Bind(&PowerSaveBlockerStubManager::AppendRequest, + weak_ptr_factory_.GetWeakPtr(), + unblock_request))); + } + + void AppendRequest(Request request) { + requests_.push_back(request); + } + + // Requests in chronological order. + std::deque<Request> requests_; + + base::WeakPtrFactory<PowerSaveBlockerStubManager> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStubManager); +}; + +} // namespace + +class PowerApiTest : public BrowserWithTestWindowTest { + public: + virtual void SetUp() OVERRIDE { + BrowserWithTestWindowTest::SetUp(); + manager_.reset(new PowerSaveBlockerStubManager); + extension_ = utils::CreateEmptyExtensionWithLocation( + extensions::Manifest::UNPACKED); + } + + protected: + // Shorthand for PowerRequestKeepAwakeFunction and + // PowerReleaseKeepAwakeFunction. + enum FunctionType { + REQUEST, + RELEASE, + }; + + // Calls the function described by |type| with |args|, a JSON list of + // arguments, on behalf of |extension|. + bool CallFunction(FunctionType type, + const std::string& args, + extensions::Extension* extension) { + UIThreadExtensionFunction* function = + type == REQUEST ? + static_cast<UIThreadExtensionFunction*>( + new PowerRequestKeepAwakeFunction) : + static_cast<UIThreadExtensionFunction*>( + new PowerReleaseKeepAwakeFunction); + function->set_extension(extension); + return utils::RunFunction(function, args, browser(), utils::NONE); + } + + // Send a notification to PowerApiManager saying that |extension| has + // been unloaded. + void UnloadExtension(extensions::Extension* extension) { + UnloadedExtensionInfo details( + extension, extension_misc::UNLOAD_REASON_UNINSTALL); + PowerApiManager::GetInstance()->Observe( + chrome::NOTIFICATION_EXTENSION_UNLOADED, + content::Source<Profile>(browser()->profile()), + content::Details<UnloadedExtensionInfo>(&details)); + } + + scoped_ptr<PowerSaveBlockerStubManager> manager_; + scoped_refptr<extensions::Extension> extension_; +}; + +TEST_F(PowerApiTest, RequestAndRelease) { + // Simulate an extension making and releasing a "display" request and a + // "system" request. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get())); + EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, RequestWithoutRelease) { + // Simulate an extension calling requestKeepAwake() without calling + // releaseKeepAwake(). The override should be automatically removed when + // the extension is unloaded. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + UnloadExtension(extension_.get()); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, ReleaseWithoutRequest) { + // Simulate an extension calling releaseKeepAwake() without having + // calling requestKeepAwake() earlier. The call should be ignored. + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, UpgradeRequest) { + // Simulate an extension calling requestKeepAwake("system") and then + // requestKeepAwake("display"). When the second call is made, a + // display-sleep-blocking request should be made before the initial + // app-suspension-blocking request is released. + ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get())); + EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, DowngradeRequest) { + // Simulate an extension calling requestKeepAwake("display") and then + // requestKeepAwake("system"). When the second call is made, an + // app-suspension-blocking request should be made before the initial + // display-sleep-blocking request is released. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get())); + EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); + EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +TEST_F(PowerApiTest, MultipleExtensions) { + // Simulate an extension blocking the display from sleeping. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + // Create a second extension that blocks system suspend. No additional + // PowerSaveBlocker is needed; the blocker from the first extension + // already covers the behavior requested by the second extension. + scoped_ptr<base::DictionaryValue> extension_value( + utils::ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}")); + scoped_refptr<extensions::Extension> extension2( + utils::CreateExtension(extensions::Manifest::UNPACKED, + extension_value.get(), "second_extension")); + ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension2.get())); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + // When the first extension is unloaded, a new app-suspension blocker + // should be created before the display-sleep blocker is destroyed. + UnloadExtension(extension_.get()); + EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); + + // Make the first extension block display-sleep again. + ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); + EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); + EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); + EXPECT_EQ(NONE, manager_->PopFirstRequest()); +} + +} // namespace extensions |