#include "extensions/browser/api/power/power_api.h"
#include <memory>
#include <string>
#include "base/containers/circular_deque.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/browser/api_unittest.h"
#include "extensions/browser/unloaded_extension_reason.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_builder.h"
#include "services/device/public/mojom/wake_lock.mojom-shared.h"
namespace extensions {
namespace {
const char kDisplayArgs[] = "[\"display\"]";
const char kSystemArgs[] = "[\"system\"]";
const char kEmptyArgs[] = "[]";
enum Request {
BLOCK_APP_SUSPENSION,
UNBLOCK_APP_SUSPENSION,
BLOCK_DISPLAY_SLEEP,
UNBLOCK_DISPLAY_SLEEP,
NONE,
};
class FakeWakeLockManager {
public:
explicit FakeWakeLockManager(content::BrowserContext* context)
: browser_context_(context) {
PowerAPI::Get(browser_context_)
->SetWakeLockFunctionsForTesting(
base::BindRepeating(&FakeWakeLockManager::ActivateWakeLock,
base::Unretained(this)),
base::BindRepeating(&FakeWakeLockManager::CancelWakeLock,
base::Unretained(this)));
}
FakeWakeLockManager(const FakeWakeLockManager&) = delete;
FakeWakeLockManager& operator=(const FakeWakeLockManager&) = delete;
~FakeWakeLockManager() {
PowerAPI::Get(browser_context_)
->SetWakeLockFunctionsForTesting(PowerAPI::ActivateWakeLockFunction(),
PowerAPI::CancelWakeLockFunction());
}
Request PopFirstRequest() {
if (requests_.empty())
return NONE;
Request request = requests_.front();
requests_.pop_front();
return request;
}
private:
void ActivateWakeLock(device::mojom::WakeLockType type) {
if (is_active_) {
if (type == type_)
return;
switch (type) {
case device::mojom::WakeLockType::kPreventAppSuspension:
requests_.push_back(BLOCK_APP_SUSPENSION);
requests_.push_back(UNBLOCK_DISPLAY_SLEEP);
break;
case device::mojom::WakeLockType::kPreventDisplaySleep:
requests_.push_back(BLOCK_DISPLAY_SLEEP);
requests_.push_back(UNBLOCK_APP_SUSPENSION);
break;
case device::mojom::WakeLockType::kPreventDisplaySleepAllowDimming:
NOTREACHED() << "Unexpected wake lock type " << type;
}
type_ = type;
return;
}
if (!is_active_) {
switch (type) {
case device::mojom::WakeLockType::kPreventAppSuspension:
requests_.push_back(BLOCK_APP_SUSPENSION);
break;
case device::mojom::WakeLockType::kPreventDisplaySleep:
requests_.push_back(BLOCK_DISPLAY_SLEEP);
break;
case device::mojom::WakeLockType::kPreventDisplaySleepAllowDimming:
NOTREACHED() << "Unexpected wake lock type " << type;
}
type_ = type;
is_active_ = true;
}
}
void CancelWakeLock() {
if (!is_active_)
return;
switch (type_) {
case device::mojom::WakeLockType::kPreventAppSuspension:
requests_.push_back(UNBLOCK_APP_SUSPENSION);
break;
case device::mojom::WakeLockType::kPreventDisplaySleep:
requests_.push_back(UNBLOCK_DISPLAY_SLEEP);
break;
case device::mojom::WakeLockType::kPreventDisplaySleepAllowDimming:
NOTREACHED() << "Unexpected wake lock type " << type_;
}
is_active_ = false;
}
raw_ptr<content::BrowserContext> browser_context_;
device::mojom::WakeLockType type_;
bool is_active_ = false;
base::circular_deque<Request> requests_;
};
}
class PowerAPITest : public ApiUnitTest {
public:
void SetUp() override {
ApiUnitTest::SetUp();
manager_ = std::make_unique<FakeWakeLockManager>(browser_context());
}
void TearDown() override {
manager_.reset();
ApiUnitTest::TearDown();
}
protected:
enum FunctionType {
REQUEST,
RELEASE,
};
bool CallFunction(FunctionType type,
const std::string& args,
const extensions::Extension* extension) {
scoped_refptr<ExtensionFunction> function;
if (type == REQUEST) {
function = base::MakeRefCounted<PowerRequestKeepAwakeFunction>();
} else {
function = base::MakeRefCounted<PowerReleaseKeepAwakeFunction>();
}
function->set_extension(extension);
return api_test_utils::RunFunction(function.get(), args, browser_context());
}
void UnloadExtension(const extensions::Extension* extension) {
PowerAPI::Get(browser_context())
->OnExtensionUnloaded(browser_context(), extension,
UnloadedExtensionReason::UNINSTALL);
}
std::unique_ptr<FakeWakeLockManager> manager_;
};
TEST_F(PowerAPITest, RequestAndRelease) {
ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension()));
EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension()));
EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension()));
EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension()));
EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
}
TEST_F(PowerAPITest, RequestWithoutRelease) {
ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension()));
EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
UnloadExtension(extension());
EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
}
TEST_F(PowerAPITest, ReleaseWithoutRequest) {
ASSERT_TRUE(CallFunction(RELEASE, kEmptyArgs, extension()));
EXPECT_EQ(NONE, manager_->PopFirstRequest());
}
TEST_F(PowerAPITest, UpgradeRequest) {
ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension()));
EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension()));
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()));
EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
}
TEST_F(PowerAPITest, DowngradeRequest) {
ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension()));
EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension()));
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()));
EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
}
TEST_F(PowerAPITest, MultipleExtensions) {
ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension()));
EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
scoped_refptr<const Extension> extension2(ExtensionBuilder("Test2").Build());
ASSERT_TRUE(CallFunction(REQUEST, kSystemArgs, extension2.get()));
EXPECT_EQ(NONE, manager_->PopFirstRequest());
UnloadExtension(extension());
EXPECT_EQ(BLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
EXPECT_EQ(UNBLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
ASSERT_TRUE(CallFunction(REQUEST, kDisplayArgs, extension()));
EXPECT_EQ(BLOCK_DISPLAY_SLEEP, manager_->PopFirstRequest());
EXPECT_EQ(UNBLOCK_APP_SUSPENSION, manager_->PopFirstRequest());
EXPECT_EQ(NONE, manager_->PopFirstRequest());
}
}