1 // Copyright (c) 2013 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.
5 #include "chrome/browser/extensions/api/power/power_api.h"
10 #include "base/basictypes.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/weak_ptr.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/extensions/api/power/power_api_manager.h"
16 #include "chrome/browser/extensions/extension_function_test_utils.h"
17 #include "chrome/test/base/browser_with_test_window_test.h"
18 #include "content/public/browser/notification_details.h"
19 #include "content/public/browser/notification_source.h"
20 #include "content/public/browser/power_save_blocker.h"
21 #include "extensions/common/extension.h"
23 namespace utils
= extension_function_test_utils
;
25 namespace extensions
{
29 // Args commonly passed to PowerSaveBlockerStubManager::CallFunction().
30 const char kDisplayArgs
[] = "[\"display\"]";
31 const char kSystemArgs
[] = "[\"system\"]";
32 const char kEmptyArgs
[] = "[]";
34 // Different actions that can be performed as a result of a
35 // PowerSaveBlocker being created or destroyed.
38 UNBLOCK_APP_SUSPENSION
,
40 UNBLOCK_DISPLAY_SLEEP
,
41 // Returned by PowerSaveBlockerStubManager::PopFirstRequest() when no
42 // requests are present.
46 // Stub implementation of content::PowerSaveBlocker that just runs a
47 // callback on destruction.
48 class PowerSaveBlockerStub
: public content::PowerSaveBlocker
{
50 explicit PowerSaveBlockerStub(base::Closure unblock_callback
)
51 : unblock_callback_(unblock_callback
) {
54 virtual ~PowerSaveBlockerStub() {
55 unblock_callback_
.Run();
59 base::Closure unblock_callback_
;
61 DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStub
);
64 // Manages PowerSaveBlockerStub objects. Tests can instantiate this class
65 // to make PowerApiManager's calls to create PowerSaveBlockers record the
66 // actions that would've been performed instead of actually blocking and
67 // unblocking power management.
68 class PowerSaveBlockerStubManager
{
70 PowerSaveBlockerStubManager() : weak_ptr_factory_(this) {
71 // Use base::Unretained since callbacks with return values can't use
73 PowerApiManager::GetInstance()->SetCreateBlockerFunctionForTesting(
74 base::Bind(&PowerSaveBlockerStubManager::CreateStub
,
75 base::Unretained(this)));
78 ~PowerSaveBlockerStubManager() {
79 PowerApiManager::GetInstance()->SetCreateBlockerFunctionForTesting(
80 PowerApiManager::CreateBlockerFunction());
83 // Removes and returns the first item from |requests_|. Returns NONE if
84 // |requests_| is empty.
85 Request
PopFirstRequest() {
86 if (requests_
.empty())
89 Request request
= requests_
.front();
90 requests_
.pop_front();
95 // Creates a new PowerSaveBlockerStub of type |type|.
96 scoped_ptr
<content::PowerSaveBlocker
> CreateStub(
97 content::PowerSaveBlocker::PowerSaveBlockerType type
,
98 const std::string
& reason
) {
99 Request unblock_request
= NONE
;
101 case content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension
:
102 requests_
.push_back(BLOCK_APP_SUSPENSION
);
103 unblock_request
= UNBLOCK_APP_SUSPENSION
;
105 case content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep
:
106 requests_
.push_back(BLOCK_DISPLAY_SLEEP
);
107 unblock_request
= UNBLOCK_DISPLAY_SLEEP
;
110 return scoped_ptr
<content::PowerSaveBlocker
>(
111 new PowerSaveBlockerStub(
112 base::Bind(&PowerSaveBlockerStubManager::AppendRequest
,
113 weak_ptr_factory_
.GetWeakPtr(),
117 void AppendRequest(Request request
) {
118 requests_
.push_back(request
);
121 // Requests in chronological order.
122 std::deque
<Request
> requests_
;
124 base::WeakPtrFactory
<PowerSaveBlockerStubManager
> weak_ptr_factory_
;
126 DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStubManager
);
131 class PowerApiTest
: public BrowserWithTestWindowTest
{
133 virtual void SetUp() OVERRIDE
{
134 BrowserWithTestWindowTest::SetUp();
135 manager_
.reset(new PowerSaveBlockerStubManager
);
136 extension_
= utils::CreateEmptyExtensionWithLocation(
137 extensions::Manifest::UNPACKED
);
141 // Shorthand for PowerRequestKeepAwakeFunction and
142 // PowerReleaseKeepAwakeFunction.
148 // Calls the function described by |type| with |args|, a JSON list of
149 // arguments, on behalf of |extension|.
150 bool CallFunction(FunctionType type
,
151 const std::string
& args
,
152 extensions::Extension
* extension
) {
153 scoped_refptr
<UIThreadExtensionFunction
> function(
155 static_cast<UIThreadExtensionFunction
*>(
156 new PowerRequestKeepAwakeFunction
) :
157 static_cast<UIThreadExtensionFunction
*>(
158 new PowerReleaseKeepAwakeFunction
));
159 function
->set_extension(extension
);
160 return utils::RunFunction(function
.get(), args
, browser(), utils::NONE
);
163 // Send a notification to PowerApiManager saying that |extension| has
165 void UnloadExtension(extensions::Extension
* extension
) {
166 UnloadedExtensionInfo
details(
167 extension
, UnloadedExtensionInfo::REASON_UNINSTALL
);
168 PowerApiManager::GetInstance()->Observe(
169 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED
,
170 content::Source
<Profile
>(browser()->profile()),
171 content::Details
<UnloadedExtensionInfo
>(&details
));
174 scoped_ptr
<PowerSaveBlockerStubManager
> manager_
;
175 scoped_refptr
<extensions::Extension
> extension_
;
178 TEST_F(PowerApiTest
, RequestAndRelease
) {
179 // Simulate an extension making and releasing a "display" request and a
181 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension_
.get()));
182 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
183 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
184 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension_
.get()));
185 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
186 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
188 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension_
.get()));
189 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
190 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
191 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension_
.get()));
192 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
193 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
196 TEST_F(PowerApiTest
, RequestWithoutRelease
) {
197 // Simulate an extension calling requestKeepAwake() without calling
198 // releaseKeepAwake(). The override should be automatically removed when
199 // the extension is unloaded.
200 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension_
.get()));
201 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
202 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
204 UnloadExtension(extension_
.get());
205 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
206 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
209 TEST_F(PowerApiTest
, ReleaseWithoutRequest
) {
210 // Simulate an extension calling releaseKeepAwake() without having
211 // calling requestKeepAwake() earlier. The call should be ignored.
212 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension_
.get()));
213 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
216 TEST_F(PowerApiTest
, UpgradeRequest
) {
217 // Simulate an extension calling requestKeepAwake("system") and then
218 // requestKeepAwake("display"). When the second call is made, a
219 // display-sleep-blocking request should be made before the initial
220 // app-suspension-blocking request is released.
221 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension_
.get()));
222 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
223 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
225 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension_
.get()));
226 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
227 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
228 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
230 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension_
.get()));
231 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
232 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
235 TEST_F(PowerApiTest
, DowngradeRequest
) {
236 // Simulate an extension calling requestKeepAwake("display") and then
237 // requestKeepAwake("system"). When the second call is made, an
238 // app-suspension-blocking request should be made before the initial
239 // display-sleep-blocking request is released.
240 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension_
.get()));
241 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
242 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
244 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension_
.get()));
245 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
246 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
247 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
249 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension_
.get()));
250 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
251 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
254 TEST_F(PowerApiTest
, MultipleExtensions
) {
255 // Simulate an extension blocking the display from sleeping.
256 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension_
.get()));
257 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
258 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
260 // Create a second extension that blocks system suspend. No additional
261 // PowerSaveBlocker is needed; the blocker from the first extension
262 // already covers the behavior requested by the second extension.
263 scoped_ptr
<base::DictionaryValue
> extension_value(
264 utils::ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}"));
265 scoped_refptr
<extensions::Extension
> extension2(
266 utils::CreateExtension(extensions::Manifest::UNPACKED
,
267 extension_value
.get(), "second_extension"));
268 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension2
.get()));
269 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
271 // When the first extension is unloaded, a new app-suspension blocker
272 // should be created before the display-sleep blocker is destroyed.
273 UnloadExtension(extension_
.get());
274 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
275 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
276 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
278 // Make the first extension block display-sleep again.
279 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension_
.get()));
280 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
281 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
282 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
285 } // namespace extensions