1 // Copyright 2014 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 "extensions/browser/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 "content/public/browser/power_save_blocker.h"
15 #include "extensions/browser/api_test_utils.h"
16 #include "extensions/browser/api_unittest.h"
17 #include "extensions/common/extension.h"
18 #include "extensions/common/test_util.h"
20 namespace extensions
{
24 // Args commonly passed to PowerSaveBlockerStubManager::CallFunction().
25 const char kDisplayArgs
[] = "[\"display\"]";
26 const char kSystemArgs
[] = "[\"system\"]";
27 const char kEmptyArgs
[] = "[]";
29 // Different actions that can be performed as a result of a
30 // PowerSaveBlocker being created or destroyed.
33 UNBLOCK_APP_SUSPENSION
,
35 UNBLOCK_DISPLAY_SLEEP
,
36 // Returned by PowerSaveBlockerStubManager::PopFirstRequest() when no
37 // requests are present.
41 // Stub implementation of content::PowerSaveBlocker that just runs a
42 // callback on destruction.
43 class PowerSaveBlockerStub
: public content::PowerSaveBlocker
{
45 explicit PowerSaveBlockerStub(base::Closure unblock_callback
)
46 : unblock_callback_(unblock_callback
) {
49 ~PowerSaveBlockerStub() override
{ unblock_callback_
.Run(); }
52 base::Closure unblock_callback_
;
54 DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStub
);
57 // Manages PowerSaveBlockerStub objects. Tests can instantiate this class
58 // to make PowerAPI's calls to create PowerSaveBlockers record the
59 // actions that would've been performed instead of actually blocking and
60 // unblocking power management.
61 class PowerSaveBlockerStubManager
{
63 explicit PowerSaveBlockerStubManager(content::BrowserContext
* context
)
64 : browser_context_(context
),
65 weak_ptr_factory_(this) {
66 // Use base::Unretained since callbacks with return values can't use
68 PowerAPI::Get(browser_context_
)
69 ->SetCreateBlockerFunctionForTesting(base::Bind(
70 &PowerSaveBlockerStubManager::CreateStub
, base::Unretained(this)));
73 ~PowerSaveBlockerStubManager() {
74 PowerAPI::Get(browser_context_
)
75 ->SetCreateBlockerFunctionForTesting(PowerAPI::CreateBlockerFunction());
78 // Removes and returns the first item from |requests_|. Returns NONE if
79 // |requests_| is empty.
80 Request
PopFirstRequest() {
81 if (requests_
.empty())
84 Request request
= requests_
.front();
85 requests_
.pop_front();
90 // Creates a new PowerSaveBlockerStub of type |type|.
91 scoped_ptr
<content::PowerSaveBlocker
> CreateStub(
92 content::PowerSaveBlocker::PowerSaveBlockerType type
,
93 content::PowerSaveBlocker::Reason reason
,
94 const std::string
& description
) {
95 Request unblock_request
= NONE
;
97 case content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension
:
98 requests_
.push_back(BLOCK_APP_SUSPENSION
);
99 unblock_request
= UNBLOCK_APP_SUSPENSION
;
101 case content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep
:
102 requests_
.push_back(BLOCK_DISPLAY_SLEEP
);
103 unblock_request
= UNBLOCK_DISPLAY_SLEEP
;
106 return scoped_ptr
<content::PowerSaveBlocker
>(
107 new PowerSaveBlockerStub(
108 base::Bind(&PowerSaveBlockerStubManager::AppendRequest
,
109 weak_ptr_factory_
.GetWeakPtr(),
113 void AppendRequest(Request request
) {
114 requests_
.push_back(request
);
117 content::BrowserContext
* browser_context_
;
119 // Requests in chronological order.
120 std::deque
<Request
> requests_
;
122 base::WeakPtrFactory
<PowerSaveBlockerStubManager
> weak_ptr_factory_
;
124 DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStubManager
);
129 class PowerAPITest
: public ApiUnitTest
{
131 void SetUp() override
{
132 ApiUnitTest::SetUp();
133 manager_
.reset(new PowerSaveBlockerStubManager(browser_context()));
136 void TearDown() override
{
138 ApiUnitTest::TearDown();
142 // Shorthand for PowerRequestKeepAwakeFunction and
143 // PowerReleaseKeepAwakeFunction.
149 // Calls the function described by |type| with |args|, a JSON list of
150 // arguments, on behalf of |extension|.
151 bool CallFunction(FunctionType type
,
152 const std::string
& args
,
153 const extensions::Extension
* extension
) {
154 scoped_refptr
<UIThreadExtensionFunction
> function(
156 static_cast<UIThreadExtensionFunction
*>(
157 new PowerRequestKeepAwakeFunction
) :
158 static_cast<UIThreadExtensionFunction
*>(
159 new PowerReleaseKeepAwakeFunction
));
160 function
->set_extension(extension
);
161 return api_test_utils::RunFunction(function
.get(), args
, browser_context());
164 // Send a notification to PowerAPI saying that |extension| has
166 void UnloadExtension(const extensions::Extension
* extension
) {
167 PowerAPI::Get(browser_context())
168 ->OnExtensionUnloaded(browser_context(), extension
,
169 UnloadedExtensionInfo::REASON_UNINSTALL
);
172 scoped_ptr
<PowerSaveBlockerStubManager
> manager_
;
175 TEST_F(PowerAPITest
, RequestAndRelease
) {
176 // Simulate an extension making and releasing a "display" request and a
178 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
179 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
180 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
181 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
182 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
183 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
185 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension()));
186 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
187 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
188 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
189 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
190 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
193 TEST_F(PowerAPITest
, RequestWithoutRelease
) {
194 // Simulate an extension calling requestKeepAwake() without calling
195 // releaseKeepAwake(). The override should be automatically removed when
196 // the extension is unloaded.
197 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
198 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
199 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
201 UnloadExtension(extension());
202 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
203 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
206 TEST_F(PowerAPITest
, ReleaseWithoutRequest
) {
207 // Simulate an extension calling releaseKeepAwake() without having
208 // calling requestKeepAwake() earlier. The call should be ignored.
209 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
210 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
213 TEST_F(PowerAPITest
, UpgradeRequest
) {
214 // Simulate an extension calling requestKeepAwake("system") and then
215 // requestKeepAwake("display"). When the second call is made, a
216 // display-sleep-blocking request should be made before the initial
217 // app-suspension-blocking request is released.
218 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension()));
219 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
220 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
222 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
223 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
224 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
225 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
227 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
228 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
229 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
232 TEST_F(PowerAPITest
, DowngradeRequest
) {
233 // Simulate an extension calling requestKeepAwake("display") and then
234 // requestKeepAwake("system"). When the second call is made, an
235 // app-suspension-blocking request should be made before the initial
236 // display-sleep-blocking request is released.
237 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
238 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
239 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
241 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension()));
242 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
243 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
244 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
246 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
247 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
248 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
251 TEST_F(PowerAPITest
, MultipleExtensions
) {
252 // Simulate an extension blocking the display from sleeping.
253 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
254 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
255 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
257 // Create a second extension that blocks system suspend. No additional
258 // PowerSaveBlocker is needed; the blocker from the first extension
259 // already covers the behavior requested by the second extension.
260 scoped_refptr
<Extension
> extension2(test_util::CreateEmptyExtension("id2"));
261 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension2
.get()));
262 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
264 // When the first extension is unloaded, a new app-suspension blocker
265 // should be created before the display-sleep blocker is destroyed.
266 UnloadExtension(extension());
267 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
268 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
269 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
271 // Make the first extension block display-sleep again.
272 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
273 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
274 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
275 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
278 } // namespace extensions