OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/browser/extensions/api/power/power_api.h" | |
6 | |
7 #include <deque> | |
8 #include <string> | |
9 | |
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" | |
22 | |
23 namespace utils = extension_function_test_utils; | |
24 | |
25 namespace extensions { | |
26 | |
27 namespace { | |
28 | |
29 // Args commonly passed to PowerSaveBlockerStubManager::CallFunction(). | |
30 const char kDisplayArgs[] = "[\"display\"]"; | |
31 const char kSystemArgs[] = "[\"system\"]"; | |
32 const char kEmptyArgs[] = "[]"; | |
33 | |
34 // Different actions that can be performed as a result of a | |
35 // PowerSaveBlocker being created or destroyed. | |
36 enum Request { | |
37 BLOCK_APP_SUSPENSION, | |
38 UNBLOCK_APP_SUSPENSION, | |
39 BLOCK_DISPLAY_SLEEP, | |
40 UNBLOCK_DISPLAY_SLEEP, | |
41 // Returned by PowerSaveBlockerStubManager::PopFirstRequest() when no | |
42 // requests are present. | |
43 NONE, | |
44 }; | |
45 | |
46 // Stub implementation of content::PowerSaveBlocker that just runs a | |
47 // callback on destruction. | |
48 class PowerSaveBlockerStub : public content::PowerSaveBlocker { | |
49 public: | |
50 explicit PowerSaveBlockerStub(base::Closure unblock_callback) | |
51 : unblock_callback_(unblock_callback) { | |
52 } | |
53 | |
54 virtual ~PowerSaveBlockerStub() { | |
55 unblock_callback_.Run(); | |
56 } | |
57 | |
58 private: | |
59 base::Closure unblock_callback_; | |
60 | |
61 DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStub); | |
62 }; | |
63 | |
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 { | |
69 public: | |
70 explicit PowerSaveBlockerStubManager(content::BrowserContext* context) | |
71 : browser_context_(context), | |
72 weak_ptr_factory_(this) { | |
73 // Use base::Unretained since callbacks with return values can't use | |
74 // weak pointers. | |
75 PowerApiManager::Get(browser_context_)->SetCreateBlockerFunctionForTesting( | |
76 base::Bind(&PowerSaveBlockerStubManager::CreateStub, | |
77 base::Unretained(this))); | |
78 } | |
79 | |
80 ~PowerSaveBlockerStubManager() { | |
81 PowerApiManager::Get(browser_context_)->SetCreateBlockerFunctionForTesting( | |
82 PowerApiManager::CreateBlockerFunction()); | |
83 } | |
84 | |
85 // Removes and returns the first item from |requests_|. Returns NONE if | |
86 // |requests_| is empty. | |
87 Request PopFirstRequest() { | |
88 if (requests_.empty()) | |
89 return NONE; | |
90 | |
91 Request request = requests_.front(); | |
92 requests_.pop_front(); | |
93 return request; | |
94 } | |
95 | |
96 private: | |
97 // Creates a new PowerSaveBlockerStub of type |type|. | |
98 scoped_ptr<content::PowerSaveBlocker> CreateStub( | |
99 content::PowerSaveBlocker::PowerSaveBlockerType type, | |
100 const std::string& reason) { | |
101 Request unblock_request = NONE; | |
102 switch (type) { | |
103 case content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension: | |
104 requests_.push_back(BLOCK_APP_SUSPENSION); | |
105 unblock_request = UNBLOCK_APP_SUSPENSION; | |
106 break; | |
107 case content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep: | |
108 requests_.push_back(BLOCK_DISPLAY_SLEEP); | |
109 unblock_request = UNBLOCK_DISPLAY_SLEEP; | |
110 break; | |
111 } | |
112 return scoped_ptr<content::PowerSaveBlocker>( | |
113 new PowerSaveBlockerStub( | |
114 base::Bind(&PowerSaveBlockerStubManager::AppendRequest, | |
115 weak_ptr_factory_.GetWeakPtr(), | |
116 unblock_request))); | |
117 } | |
118 | |
119 void AppendRequest(Request request) { | |
120 requests_.push_back(request); | |
121 } | |
122 | |
123 content::BrowserContext* browser_context_; | |
124 | |
125 // Requests in chronological order. | |
126 std::deque<Request> requests_; | |
127 | |
128 base::WeakPtrFactory<PowerSaveBlockerStubManager> weak_ptr_factory_; | |
129 | |
130 DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStubManager); | |
131 }; | |
132 | |
133 } // namespace | |
134 | |
135 class PowerApiTest : public BrowserWithTestWindowTest { | |
136 public: | |
137 virtual void SetUp() OVERRIDE { | |
138 BrowserWithTestWindowTest::SetUp(); | |
139 manager_.reset(new PowerSaveBlockerStubManager(profile())); | |
140 extension_ = utils::CreateEmptyExtensionWithLocation( | |
141 extensions::Manifest::UNPACKED); | |
142 } | |
143 | |
144 virtual void TearDown() OVERRIDE { | |
145 extension_ = NULL; | |
146 manager_.reset(); | |
147 BrowserWithTestWindowTest::TearDown(); | |
148 } | |
149 | |
150 protected: | |
151 // Shorthand for PowerRequestKeepAwakeFunction and | |
152 // PowerReleaseKeepAwakeFunction. | |
153 enum FunctionType { | |
154 REQUEST, | |
155 RELEASE, | |
156 }; | |
157 | |
158 // Calls the function described by |type| with |args|, a JSON list of | |
159 // arguments, on behalf of |extension|. | |
160 bool CallFunction(FunctionType type, | |
161 const std::string& args, | |
162 extensions::Extension* extension) { | |
163 scoped_refptr<UIThreadExtensionFunction> function( | |
164 type == REQUEST ? | |
165 static_cast<UIThreadExtensionFunction*>( | |
166 new PowerRequestKeepAwakeFunction) : | |
167 static_cast<UIThreadExtensionFunction*>( | |
168 new PowerReleaseKeepAwakeFunction)); | |
169 function->set_extension(extension); | |
170 return utils::RunFunction(function.get(), args, browser(), utils::NONE); | |
171 } | |
172 | |
173 // Send a notification to PowerApiManager saying that |extension| has | |
174 // been unloaded. | |
175 void UnloadExtension(extensions::Extension* extension) { | |
176 PowerApiManager::Get(profile())->OnExtensionUnloaded( | |
177 profile(), extension, UnloadedExtensionInfo::REASON_UNINSTALL); | |
178 } | |
179 | |
180 scoped_ptr<PowerSaveBlockerStubManager> manager_; | |
181 scoped_refptr<extensions::Extension> extension_; | |
182 }; | |
183 | |
184 TEST_F(PowerApiTest, RequestAndRelease) { | |
185 // Simulate an extension making and releasing a "display" request and a | |
186 // "system" request. | |
187 ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); | |
188 EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
189 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
190 ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); | |
191 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
192 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
193 | |
194 ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get())); | |
195 EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); | |
196 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
197 ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); | |
198 EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); | |
199 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
200 } | |
201 | |
202 TEST_F(PowerApiTest, RequestWithoutRelease) { | |
203 // Simulate an extension calling requestKeepAwake() without calling | |
204 // releaseKeepAwake(). The override should be automatically removed when | |
205 // the extension is unloaded. | |
206 ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); | |
207 EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
208 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
209 | |
210 UnloadExtension(extension_.get()); | |
211 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
212 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
213 } | |
214 | |
215 TEST_F(PowerApiTest, ReleaseWithoutRequest) { | |
216 // Simulate an extension calling releaseKeepAwake() without having | |
217 // calling requestKeepAwake() earlier. The call should be ignored. | |
218 ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); | |
219 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
220 } | |
221 | |
222 TEST_F(PowerApiTest, UpgradeRequest) { | |
223 // Simulate an extension calling requestKeepAwake("system") and then | |
224 // requestKeepAwake("display"). When the second call is made, a | |
225 // display-sleep-blocking request should be made before the initial | |
226 // app-suspension-blocking request is released. | |
227 ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get())); | |
228 EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); | |
229 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
230 | |
231 ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); | |
232 EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
233 EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); | |
234 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
235 | |
236 ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); | |
237 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
238 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
239 } | |
240 | |
241 TEST_F(PowerApiTest, DowngradeRequest) { | |
242 // Simulate an extension calling requestKeepAwake("display") and then | |
243 // requestKeepAwake("system"). When the second call is made, an | |
244 // app-suspension-blocking request should be made before the initial | |
245 // display-sleep-blocking request is released. | |
246 ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); | |
247 EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
248 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
249 | |
250 ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get())); | |
251 EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); | |
252 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
253 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
254 | |
255 ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get())); | |
256 EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); | |
257 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
258 } | |
259 | |
260 TEST_F(PowerApiTest, MultipleExtensions) { | |
261 // Simulate an extension blocking the display from sleeping. | |
262 ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); | |
263 EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
264 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
265 | |
266 // Create a second extension that blocks system suspend. No additional | |
267 // PowerSaveBlocker is needed; the blocker from the first extension | |
268 // already covers the behavior requested by the second extension. | |
269 scoped_ptr<base::DictionaryValue> extension_value( | |
270 utils::ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}")); | |
271 scoped_refptr<extensions::Extension> extension2( | |
272 utils::CreateExtension(extensions::Manifest::UNPACKED, | |
273 extension_value.get(), "second_extension")); | |
274 ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension2.get())); | |
275 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
276 | |
277 // When the first extension is unloaded, a new app-suspension blocker | |
278 // should be created before the display-sleep blocker is destroyed. | |
279 UnloadExtension(extension_.get()); | |
280 EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); | |
281 EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
282 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
283 | |
284 // Make the first extension block display-sleep again. | |
285 ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get())); | |
286 EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest()); | |
287 EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest()); | |
288 EXPECT_EQ(NONE, manager_->PopFirstRequest()); | |
289 } | |
290 | |
291 } // namespace extensions | |
OLD | NEW |