blob: 398b9e06ae9d0c77cd1a27e0e43a09c512d54a7b [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_DRIVER_MANAGER_MULTIPLE_DEVICE_TEST_H_
#define SRC_DEVICES_DRIVER_MANAGER_MULTIPLE_DEVICE_TEST_H_
#include <fuchsia/fshost/llcpp/fidl.h>
#include <lib/fidl-async/cpp/bind.h>
#include <zxtest/zxtest.h>
#include "coordinator-test-utils.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) {
printf(
"devcoordinator: failed to create client for mock fshost admin, failed to create "
"channel: %s\n",
zx_status_get_string(status));
return std::make_unique<llcpp::fuchsia::fshost::Admin::SyncClient>(zx::channel());
}
status = fidl::Bind(dispatcher, std::move(server), this);
if (status != ZX_OK) {
printf("devcoordinator: failed to create client for mock fshost admin, failed to bind: %s\n",
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 devmgr::Coordinator {
public:
CoordinatorForTest(devmgr::CoordinatorConfig config) : devmgr::Coordinator(std::move(config)) {}
void SetFshostAdminClient(std::unique_ptr<llcpp::fuchsia::fshost::Admin::SyncClient> client) {
fshost_admin_client_ = std::move(client);
}
MockFshostAdminServer admin_server_;
};
struct DeviceState {
// The representation in the coordinator of the device
fbl::RefPtr<devmgr::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_; }
devmgr::Devhost* devhost() { return &devhost_; }
const zx::channel& devhost_remote() { return devhost_remote_; }
const fbl::RefPtr<devmgr::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<devmgr::Device>& parent, const char* name, uint32_t protocol_id,
fbl::String driver, bool invisible, bool has_init, bool reply_to_init,
bool always_init, size_t* device_index);
void AddDevice(const fbl::RefPtr<devmgr::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 DoResume(
SystemPowerState target_state, devmgr::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;
// The fake devhost that the platform bus is put into
devmgr::Devhost devhost_;
// The remote end of the channel that the coordinator uses to talk to the
// devhost
zx::channel devhost_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_;
// These should be listed after devhost/sys_proxy as it needs to be
// destroyed before them.
async::Loop coordinator_loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
bool coordinator_loop_thread_running_ = false;
devmgr::BootArgs boot_args_;
// The admin server needs its own loop/thread, because if we schedule the
// admin server on coordinator_loop then coordinator will deadlock waiting
// for itself to respond to its shutdown request.
async::Loop admin_server_loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
CoordinatorForTest coordinator_{DefaultConfig(coordinator_loop_.dispatcher(), &boot_args_)};
// 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_DRIVER_MANAGER_MULTIPLE_DEVICE_TEST_H_