Index: chrome/browser/extensions/api/power/power_api_unittest.cc |
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 0000000000000000000000000000000000000000..1069f634d8cc2df325648ef3c9313cf3c21c08ce |
--- /dev/null |
+++ b/chrome/browser/extensions/api/power/power_api_unittest.cc |
@@ -0,0 +1,308 @@ |
+// 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_constants.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 constants = extensions::power_api_constants; |
+ |
+namespace extensions { |
+ |
+namespace { |
+ |
+// 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()->set_create_blocker_function_for_testing( |
+ scoped_ptr<PowerApiManager::CreateBlockerFunction>( |
+ new PowerApiManager::CreateBlockerFunction( |
+ base::Bind(&PowerSaveBlockerStubManager::CreateStub, |
+ base::Unretained(this))))); |
+ } |
+ |
+ ~PowerSaveBlockerStubManager() { |
+ PowerApiManager::GetInstance()->set_create_blocker_function_for_testing( |
+ scoped_ptr<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, |
+ }; |
+ |
+ // Args commonly passed to PowerSaveBlockerStubManager::CallFunction(). |
+ static const std::string kDisplayArgs; |
+ static const std::string kSystemArgs; |
+ static const std::string kEmptyArgs; |
+ |
+ // 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_; |
+}; |
+ |
+const std::string PowerApiTest::kDisplayArgs = |
+ std::string("[\"") + constants::kDisplayLevelValue + "\"]"; |
+const std::string PowerApiTest::kSystemArgs = |
+ std::string("[\"") + constants::kSystemLevelValue + "\"]"; |
+const std::string PowerApiTest::kEmptyArgs = "[]"; |
+ |
+TEST_F(PowerApiTest, RequestAndRelease) { |
+ // Simulate an extension making and releasing a "display" request, a |
+ // "system" request, a request without any arguments (which should be |
+ // treated as "display"), and a request with an invalid argument (which |
+ // should also be treated as "display"). |
+ 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()); |
+ |
+ ASSERT_TRUE(CallFunction(REQUEST, kEmptyArgs, 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, "[\"foo\"]", 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()); |
+} |
+ |
+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 |