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/power/power_api_manager.h"
16 #include "extensions/browser/api_test_utils.h"
17 #include "extensions/browser/api_unittest.h"
18 #include "extensions/common/extension.h"
19 #include "extensions/common/test_util.h"
21 namespace extensions
{
25 // Args commonly passed to PowerSaveBlockerStubManager::CallFunction().
26 const char kDisplayArgs
[] = "[\"display\"]";
27 const char kSystemArgs
[] = "[\"system\"]";
28 const char kEmptyArgs
[] = "[]";
30 // Different actions that can be performed as a result of a
31 // PowerSaveBlocker being created or destroyed.
34 UNBLOCK_APP_SUSPENSION
,
36 UNBLOCK_DISPLAY_SLEEP
,
37 // Returned by PowerSaveBlockerStubManager::PopFirstRequest() when no
38 // requests are present.
42 // Stub implementation of content::PowerSaveBlocker that just runs a
43 // callback on destruction.
44 class PowerSaveBlockerStub
: public content::PowerSaveBlocker
{
46 explicit PowerSaveBlockerStub(base::Closure unblock_callback
)
47 : unblock_callback_(unblock_callback
) {
50 ~PowerSaveBlockerStub() override
{ unblock_callback_
.Run(); }
53 base::Closure unblock_callback_
;
55 DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStub
);
58 // Manages PowerSaveBlockerStub objects. Tests can instantiate this class
59 // to make PowerApiManager's calls to create PowerSaveBlockers record the
60 // actions that would've been performed instead of actually blocking and
61 // unblocking power management.
62 class PowerSaveBlockerStubManager
{
64 explicit PowerSaveBlockerStubManager(content::BrowserContext
* context
)
65 : browser_context_(context
),
66 weak_ptr_factory_(this) {
67 // Use base::Unretained since callbacks with return values can't use
69 PowerApiManager::Get(browser_context_
)->SetCreateBlockerFunctionForTesting(
70 base::Bind(&PowerSaveBlockerStubManager::CreateStub
,
71 base::Unretained(this)));
74 ~PowerSaveBlockerStubManager() {
75 PowerApiManager::Get(browser_context_
)->SetCreateBlockerFunctionForTesting(
76 PowerApiManager::CreateBlockerFunction());
79 // Removes and returns the first item from |requests_|. Returns NONE if
80 // |requests_| is empty.
81 Request
PopFirstRequest() {
82 if (requests_
.empty())
85 Request request
= requests_
.front();
86 requests_
.pop_front();
91 // Creates a new PowerSaveBlockerStub of type |type|.
92 scoped_ptr
<content::PowerSaveBlocker
> CreateStub(
93 content::PowerSaveBlocker::PowerSaveBlockerType type
,
94 const std::string
& reason
) {
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 PowerApiManager saying that |extension| has
166 void UnloadExtension(const extensions::Extension
* extension
) {
167 PowerApiManager::Get(browser_context())->OnExtensionUnloaded(
168 browser_context(), extension
, UnloadedExtensionInfo::REASON_UNINSTALL
);
171 scoped_ptr
<PowerSaveBlockerStubManager
> manager_
;
174 TEST_F(PowerApiTest
, RequestAndRelease
) {
175 // Simulate an extension making and releasing a "display" request and a
177 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
178 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
179 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
180 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
181 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
182 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
184 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension()));
185 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
186 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
187 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
188 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
189 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
192 TEST_F(PowerApiTest
, RequestWithoutRelease
) {
193 // Simulate an extension calling requestKeepAwake() without calling
194 // releaseKeepAwake(). The override should be automatically removed when
195 // the extension is unloaded.
196 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
197 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
198 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
200 UnloadExtension(extension());
201 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
202 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
205 TEST_F(PowerApiTest
, ReleaseWithoutRequest
) {
206 // Simulate an extension calling releaseKeepAwake() without having
207 // calling requestKeepAwake() earlier. The call should be ignored.
208 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
209 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
212 TEST_F(PowerApiTest
, UpgradeRequest
) {
213 // Simulate an extension calling requestKeepAwake("system") and then
214 // requestKeepAwake("display"). When the second call is made, a
215 // display-sleep-blocking request should be made before the initial
216 // app-suspension-blocking request is released.
217 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension()));
218 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
219 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
221 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
222 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
223 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
224 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
226 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
227 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
228 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
231 TEST_F(PowerApiTest
, DowngradeRequest
) {
232 // Simulate an extension calling requestKeepAwake("display") and then
233 // requestKeepAwake("system"). When the second call is made, an
234 // app-suspension-blocking request should be made before the initial
235 // display-sleep-blocking request is released.
236 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
237 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
238 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
240 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension()));
241 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
242 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
243 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
245 ASSERT_TRUE(CallFunction(RELEASE
, kEmptyArgs
, extension()));
246 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
247 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
250 TEST_F(PowerApiTest
, MultipleExtensions
) {
251 // Simulate an extension blocking the display from sleeping.
252 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
253 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
254 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
256 // Create a second extension that blocks system suspend. No additional
257 // PowerSaveBlocker is needed; the blocker from the first extension
258 // already covers the behavior requested by the second extension.
259 scoped_refptr
<Extension
> extension2(test_util::CreateEmptyExtension("id2"));
260 ASSERT_TRUE(CallFunction(REQUEST
, kSystemArgs
, extension2
.get()));
261 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
263 // When the first extension is unloaded, a new app-suspension blocker
264 // should be created before the display-sleep blocker is destroyed.
265 UnloadExtension(extension());
266 EXPECT_EQ(BLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
267 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
268 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
270 // Make the first extension block display-sleep again.
271 ASSERT_TRUE(CallFunction(REQUEST
, kDisplayArgs
, extension()));
272 EXPECT_EQ(BLOCK_DISPLAY_SLEEP
, manager_
->PopFirstRequest());
273 EXPECT_EQ(UNBLOCK_APP_SUSPENSION
, manager_
->PopFirstRequest());
274 EXPECT_EQ(NONE
, manager_
->PopFirstRequest());
277 } // namespace extensions