root/chrome/browser/extensions/api/power/power_api_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. PopFirstRequest
  2. CreateStub
  3. AppendRequest
  4. SetUp
  5. CallFunction
  6. UnloadExtension
  7. TEST_F
  8. TEST_F
  9. TEST_F
  10. TEST_F
  11. TEST_F
  12. TEST_F

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/api/power/power_api.h"

#include <deque>
#include <string>

#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/api/power/power_api_manager.h"
#include "chrome/browser/extensions/extension_function_test_utils.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/power_save_blocker.h"
#include "extensions/common/extension.h"

namespace utils = extension_function_test_utils;

namespace extensions {

namespace {

// Args commonly passed to PowerSaveBlockerStubManager::CallFunction().
const char kDisplayArgs[] = "[\"display\"]";
const char kSystemArgs[] = "[\"system\"]";
const char kEmptyArgs[] = "[]";

// Different actions that can be performed as a result of a
// PowerSaveBlocker being created or destroyed.
enum Request {
  BLOCK_APP_SUSPENSION,
  UNBLOCK_APP_SUSPENSION,
  BLOCK_DISPLAY_SLEEP,
  UNBLOCK_DISPLAY_SLEEP,
  // Returned by PowerSaveBlockerStubManager::PopFirstRequest() when no
  // requests are present.
  NONE,
};

// Stub implementation of content::PowerSaveBlocker that just runs a
// callback on destruction.
class PowerSaveBlockerStub : public content::PowerSaveBlocker {
 public:
  explicit PowerSaveBlockerStub(base::Closure unblock_callback)
      : unblock_callback_(unblock_callback) {
  }

  virtual ~PowerSaveBlockerStub() {
    unblock_callback_.Run();
  }

 private:
  base::Closure unblock_callback_;

  DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStub);
};

// Manages PowerSaveBlockerStub objects.  Tests can instantiate this class
// to make PowerApiManager's calls to create PowerSaveBlockers record the
// actions that would've been performed instead of actually blocking and
// unblocking power management.
class PowerSaveBlockerStubManager {
 public:
  PowerSaveBlockerStubManager() : weak_ptr_factory_(this) {
    // Use base::Unretained since callbacks with return values can't use
    // weak pointers.
    PowerApiManager::GetInstance()->SetCreateBlockerFunctionForTesting(
        base::Bind(&PowerSaveBlockerStubManager::CreateStub,
                   base::Unretained(this)));
  }

  ~PowerSaveBlockerStubManager() {
    PowerApiManager::GetInstance()->SetCreateBlockerFunctionForTesting(
        PowerApiManager::CreateBlockerFunction());
  }

  // Removes and returns the first item from |requests_|.  Returns NONE if
  // |requests_| is empty.
  Request PopFirstRequest() {
    if (requests_.empty())
      return NONE;

    Request request = requests_.front();
    requests_.pop_front();
    return request;
  }

 private:
  // Creates a new PowerSaveBlockerStub of type |type|.
  scoped_ptr<content::PowerSaveBlocker> CreateStub(
      content::PowerSaveBlocker::PowerSaveBlockerType type,
      const std::string& reason) {
    Request unblock_request = NONE;
    switch (type) {
      case content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension:
        requests_.push_back(BLOCK_APP_SUSPENSION);
        unblock_request = UNBLOCK_APP_SUSPENSION;
        break;
      case content::PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep:
        requests_.push_back(BLOCK_DISPLAY_SLEEP);
        unblock_request = UNBLOCK_DISPLAY_SLEEP;
        break;
    }
    return scoped_ptr<content::PowerSaveBlocker>(
        new PowerSaveBlockerStub(
            base::Bind(&PowerSaveBlockerStubManager::AppendRequest,
                       weak_ptr_factory_.GetWeakPtr(),
                       unblock_request)));
  }

  void AppendRequest(Request request) {
    requests_.push_back(request);
  }

  // Requests in chronological order.
  std::deque<Request> requests_;

  base::WeakPtrFactory<PowerSaveBlockerStubManager> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(PowerSaveBlockerStubManager);
};

}  // namespace

class PowerApiTest : public BrowserWithTestWindowTest {
 public:
  virtual void SetUp() OVERRIDE {
    BrowserWithTestWindowTest::SetUp();
    manager_.reset(new PowerSaveBlockerStubManager);
    extension_ = utils::CreateEmptyExtensionWithLocation(
        extensions::Manifest::UNPACKED);
  }

