Home | History | Annotate | Download | only in power
      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