blob: 1ac6eea6b3fd33549d19bc2601f8de73b1514432 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "power.h"
#include <fuchsia/hardware/powerimpl/c/banjo.h>
#include <fuchsia/hardware/powerimpl/cpp/banjo.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <memory>
#include <zxtest/zxtest.h>
#include "src/devices/testing/mock-ddk/mock-device.h"
namespace power {
class FakePower : public fidl::WireServer<fuchsia_hardware_power::Device> {
public:
void RegisterPowerDomain(RegisterPowerDomainRequestView request,
RegisterPowerDomainCompleter::Sync& completer) override {
power_domain_registered_count_++;
completer.ReplySuccess();
}
void UnregisterPowerDomain(UnregisterPowerDomainCompleter::Sync& completer) override {
power_domain_unregistered_count_++;
completer.ReplySuccess();
}
void GetPowerDomainStatus(GetPowerDomainStatusCompleter::Sync& completer) override {
completer.ReplySuccess(::fuchsia_hardware_power::wire::PowerDomainStatus::kEnabled);
}
void GetSupportedVoltageRange(GetSupportedVoltageRangeCompleter::Sync& completer) override {
completer.ReplySuccess(0, 10);
}
void RequestVoltage(RequestVoltageRequestView request,
RequestVoltageCompleter::Sync& completer) override {
completer.ReplySuccess(request->voltage);
}
void GetCurrentVoltage(GetCurrentVoltageRequestView request,
GetCurrentVoltageCompleter::Sync& completer) override {
completer.ReplySuccess(0);
}
void WritePmicCtrlReg(WritePmicCtrlRegRequestView request,
WritePmicCtrlRegCompleter::Sync& completer) override {
completer.ReplySuccess();
}
void ReadPmicCtrlReg(ReadPmicCtrlRegRequestView request,
ReadPmicCtrlRegCompleter::Sync& completer) override {
completer.ReplySuccess(0);
}
uint32_t power_domain_registered_count() { return power_domain_registered_count_; }
uint32_t power_domain_unregistered_count() { return power_domain_unregistered_count_; }
zx::result<fidl::ClientEnd<fuchsia_hardware_power::Device>> BindServer() {
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_power::Device>();
if (endpoints.is_error()) {
return endpoints.take_error();
}
binding_.AddBinding(fdf::Dispatcher::GetCurrent()->async_dispatcher(),
std::move(endpoints->server), this, fidl::kIgnoreBindingClosure);
return zx::ok(std::move(endpoints->client));
}
private:
fidl::ServerBindingGroup<fuchsia_hardware_power::Device> binding_;
uint32_t power_domain_registered_count_ = 0;
uint32_t power_domain_unregistered_count_ = 0;
};
class FakePowerImpl : public ddk::PowerImplProtocol<FakePowerImpl> {
public:
FakePowerImpl() : proto_{.ops = &power_impl_protocol_ops_, .ctx = this} {}
ddk::PowerImplProtocolClient GetClient() const { return ddk::PowerImplProtocolClient(&proto_); }
zx_status_t PowerImplGetCurrentVoltage(uint32_t index, uint32_t* current_voltage) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t PowerImplDisablePowerDomain(uint32_t index) {
power_domain_disabled_count_++;
return ZX_OK;
}
zx_status_t PowerImplEnablePowerDomain(uint32_t index) {
power_domain_enabled_count_++;
return ZX_OK;
}
zx_status_t PowerImplGetPowerDomainStatus(uint32_t index, power_domain_status_t* out_status) {
return ZX_OK;
}
zx_status_t PowerImplGetSupportedVoltageRange(uint32_t index, uint32_t* min_voltage,
uint32_t* max_voltage) {
return ZX_OK;
}
zx_status_t PowerImplRequestVoltage(uint32_t index, uint32_t voltage, uint32_t* actual_voltage) {
*actual_voltage = voltage;
return ZX_OK;
}
zx_status_t PowerImplWritePmicCtrlReg(uint32_t index, uint32_t reg_addr, uint32_t value) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t PowerImplReadPmicCtrlReg(uint32_t index, uint32_t addr, uint32_t* value) {
return ZX_ERR_NOT_SUPPORTED;
}
uint32_t power_domain_enabled_count() { return power_domain_enabled_count_; }
uint32_t power_domain_disabled_count() { return power_domain_disabled_count_; }
private:
power_impl_protocol_t proto_;
uint32_t power_domain_enabled_count_ = 0;
uint32_t power_domain_disabled_count_ = 0;
};
class GenericPowerTest : public zxtest::Test {
public:
explicit GenericPowerTest() {}
void SetUp() override {
power_impl_ = std::make_unique<FakePowerImpl>();
zx::result parent_power_client = parent_power_.SyncCall(&FakePower::BindServer);
ASSERT_OK(parent_power_client);
dut_ = std::make_unique<PowerDevice>(fake_parent_.get(), 0, power_impl_->GetClient(),
std::move(parent_power_client.value()), 10, 1000, false);
}
fidl::ClientEnd<fuchsia_hardware_power::Device> ConnectDut() {
auto endpoints = fidl::Endpoints<fuchsia_hardware_power::Device>::Create();
dut_->GetHandler()(std::move(endpoints.server));
return std::move(endpoints.client);
}
static void RunSyncClientTask(fit::closure task) {
// Spawn a separate thread to run the client task using an async::Loop.
async::Loop loop{&kAsyncLoopConfigNeverAttachToThread};
loop.StartThread();
zx::result result;
ASSERT_NO_FAILURES(result = fdf::RunOnDispatcherSync(loop.dispatcher(), std::move(task)));
ASSERT_EQ(ZX_OK, result.status_value());
}
protected:
std::shared_ptr<MockDevice> fake_parent_ = MockDevice::FakeRootParent();
fdf::UnownedSynchronizedDispatcher env_dispatcher_ =
fdf_testing::DriverRuntime::GetInstance()->StartBackgroundDispatcher();
std::unique_ptr<PowerDevice> dut_;
async_patterns::TestDispatcherBound<FakePower> parent_power_{env_dispatcher_->async_dispatcher(),
std::in_place};
std::unique_ptr<FakePowerImpl> power_impl_;
};
TEST_F(GenericPowerTest, RegisterDomain) {
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client(ConnectDut());
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RegisterPowerDomain(20, 800);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 1);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
}
TEST_F(GenericPowerTest, RegisterTwice) {
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client(ConnectDut());
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RegisterPowerDomain(20, 800);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 1);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RegisterPowerDomain(20, 800);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 1);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
}
TEST_F(GenericPowerTest, UnregisterDomain) {
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client(ConnectDut());
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RegisterPowerDomain(20, 800);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 1);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->UnregisterPowerDomain();
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 0);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_unregistered_count), 1);
EXPECT_EQ(power_impl_->power_domain_disabled_count(), 1);
}
TEST_F(GenericPowerTest, UnregisterTwice) {
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client(ConnectDut());
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RegisterPowerDomain(20, 800);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 1);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->UnregisterPowerDomain();
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 0);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->UnregisterPowerDomain();
ZX_ASSERT(result.ok());
EXPECT_EQ(ZX_ERR_UNAVAILABLE, result.value().error_value());
});
EXPECT_EQ(dut_->GetDependentCount(), 0);
}
TEST_F(GenericPowerTest, DependentCount_TwoChildren) {
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client(ConnectDut());
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RegisterPowerDomain(20, 800);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 1);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client_2(ConnectDut());
EXPECT_EQ(dut_->GetDependentCount(), 1);
RunSyncClientTask([&dut_client_2]() {
fidl::WireResult result = dut_client_2->RegisterPowerDomain(50, 400);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 2);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
}
TEST_F(GenericPowerTest, GetSupportedVoltageRange) {
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client(ConnectDut());
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->GetSupportedVoltageRange();
ZX_ASSERT(result.ok() && result->is_ok());
EXPECT_EQ(result->value()->min, 10);
EXPECT_EQ(result->value()->max, 1000);
});
}
TEST_F(GenericPowerTest, RequestVoltage_UnsuppportedVoltage) {
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client(ConnectDut());
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RegisterPowerDomain(20, 800);
ZX_ASSERT(result.ok() && result->is_ok());
fidl::WireResult get_result = dut_client->GetSupportedVoltageRange();
ZX_ASSERT(get_result.ok() && get_result->is_ok());
EXPECT_EQ(get_result.value()->min, 10);
EXPECT_EQ(get_result.value()->max, 1000);
fidl::WireResult request_result = dut_client->RequestVoltage(1010);
ZX_ASSERT(request_result.ok());
EXPECT_EQ(ZX_ERR_INVALID_ARGS, request_result.value().error_value());
});
}
TEST_F(GenericPowerTest, RequestVoltage) {
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client(ConnectDut());
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RegisterPowerDomain(20, 800);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 1);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client_2(ConnectDut());
EXPECT_EQ(dut_->GetDependentCount(), 1);
RunSyncClientTask([&dut_client_2]() {
fidl::WireResult result = dut_client_2->RegisterPowerDomain(10, 400);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 2);
RunSyncClientTask([&dut_client_2]() {
fidl::WireResult result = dut_client_2->RequestVoltage(900);
ZX_ASSERT(result.ok() && result->is_ok());
EXPECT_EQ(400, result.value()->actual_voltage);
fidl::WireResult result2 = dut_client_2->RequestVoltage(15);
ZX_ASSERT(result2.ok() && result2->is_ok());
EXPECT_EQ(20, result2.value()->actual_voltage);
fidl::WireResult unregister_result = dut_client_2->UnregisterPowerDomain();
ZX_ASSERT(unregister_result.ok() && unregister_result->is_ok());
});
EXPECT_EQ(dut_->GetDependentCount(), 1);
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RequestVoltage(900);
ZX_ASSERT(result.ok() && result->is_ok());
EXPECT_EQ(800, result.value()->actual_voltage);
fidl::WireResult result2 = dut_client->RequestVoltage(15);
ZX_ASSERT(result2.ok() && result2->is_ok());
EXPECT_EQ(20, result2.value()->actual_voltage);
});
}
TEST_F(GenericPowerTest, RequestVoltage_Unregistered) {
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client(ConnectDut());
RunSyncClientTask([&dut_client]() {
fidl::WireResult result = dut_client->RequestVoltage(900);
ZX_ASSERT(result.ok());
EXPECT_EQ(ZX_ERR_UNAVAILABLE, result.value().error_value());
});
}
TEST_F(GenericPowerTest, FixedVoltageDomain) {
zx::result parent_power_client = parent_power_.SyncCall(&FakePower::BindServer);
ASSERT_OK(parent_power_client);
auto dut_fixed =
std::make_unique<PowerDevice>(fake_parent_.get(), 1, power_impl_->GetClient(),
std::move(parent_power_client.value()), 1000, 1000, true);
auto endpoints = fidl::Endpoints<fuchsia_hardware_power::Device>::Create();
dut_fixed->GetHandler()(std::move(endpoints.server));
fidl::WireSyncClient<fuchsia_hardware_power::Device> dut_client_2(std::move(endpoints.client));
RunSyncClientTask([&dut_client_2]() {
fidl::WireResult result = dut_client_2->RegisterPowerDomain(0, 0);
ZX_ASSERT(result.ok() && result->is_ok());
});
EXPECT_EQ(dut_fixed->GetDependentCount(), 1);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_registered_count), 1);
EXPECT_EQ(power_impl_->power_domain_enabled_count(), 1);
RunSyncClientTask([&dut_client_2]() {
fidl::WireResult result = dut_client_2->GetSupportedVoltageRange();
ZX_ASSERT(result.ok());
EXPECT_EQ(ZX_ERR_NOT_SUPPORTED, result.value().error_value());
fidl::WireResult request_result = dut_client_2->RequestVoltage(900);
ZX_ASSERT(request_result.ok());
EXPECT_EQ(ZX_ERR_NOT_SUPPORTED, request_result.value().error_value());
fidl::WireResult unregister_result = dut_client_2->UnregisterPowerDomain();
ZX_ASSERT(unregister_result.ok() && unregister_result->is_ok());
});
EXPECT_EQ(dut_fixed->GetDependentCount(), 0);
EXPECT_EQ(parent_power_.SyncCall(&FakePower::power_domain_unregistered_count), 1);
EXPECT_EQ(power_impl_->power_domain_disabled_count(), 1);
}
} // namespace power