 protected:
  // Shorthand for PowerRequestKeepAwakeFunction and
  // PowerReleaseKeepAwakeFunction.
  enum FunctionType {
    REQUEST,
    RELEASE,
  };

  // Calls the function described by |type| with |args|, a JSON list of
  // arguments, on behalf of |extension|.
  bool CallFunction(FunctionType type,
                    const std::string& args,
                    extensions::Extension* extension) {
    scoped_refptr<UIThreadExtensionFunction> function(
        type == REQUEST ?
        static_cast<UIThreadExtensionFunction*>(
            new PowerRequestKeepAwakeFunction) :
        static_cast<UIThreadExtensionFunction*>(
            new PowerReleaseKeepAwakeFunction));
    function->set_extension(extension);
    return utils::RunFunction(function.get(), args, browser(), utils::NONE);
  }

  // Send a notification to PowerApiManager saying that |extension| has
  // been unloaded.
  void UnloadExtension(extensions::Extension* extension) {
    UnloadedExtensionInfo details(
        extension, UnloadedExtensionInfo::REASON_UNINSTALL);
    PowerApiManager::GetInstance()->Observe(
        chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
        content::Source<Profile>(browser()->profile()),
        content::Details<UnloadedExtensionInfo>(&details));
  }

  scoped_ptr<PowerSaveBlockerStubManager> manager_;
  scoped_refptr<extensions::Extension> extension_;
};

TEST_F(PowerApiTest, RequestAndRelease) {
  // Simulate an extension making and releasing a "display" request and a
  // "system" request.
  ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get()));
  EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());
  ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get()));
  EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());

  ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get()));
  EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());
  ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get()));
  EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());
}

TEST_F(PowerApiTest, RequestWithoutRelease) {
  // Simulate an extension calling requestKeepAwake() without calling
  // releaseKeepAwake().  The override should be automatically removed when
  // the extension is unloaded.
  ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get()));
  EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());

  UnloadExtension(extension_.get());
  EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());
}

TEST_F(PowerApiTest, ReleaseWithoutRequest) {
  // Simulate an extension calling releaseKeepAwake() without having
  // calling requestKeepAwake() earlier.  The call should be ignored.
  ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get()));
  EXPECT_EQ(NONE, manager_->PopFirstRequest());
}

TEST_F(PowerApiTest, UpgradeRequest) {
  // Simulate an extension calling requestKeepAwake("system") and then
  // requestKeepAwake("display").  When the second call is made, a
  // display-sleep-blocking request should be made before the initial
  // app-suspension-blocking request is released.
  ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get()));
  EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());

  ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get()));
  EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());

  ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get()));
  EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());
}

TEST_F(PowerApiTest, DowngradeRequest) {
  // Simulate an extension calling requestKeepAwake("display") and then
  // requestKeepAwake("system").  When the second call is made, an
  // app-suspension-blocking request should be made before the initial
  // display-sleep-blocking request is released.
  ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get()));
  EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());

  ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension_.get()));
  EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
  EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());

  ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension_.get()));
  EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());
}

TEST_F(PowerApiTest, MultipleExtensions) {
  // Simulate an extension blocking the display from sleeping.
  ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get()));
  EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());

  // Create a second extension that blocks system suspend.  No additional
  // PowerSaveBlocker is needed; the blocker from the first extension
  // already covers the behavior requested by the second extension.
  scoped_ptr<base::DictionaryValue> extension_value(
      utils::ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}"));
  scoped_refptr<extensions::Extension> extension2(
      utils::CreateExtension(extensions::Manifest::UNPACKED,
                             extension_value.get(), "second_extension"));
  ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension2.get()));
  EXPECT_EQ(NONE, manager_->PopFirstRequest());

  // When the first extension is unloaded, a new app-suspension blocker
  // should be created before the display-sleep blocker is destroyed.
  UnloadExtension(extension_.get());
  EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
  EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());

  // Make the first extension block display-sleep again.
  ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension_.get()));
  EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
  EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
  EXPECT_EQ(NONE, manager_->PopFirstRequest());
}

}  // namespace extensions

/* [<][>][^][v][top][bottom][index][help] */