blob: 501c232606c22595e987aad4d864aeb155549b7b [file] [log] [blame]
// Copyright 2019 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_BIN_DRIVER_MANAGER_MULTIPLE_DEVICE_TEST_H_
#define SRC_DEVICES_BIN_DRIVER_MANAGER_MULTIPLE_DEVICE_TEST_H_
#include <fuchsia/boot/llcpp/fidl.h>
#include <fuchsia/fshost/llcpp/fidl.h>
#include <lib/fidl-async/cpp/bind.h>
#include <zircon/status.h>
#include <mock-boot-arguments/server.h>
#include <zxtest/zxtest.h>
#include "coordinator_test_utils.h"
#include "src/devices/lib/log/log.h"
class MockFshostAdminServer final : public llcpp::fuchsia::fshost::Admin::Interface {
public:
MockFshostAdminServer() : has_been_shutdown_(false) {}
std::unique_ptr<llcpp::fuchsia::fshost::Admin::SyncClient> CreateClient(
async_dispatcher* dispatcher) {
zx::channel client, server;
zx_status_t status = zx::channel::create(0, &client, &server);
if (status != ZX_OK) {
return std::make_unique<llcpp::fuchsia::fshost::Admin::SyncClient>(zx::channel());
}
status = fidl::BindSingleInFlightOnly(dispatcher, std::move(server), this);
if (status != ZX_OK) {
LOGF(ERROR, "Failed to create client for mock fshost admin, failed to bind: %s",
zx_status_get_string(status));
return std::make_unique<llcpp::fuchsia::fshost::Admin::SyncClient>(zx::channel());
}
return std::make_unique<llcpp::fuchsia::fshost::Admin::SyncClient>(std::move(client));
}
void Shutdown(ShutdownCompleter::Sync& completer) override {
has_been_shutdown_ = true;
completer.Reply();
}
bool has_been_shutdown_;
};
class CoordinatorForTest : public Coordinator {
public:
CoordinatorForTest(CoordinatorConfig config, async_dispatcher_t* dispatcher)
: Coordinator(std::move(config), dispatcher) {}
void SetFshostAdminClient(std::unique_ptr<llcpp::fuchsia::fshost::Admin::SyncClient> client) {
fshost_admin_client_ = std::move(client);
}
MockFshostAdminServer admin_server_;
};
struct DeviceState {
DeviceState() = default;
DeviceState(DeviceState&& other)
: device(std::move(other.device)),
coordinator_remote(std::move(other.coordinator_remote)),
controller_remote(std::move(other.controller_remote)) {}
DeviceState& operator=(DeviceState&& other) {
device = std::move(other.device);
coordinator_remote = std::move(other.coordinator_remote);
controller_remote = std::move(other.controller_remote);
return *this;
}
~DeviceState() {
if (device) {
device->coordinator->RemoveDevice(device, false);
}
}
// The representation in the coordinator of the device
fbl::RefPtr<Device> device;
// The remote end of the channel that the coordinator is talking to
zx::channel coordinator_remote;
// The remote end of the channel that the controller is talking to
zx::channel controller_remote;
};
class MultipleDeviceTestCase : public zxtest::Test {
public:
~MultipleDeviceTestCase() override = default;
async::Loop* coordinator_loop() { return &coordinator_loop_; }
bool coordinator_loop_thread_running() { return coordinator_loop_thread_running_; }
void set_coordinator_loop_thread_running(bool value) { coordinator_loop_thread_running_ = value; }
CoordinatorForTest* coordinator() { return &coordinator_; }
const fbl::RefPtr<DriverHost>& driver_host() { return driver_host_; }
const zx::channel& driver_host_remote() { return driver_host_remote_; }
const fbl::RefPtr<Device>& platform_bus() const { return platform_bus_.device; }
const zx::channel& platform_bus_coordinator_remote() const {
return platform_bus_.coordinator_remote;
}
const zx::channel& platform_bus_controller_remote() const {
return platform_bus_.controller_remote;
}
DeviceState* device(size_t index) const { return &devices_[index]; }
void AddDevice(const fbl::RefPtr<Device>& parent, const char* name, uint32_t protocol_id,
fbl::String driver, bool invisible, bool has_init, bool reply_to_init,
bool always_init, zx::vmo inspect, size_t* device_index);
void AddDeviceSkipAutobind(const fbl::RefPtr<Device>& parent, const char* name,
uint32_t protocol_id, size_t* device_index);
void AddDevice(const fbl::RefPtr<Device>& parent, const char* name, uint32_t protocol_id,
fbl::String driver, size_t* device_index);
void RemoveDevice(size_t device_index);
bool DeviceHasPendingMessages(size_t device_index);
bool DeviceHasPendingMessages(const zx::channel& remote);
void DoSuspend(uint32_t flags);
void DoSuspend(uint32_t flags, fit::function<void(uint32_t)> suspend_cb);
void DoSuspendWithCallback(uint32_t flags, fit::function<void(zx_status_t)> suspend_complete_cb);
void DoResume(
SystemPowerState target_state, ResumeCallback callback = [](zx_status_t) {});
void DoResume(SystemPowerState target_state, fit::function<void(SystemPowerState)> resume_cb);
void CheckInitReceived(const zx::channel& remote, zx_txid_t* txid);
void SendInitReply(const zx::channel& remote, zx_txid_t txid, zx_status_t return_status = ZX_OK);
void CheckInitReceivedAndReply(const zx::channel& remote, zx_status_t return_status = ZX_OK);
void CheckUnbindReceived(const zx::channel& remote, zx_txid_t* txid);
void SendUnbindReply(const zx::channel& remote, zx_txid_t txid);
void CheckUnbindReceivedAndReply(const zx::channel& remote);
void CheckRemoveReceived(const zx::channel& remote, zx_txid_t* zxid);
void SendRemoveReply(const zx::channel& remote, zx_txid_t txid);
void CheckRemoveReceivedAndReply(const zx::channel& remote);
void CheckSuspendReceived(const zx::channel& remote, uint32_t expected_flags, zx_txid_t* txid);
void SendSuspendReply(const zx::channel& remote, zx_status_t return_status, zx_txid_t txid);
void CheckSuspendReceivedAndReply(const zx::channel& remote, uint32_t expected_flags,
zx_status_t return_status);
void CheckCreateDeviceReceived(const zx::channel& remote, const char* expected_driver,
zx::channel* device_coordinator_remote,
zx::channel* device_controller_remote);
void CheckResumeReceived(const zx::channel& remote, SystemPowerState target_state,
zx_txid_t* txid);
void SendResumeReply(const zx::channel& remote, zx_status_t return_status, zx_txid_t txid);
void CheckResumeReceived(const zx::channel& remote, SystemPowerState target_state,
zx_status_t return_status);
protected:
void SetUp() override;
void TearDown() override;
// These should be listed after driver_host/sys_proxy as it needs to be
// destroyed before them.
async::Loop coordinator_loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
bool coordinator_loop_thread_running_ = false;
mock_boot_arguments::Server boot_args_{{}};
llcpp::fuchsia::boot::Arguments::SyncClient args_client_{zx::channel()};
// The admin/bootargs servers need their own loop/thread, because if we schedule them
// on coordinator_loop then coordinator will deadlock waiting
// for itself to respond to its requests.
async::Loop mock_server_loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
CoordinatorForTest coordinator_{
DefaultConfig(mock_server_loop_.dispatcher(), &boot_args_, &args_client_),
coordinator_loop_.dispatcher()};
// The fake driver_host that the platform bus is put into
fbl::RefPtr<DriverHost> driver_host_;
// The remote end of the channel that the coordinator uses to talk to the
// driver_host
zx::channel driver_host_remote_;
// The remote end of the channel that the coordinator uses to talk to the
// sys device proxy
zx::channel sys_proxy_coordinator_remote_;
zx::channel sys_proxy_controller_remote_;
// The device object representing the platform bus driver (child of the
// sys proxy)
DeviceState platform_bus_;
// A list of all devices that were added during this test, and their
// channels. These exist to keep them alive until the test is over.
fbl::Vector<DeviceState> devices_;
};
#endif // SRC_DEVICES_BIN_DRIVER_MANAGER_MULTIPLE_DEVICE_TEST_H_