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