blob: ac62b10547dc059eafec4918bc1d412ea2555af0 [file] [edit]
// Copyright 2024 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.
#ifndef SRC_DEVICES_SPMI_TESTING_INCLUDE_LIB_MOCK_SPMI_MOCK_SPMI_H_
#define SRC_DEVICES_SPMI_TESTING_INCLUDE_LIB_MOCK_SPMI_MOCK_SPMI_H_
#include <fidl/fuchsia.hardware.spmi/cpp/fidl.h>
#include <fidl/fuchsia.hardware.spmi/cpp/test_base.h>
#include <memory>
#include <optional>
#include <queue>
#include <gtest/gtest.h>
namespace mock_spmi {
class MockSpmi : public fidl::testing::TestBase<fuchsia_hardware_spmi::Device> {
public:
void ExpectGetProperties(uint16_t sid, std::string name) {
expectations_.push({
.type = CallType::kGetProperties,
.sid = sid,
.name = name,
});
}
void ExpectRegisterRead(uint16_t address, uint32_t size_bytes,
std::vector<uint8_t> expected_data) {
expectations_.push({
.type = CallType::kRead,
.address = address,
.size_bytes = size_bytes,
.data = std::move(expected_data),
});
}
void ExpectRegisterRead(uint16_t address, uint32_t size_bytes,
fuchsia_hardware_spmi::DriverError expected_error) {
expectations_.push({
.type = CallType::kRead,
.error = expected_error,
.address = address,
.size_bytes = size_bytes,
});
}
void ExpectRegisterWrite(
uint16_t address, std::vector<uint8_t> data,
std::optional<fuchsia_hardware_spmi::DriverError> expected_error = std::nullopt) {
expectations_.push({
.type = CallType::kWrite,
.error = expected_error,
.address = address,
.data = std::move(data),
});
}
struct QueuedExpectation {
fuchsia_hardware_spmi::Register8 data;
std::shared_ptr<zx::eventpair> out_lease;
};
zx::eventpair ExpectWatchControllerWriteCommands(
uint8_t address, uint16_t data, std::shared_ptr<zx::eventpair> out_lease = nullptr) {
if (std::holds_alternative<std::deque<QueuedExpectation>>(expected_watches_[address])) {
std::get<std::deque<QueuedExpectation>>(expected_watches_[address])
.emplace_back(
QueuedExpectation{fuchsia_hardware_spmi::Register8{address, data}, out_lease});
return SyncWatchControllerWriteCommands();
}
auto& request = std::get<WatchControllerRequest>(expected_watches_[address]);
zx::eventpair e0, e1;
EXPECT_EQ(zx::eventpair::create(0, &e0, out_lease ? out_lease.get() : &e1), ZX_OK);
request.completer.Reply(
zx::ok(fuchsia_hardware_spmi::DeviceWatchControllerWriteCommandsResponse{
std::vector{fuchsia_hardware_spmi::Register8{address, data}},
request.wake_lease_requested ? std::move(e0) : zx::eventpair{}}));
expected_watches_[address] = std::deque<QueuedExpectation>{};
return SyncWatchControllerWriteCommands();
}
// Returns true if the SPMI device is currently watching for write commands
// at `address`.
bool IsWatchingControllerWriteCommandsAt(uint8_t address) {
if (!expected_watches_.contains(address)) {
return false;
}
if (!std::holds_alternative<WatchControllerRequest>(expected_watches_.at(address))) {
return false;
}
return true;
}
void VerifyAndClear() {
EXPECT_TRUE(expectations_.empty());
for (auto const& [addr, watch] : expected_watches_) {
if (std::holds_alternative<std::deque<QueuedExpectation>>(watch)) {
EXPECT_TRUE(std::get<std::deque<QueuedExpectation>>(watch).empty());
}
}
expectations_ = {};
expected_watches_.clear();
}
zx::eventpair SyncWatchControllerWriteCommands() {
zx::eventpair e0, e1;
EXPECT_EQ(zx::eventpair::create(0, &e0, &e1), ZX_OK);
sync_watch_.emplace(std::move(e0));
return std::move(e1);
}
fidl::ServerBindingGroup<fuchsia_hardware_spmi::Device> bindings_;
private:
enum CallType : uint8_t {
kRead = 0,
kWrite = 1,
kGetProperties = 2,
};
struct SpmiExpectation {
CallType type;
std::optional<fuchsia_hardware_spmi::DriverError> error = std::nullopt;
uint16_t address;
uint32_t size_bytes;
std::vector<uint8_t> data;
uint16_t sid;
std::string name;
};
void GetProperties(GetPropertiesCompleter::Sync& completer) override {
ASSERT_FALSE(expectations_.empty());
auto expectation = std::move(expectations_.front());
expectations_.pop();
ASSERT_EQ(expectation.type, CallType::kGetProperties);
completer.Reply({{
.sid = expectation.sid,
.name = expectation.name,
}});
}
void RegisterRead(RegisterReadRequest& request, RegisterReadCompleter::Sync& completer) override {
ASSERT_FALSE(expectations_.empty());
auto expectation = std::move(expectations_.front());
expectations_.pop();
ASSERT_EQ(expectation.type, CallType::kRead);
EXPECT_EQ(expectation.address, request.address());
EXPECT_EQ(expectation.size_bytes, request.size_bytes());
EXPECT_EQ(expectation.size_bytes, expectation.data.size());
if (expectation.error.has_value()) {
completer.Reply(zx::error(expectation.error.value()));
} else {
completer.Reply(zx::ok(std::move(expectation.data)));
}
}
void RegisterWrite(RegisterWriteRequest& request,
RegisterWriteCompleter::Sync& completer) override {
ASSERT_FALSE(expectations_.empty());
auto expectation = std::move(expectations_.front());
expectations_.pop();
ASSERT_EQ(expectation.type, CallType::kWrite);
EXPECT_EQ(expectation.address, request.address());
ASSERT_EQ(expectation.data.size(), request.data().size());
EXPECT_EQ(expectation.data, std::vector<uint8_t>(request.data().begin(), request.data().end()));
if (expectation.error.has_value()) {
completer.Reply(zx::error(expectation.error.value()));
} else {
completer.Reply(zx::ok());
}
}
void WatchControllerWriteCommands(
WatchControllerWriteCommandsRequest& request,
WatchControllerWriteCommandsCompleter::Sync& completer) override {
EXPECT_EQ(request.size(), 1);
if (!sync_watch_.empty()) {
sync_watch_.front().signal_peer(0, ZX_USER_SIGNAL_0);
sync_watch_.pop();
}
if (std::holds_alternative<WatchControllerRequest>(expected_watches_[request.address()])) {
ZX_ASSERT(false);
return;
}
if (std::get<std::deque<QueuedExpectation>>(expected_watches_[request.address()]).empty()) {
expected_watches_[request.address()] =
WatchControllerRequest{.completer = completer.ToAsync(),
.wake_lease_requested = request.setup_wake_lease().is_valid()};
return;
}
auto& expectation =
std::get<std::deque<QueuedExpectation>>(expected_watches_[request.address()]).front();
EXPECT_EQ(expectation.data.address(), request.address());
zx::eventpair e0, e1;
EXPECT_EQ(
zx::eventpair::create(0, &e0, expectation.out_lease ? expectation.out_lease.get() : &e1),
ZX_OK);
completer.Reply(zx::ok(fuchsia_hardware_spmi::DeviceWatchControllerWriteCommandsResponse{
std::vector{expectation.data},
request.setup_wake_lease().is_valid() ? std::move(e0) : zx::eventpair{}}));
std::get<std::deque<QueuedExpectation>>(expected_watches_[request.address()]).pop_front();
}
void CancelWatchControllerWriteCommands(
CancelWatchControllerWriteCommandsRequest& request,
CancelWatchControllerWriteCommandsCompleter::Sync& completer) override {
EXPECT_EQ(request.size(), 1);
if (std::holds_alternative<std::deque<QueuedExpectation>>(
expected_watches_[request.address()])) {
ZX_ASSERT(
std::get<std::deque<QueuedExpectation>>(expected_watches_[request.address()]).empty());
return;
}
std::get<WatchControllerRequest>(expected_watches_[request.address()])
.completer.Reply(zx::error(ZX_ERR_CANCELED));
expected_watches_[request.address()] = std::deque<QueuedExpectation>{};
completer.Reply(zx::ok());
}
void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_hardware_spmi::Device> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override {
ASSERT_TRUE(false);
}
void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override {
FAIL();
}
std::queue<SpmiExpectation> expectations_;
struct WatchControllerRequest {
WatchControllerWriteCommandsCompleter::Async completer;
bool wake_lease_requested;
};
std::map<uint8_t, std::variant<std::deque<QueuedExpectation>, WatchControllerRequest>>
expected_watches_;
std::queue<zx::eventpair> sync_watch_;
};
} // namespace mock_spmi
#endif // SRC_DEVICES_SPMI_TESTING_INCLUDE_LIB_MOCK_SPMI_MOCK_SPMI_H_