blob: ab61bad19c9579c35035fd3787465a6ef0ba96e3 [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.
#include <set>
#include <fbl/algorithm.h>
#include <fuchsia/device/manager/c/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fidl/coding.h>
#include <unittest/unittest.h>
#include <zircon/fidl.h>
#include "coordinator.h"
#include "devmgr.h"
namespace devmgr {
zx::channel fs_clone(const char* path) {
return zx::channel();
}
} // namespace devmgr
static devmgr::CoordinatorConfig default_config(async_dispatcher_t* dispatcher) {
devmgr::CoordinatorConfig config;
config.dispatcher = dispatcher;
config.require_system = false;
config.asan_drivers = false;
return config;
}
bool initialize_core_devices() {
BEGIN_TEST;
devmgr::Coordinator coordinator(default_config(nullptr));
zx_status_t status = coordinator.InitializeCoreDevices();
ASSERT_EQ(ZX_OK, status);
END_TEST;
}
bool open_virtcon() {
BEGIN_TEST;
devmgr::Coordinator coordinator(default_config(nullptr));
zx::channel client, server;
zx_status_t status = zx::channel::create(0, &client, &server);
ASSERT_EQ(ZX_OK, status);
coordinator.set_virtcon_channel(std::move(client));
zx::channel sender, receiver;
status = zx::channel::create(0, &sender, &receiver);
ASSERT_EQ(ZX_OK, status);
status = coordinator.DmOpenVirtcon(std::move(sender));
ASSERT_EQ(ZX_OK, status);
zx_signals_t signals;
status = server.wait_one(ZX_CHANNEL_READABLE, zx::time::infinite(), &signals);
ASSERT_EQ(ZX_OK, status);
ASSERT_TRUE(signals & ZX_CHANNEL_READABLE);
zx::channel sender_channel;
uint32_t actual_handles;
status = server.read(0, nullptr, 0, nullptr, sender_channel.reset_and_get_address(), 1,
&actual_handles);
ASSERT_EQ(ZX_OK, status);
ASSERT_EQ(1, actual_handles);
ASSERT_TRUE(sender_channel.is_valid());
END_TEST;
}
bool dump_state() {
BEGIN_TEST;
devmgr::Coordinator coordinator(default_config(nullptr));
zx_status_t status = coordinator.InitializeCoreDevices();
ASSERT_EQ(ZX_OK, status);
zx::socket client, server;
status = zx::socket::create(0, &client, &server);
ASSERT_EQ(ZX_OK, status);
coordinator.set_dmctl_socket(std::move(client));
coordinator.DumpState();
zx_signals_t signals;
status = server.wait_one(ZX_SOCKET_READABLE, zx::time::infinite(), &signals);
ASSERT_EQ(ZX_OK, status);
ASSERT_TRUE(signals & ZX_SOCKET_READABLE);
uint8_t buf[256];
size_t actual;
status = server.read(0, buf, sizeof(buf), &actual);
ASSERT_EQ(ZX_OK, status);
ASSERT_NE(0, actual);
END_TEST;
}
bool find_loadable_drivers() {
BEGIN_TEST;
bool found_driver = false;
auto callback = [&found_driver](devmgr::Driver* drv, const char* version) {
delete drv;
found_driver = true;
};
devmgr::find_loadable_drivers("/boot/driver/test", callback);
ASSERT_TRUE(found_driver);
END_TEST;
}
bool bind_drivers() {
BEGIN_TEST;
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
devmgr::Coordinator coordinator(default_config(loop.dispatcher()));
zx_status_t status = coordinator.InitializeCoreDevices();
ASSERT_EQ(ZX_OK, status);
coordinator.set_running(true);
std::set<const devmgr::Driver*> drivers;
auto callback = [&coordinator, &drivers](devmgr::Driver* drv, const char* version) {
drivers.insert(drv);
return coordinator.DriverAdded(drv, version);
};
devmgr::find_loadable_drivers("/boot/driver/test", callback);
loop.RunUntilIdle();
ASSERT_FALSE(coordinator.drivers().is_empty());
for (const devmgr::Driver& drv : coordinator.drivers()) {
ASSERT_TRUE(drivers.find(&drv) != drivers.end());
}
END_TEST;
}
bool bind_devices() {
BEGIN_TEST;
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
devmgr::Coordinator coordinator(default_config(loop.dispatcher()));
zx_status_t status = coordinator.InitializeCoreDevices();
ASSERT_EQ(ZX_OK, status);
// Initialize devfs.
devmgr::devfs_init(&coordinator.root_device(), loop.dispatcher());
status = devmgr::devfs_publish(&coordinator.root_device(), &coordinator.test_device());
ASSERT_EQ(ZX_OK, status);
coordinator.set_running(true);
// Add the device.
zx::channel local, remote;
status = zx::channel::create(0, &local, &remote);
ASSERT_EQ(ZX_OK, status);
status = coordinator.AddDevice(&coordinator.test_device(), std::move(local),
nullptr /* props_data */, 0 /* props_count */, "mock-device",
ZX_PROTOCOL_TEST, nullptr /* driver_path */, nullptr /* args */,
false /* invisible */, zx::channel() /* client_remote */);
ASSERT_EQ(ZX_OK, status);
ASSERT_EQ(1, coordinator.devices().size_slow());
// Add the driver.
devmgr::find_loadable_drivers(
"/boot/driver/test", fit::bind_member(&coordinator, &devmgr::Coordinator::DriverAdded));
loop.RunUntilIdle();
ASSERT_FALSE(coordinator.drivers().is_empty());
// Bind the device to a fake devhost.
devmgr::Device* dev = &coordinator.devices().front();
devmgr::Devhost host;
dev->host = &host;
status = coordinator.BindDevice(dev, "/boot/driver/test/mock-device.so");
ASSERT_EQ(ZX_OK, status);
// Wait for the BindDriver request.
zx_signals_t pending;
status = remote.wait_one(ZX_CHANNEL_READABLE, zx::time::infinite(), &pending);
ASSERT_EQ(ZX_OK, status);
ASSERT_TRUE(pending & ZX_CHANNEL_READABLE);
// Read the BindDriver request.
uint8_t bytes[ZX_CHANNEL_MAX_MSG_BYTES];
zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
uint32_t actual_bytes;
uint32_t actual_handles;
status = remote.read(0, bytes, sizeof(bytes), &actual_bytes, handles, fbl::count_of(handles),
&actual_handles);
ASSERT_EQ(ZX_OK, status);
ASSERT_LT(0, actual_bytes);
ASSERT_EQ(1, actual_handles);
status = zx_handle_close(handles[0]);
ASSERT_EQ(ZX_OK, status);
// Validate the BindDriver request.
auto hdr = reinterpret_cast<fidl_message_header_t*>(bytes);
ASSERT_EQ(fuchsia_device_manager_ControllerBindDriverOrdinal, hdr->ordinal);
status = fidl_decode(&fuchsia_device_manager_ControllerBindDriverRequestTable, bytes,
actual_bytes, handles, actual_handles, nullptr);
ASSERT_EQ(ZX_OK, status);
auto req = reinterpret_cast<fuchsia_device_manager_ControllerBindDriverRequest*>(bytes);
ASSERT_STR_EQ("/boot/driver/test/mock-device.so", req->driver_path.data);
// Write the BindDriver response.
memset(bytes, 0, sizeof(bytes));
auto resp = reinterpret_cast<fuchsia_device_manager_ControllerBindDriverResponse*>(bytes);
resp->hdr.ordinal = fuchsia_device_manager_ControllerBindDriverOrdinal;
resp->status = ZX_OK;
status = fidl_encode(&fuchsia_device_manager_ControllerBindDriverResponseTable, bytes,
sizeof(*resp), handles, fbl::count_of(handles), &actual_handles, nullptr);
ASSERT_EQ(ZX_OK, status);
ASSERT_EQ(0, actual_handles);
status = remote.write(0, bytes, sizeof(*resp), nullptr, 0);
ASSERT_EQ(ZX_OK, status);
loop.RunUntilIdle();
// Reset the fake devhost connection.
dev->host = nullptr;
remote.reset();
loop.RunUntilIdle();
END_TEST;
}
BEGIN_TEST_CASE(coordinator_tests)
RUN_TEST(initialize_core_devices)
RUN_TEST(open_virtcon)
RUN_TEST(dump_state)
RUN_TEST(find_loadable_drivers)
RUN_TEST(bind_drivers)
RUN_TEST(bind_devices)
END_TEST_CASE(coordinator_tests)