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