blob: fde5463164190fc59f62d9fa1dd8dd5918bc4130 [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 <fcntl.h>
#include <fidl/fuchsia.device.test/cpp/wire.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <lib/ddk/platform-defs.h>
#include <lib/devmgr-integration-test/fixture.h>
#include <lib/driver-integration-test/fixture.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/fdio.h>
#include <lib/zx/channel.h>
#include <lib/zx/vmo.h>
#include <limits.h>
#include <map>
#include <ddk/metadata/test.h>
#include <zxtest/zxtest.h>
#include "src/devices/bin/driver_host/test-metadata.h"
namespace {
using devmgr_integration_test::IsolatedDevmgr;
static constexpr const char kDriverTestDir[] = "/boot/driver";
static constexpr const char kPassDriverName[] = "unit-test-pass.so";
static constexpr const char kFailDriverName[] = "unit-test-fail.so";
void CreateTestDevice(const IsolatedDevmgr& devmgr, const char* driver_name,
zx::channel* dev_channel) {
fbl::unique_fd root_fd;
zx_status_t status =
device_watcher::RecursiveWaitForFile(devmgr.devfs_root(), "sys/test/test", &root_fd);
ASSERT_OK(status);
zx::status test_client_end =
fdio_cpp::FdioCaller(std::move(root_fd)).take_as<fuchsia_device_test::RootDevice>();
ASSERT_OK(test_client_end.status_value());
fidl::WireSyncClient test_root{std::move(*test_client_end)};
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
auto result =
test_root->CreateDevice(fidl::StringView::FromExternal(driver_name), std::move(remote));
ASSERT_OK(result.status());
ASSERT_OK(result.value().status);
*dev_channel = std::move(local);
}
// Test binding second time
TEST(DeviceControllerIntegrationTest, TestDuplicateBindSameDriver) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
args.sys_device_driver = "/boot/driver/test-parent-sys.so";
zx_status_t status = IsolatedDevmgr::Create(std::move(args), &devmgr);
ASSERT_OK(status);
zx::channel dev_channel;
CreateTestDevice(devmgr, kPassDriverName, &dev_channel);
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kPassDriverName);
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Bind(::fidl::StringView(libpath, len));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
call_status = ZX_OK;
auto resp2 = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Bind(::fidl::StringView(libpath, len));
ASSERT_OK(resp2.status());
if (resp2.value().is_error()) {
call_status = resp2.value().error_value();
}
ASSERT_OK(resp2.status());
ASSERT_EQ(call_status, ZX_ERR_ALREADY_BOUND);
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)fidl::WireCall<fuchsia_device_test::Device>(zx::unowned_channel{dev_channel})->Destroy();
}
TEST(DeviceControllerIntegrationTest, TestRebindNoChildrenManualBind) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
args.sys_device_driver = "/boot/driver/test-parent-sys.so";
zx_status_t status = IsolatedDevmgr::Create(std::move(args), &devmgr);
ASSERT_OK(status);
zx::channel dev_channel;
CreateTestDevice(devmgr, kPassDriverName, &dev_channel);
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kPassDriverName);
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Rebind(::fidl::StringView(libpath, len));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)fidl::WireCall<fuchsia_device_test::Device>(zx::unowned_channel{dev_channel})->Destroy();
}
TEST(DeviceControllerIntegrationTest, TestRebindChildrenAutoBind) {
using driver_integration_test::IsolatedDevmgr;
driver_integration_test::IsolatedDevmgr::Args args;
driver_integration_test::IsolatedDevmgr devmgr;
board_test::DeviceEntry dev = {};
struct devhost_test_metadata test_metadata = {
.init_reply_success = true,
};
dev.metadata = reinterpret_cast<const uint8_t*>(&test_metadata);
dev.metadata_size = sizeof(test_metadata);
dev.vid = PDEV_VID_TEST;
dev.pid = PDEV_PID_DEVHOST_TEST;
dev.did = 0;
args.device_list.push_back(dev);
zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr);
ASSERT_OK(status);
fbl::unique_fd test_fd, parent_fd, child_fd;
zx::channel parent_channel;
status =
device_watcher::RecursiveWaitForFile(devmgr.devfs_root(), "sys/platform/11:0e:0", &test_fd);
status = device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd);
ASSERT_OK(status);
status = fdio_get_service_handle(parent_fd.release(), parent_channel.reset_and_get_address());
ASSERT_OK(status);
// Do not open the child. Otherwise rebind will be stuck.
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(parent_channel))
->Rebind(::fidl::StringView(""));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent/devhost-test-child",
&child_fd));
}
TEST(DeviceControllerIntegrationTest, TestRebindChildrenManualBind) {
using driver_integration_test::IsolatedDevmgr;
driver_integration_test::IsolatedDevmgr::Args args;
driver_integration_test::IsolatedDevmgr devmgr;
board_test::DeviceEntry dev = {};
struct devhost_test_metadata test_metadata = {
.init_reply_success = true,
};
dev.metadata = reinterpret_cast<const uint8_t*>(&test_metadata);
dev.metadata_size = sizeof(test_metadata);
dev.vid = PDEV_VID_TEST;
dev.pid = PDEV_PID_DEVHOST_TEST;
dev.did = 0;
args.device_list.push_back(dev);
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr));
fbl::unique_fd test_fd, parent_fd, child_fd;
zx::channel parent_channel;
ASSERT_OK(
device_watcher::RecursiveWaitForFile(devmgr.devfs_root(), "sys/platform/11:0e:0", &test_fd));
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
ASSERT_OK(fdio_get_service_handle(parent_fd.release(), parent_channel.reset_and_get_address()));
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", "/boot/driver",
"driver-host-test-child-driver.so");
// Do not open the child. Otherwise rebind will be stuck.
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(parent_channel))
->Rebind(::fidl::StringView(libpath, len));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent/devhost-test-child",
&child_fd));
}
TEST(DeviceControllerIntegrationTest, TestUnbindChildrenSuccess) {
using driver_integration_test::IsolatedDevmgr;
driver_integration_test::IsolatedDevmgr::Args args;
driver_integration_test::IsolatedDevmgr devmgr;
board_test::DeviceEntry dev = {};
struct devhost_test_metadata test_metadata = {
.init_reply_success = true,
};
dev.metadata = reinterpret_cast<const uint8_t*>(&test_metadata);
dev.metadata_size = sizeof(test_metadata);
dev.vid = PDEV_VID_TEST;
dev.pid = PDEV_PID_DEVHOST_TEST;
dev.did = 0;
args.device_list.push_back(dev);
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr));
fbl::unique_fd test_fd, parent_fd, child_fd;
zx::channel parent_channel;
ASSERT_OK(
device_watcher::RecursiveWaitForFile(devmgr.devfs_root(), "sys/platform/11:0e:0", &test_fd));
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
ASSERT_OK(fdio_get_service_handle(parent_fd.release(), parent_channel.reset_and_get_address()));
zx_status_t call_status = ZX_OK;
auto resp =
fidl::WireCall<fuchsia_device::Controller>(zx::unowned(parent_channel))->UnbindChildren();
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
}
// Test binding again, but with different driver
TEST(DeviceControllerIntegrationTest, TestDuplicateBindDifferentDriver) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
args.sys_device_driver = "/boot/driver/test-parent-sys.so";
zx_status_t status = IsolatedDevmgr::Create(std::move(args), &devmgr);
ASSERT_OK(status);
zx::channel dev_channel;
CreateTestDevice(devmgr, kPassDriverName, &dev_channel);
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kPassDriverName);
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Bind(::fidl::StringView(libpath, len));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
call_status = ZX_OK;
len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kFailDriverName);
auto resp2 = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Bind(::fidl::StringView(libpath, len));
ASSERT_OK(resp2.status());
if (resp2.value().is_error()) {
call_status = resp2.value().error_value();
}
ASSERT_OK(resp2.status());
ASSERT_EQ(call_status, ZX_ERR_ALREADY_BOUND);
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)fidl::WireCall<fuchsia_device_test::Device>(zx::unowned_channel{dev_channel})->Destroy();
}
TEST(DeviceControllerIntegrationTest, AllTestsEnabledBind) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
args.sys_device_driver = "/boot/driver/test-parent-sys.so";
args.driver_tests_enable_all = true;
zx_status_t status = IsolatedDevmgr::Create(std::move(args), &devmgr);
ASSERT_OK(status);
zx::channel dev_channel;
CreateTestDevice(devmgr, kPassDriverName, &dev_channel);
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kPassDriverName);
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Bind(::fidl::StringView(libpath, len));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)fidl::WireCall<fuchsia_device_test::Device>(zx::unowned_channel{dev_channel})->Destroy();
}
TEST(DeviceControllerIntegrationTest, AllTestsEnabledBindFail) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
args.sys_device_driver = "/boot/driver/test-parent-sys.so";
args.driver_tests_enable_all = true;
zx_status_t status = IsolatedDevmgr::Create(std::move(args), &devmgr);
ASSERT_OK(status);
zx::channel dev_channel;
CreateTestDevice(devmgr, kFailDriverName, &dev_channel);
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kFailDriverName);
zx_status_t call_status;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Bind(::fidl::StringView(libpath, len));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_EQ(call_status, ZX_ERR_BAD_STATE);
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)fidl::WireCall<fuchsia_device_test::Device>(zx::unowned_channel{dev_channel})->Destroy();
}
// Test the flag using bind failure as a proxy for "the unit test did run".
TEST(DeviceControllerIntegrationTest, SpecificTestEnabledBindFail) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
args.sys_device_driver = "/boot/driver/test-parent-sys.so";
args.driver_tests_enable.push_back("unit_test_fail");
zx_status_t status = IsolatedDevmgr::Create(std::move(args), &devmgr);
ASSERT_OK(status);
zx::channel dev_channel;
CreateTestDevice(devmgr, kFailDriverName, &dev_channel);
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kFailDriverName);
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Bind(::fidl::StringView(libpath, len));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_EQ(call_status, ZX_ERR_BAD_STATE);
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)fidl::WireCall<fuchsia_device_test::Device>(zx::unowned_channel{dev_channel})->Destroy();
}
// Test the flag using bind success as a proxy for "the unit test didn't run".
TEST(DeviceControllerIntegrationTest, DefaultTestsDisabledBind) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
args.sys_device_driver = "/boot/driver/test-parent-sys.so";
zx_status_t status = IsolatedDevmgr::Create(std::move(args), &devmgr);
ASSERT_OK(status);
zx::channel dev_channel;
CreateTestDevice(devmgr, kFailDriverName, &dev_channel);
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kFailDriverName);
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Bind(::fidl::StringView(libpath, len));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)fidl::WireCall<fuchsia_device_test::Device>(zx::unowned_channel{dev_channel})->Destroy();
}
// Test the flag using bind success as a proxy for "the unit test didn't run".
TEST(DeviceControllerIntegrationTest, SpecificTestDisabledBind) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
args.sys_device_driver = "/boot/driver/test-parent-sys.so";
args.driver_tests_enable_all = true;
args.driver_tests_disable.push_back("unit_test_fail");
zx_status_t status = IsolatedDevmgr::Create(std::move(args), &devmgr);
ASSERT_OK(status);
zx::channel dev_channel;
CreateTestDevice(devmgr, kFailDriverName, &dev_channel);
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kFailDriverName);
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(dev_channel))
->Bind(::fidl::StringView(libpath, len));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)fidl::WireCall<fuchsia_device_test::Device>(zx::unowned_channel{dev_channel})->Destroy();
}
TEST(DeviceControllerIntegrationTest, TestRebindWithInit_Success) {
using driver_integration_test::IsolatedDevmgr;
driver_integration_test::IsolatedDevmgr::Args args;
driver_integration_test::IsolatedDevmgr devmgr;
board_test::DeviceEntry dev = {};
struct devhost_test_metadata test_metadata = {
.init_reply_success = true,
};
dev.metadata = reinterpret_cast<const uint8_t*>(&test_metadata);
dev.metadata_size = sizeof(test_metadata);
dev.vid = PDEV_VID_TEST;
dev.pid = PDEV_PID_DEVHOST_TEST;
dev.did = 0;
args.device_list.push_back(dev);
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr));
fbl::unique_fd test_fd, parent_fd, child_fd;
zx::channel test_channel, parent_channel;
ASSERT_OK(
device_watcher::RecursiveWaitForFile(devmgr.devfs_root(), "sys/platform/11:0e:0", &test_fd));
ASSERT_OK(fdio_get_service_handle(test_fd.release(), test_channel.reset_and_get_address()));
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
ASSERT_OK(fdio_get_service_handle(parent_fd.release(), parent_channel.reset_and_get_address()));
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(parent_channel))
->Rebind(::fidl::StringView(""));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_OK(call_status);
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent/devhost-test-child",
&child_fd));
}
TEST(DeviceControllerIntegrationTest, TestRebindWithInit_Failure) {
using driver_integration_test::IsolatedDevmgr;
driver_integration_test::IsolatedDevmgr::Args args;
driver_integration_test::IsolatedDevmgr devmgr;
board_test::DeviceEntry dev = {};
struct devhost_test_metadata test_metadata = {
.init_reply_success = false,
};
dev.metadata = reinterpret_cast<const uint8_t*>(&test_metadata);
dev.metadata_size = sizeof(test_metadata);
dev.vid = PDEV_VID_TEST;
dev.pid = PDEV_PID_DEVHOST_TEST;
dev.did = 0;
args.device_list.push_back(dev);
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr));
fbl::unique_fd test_fd, parent_fd, child_fd;
zx::channel test_channel, parent_channel;
ASSERT_OK(
device_watcher::RecursiveWaitForFile(devmgr.devfs_root(), "sys/platform/11:0e:0", &test_fd));
ASSERT_OK(fdio_get_service_handle(test_fd.release(), test_channel.reset_and_get_address()));
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
ASSERT_OK(fdio_get_service_handle(parent_fd.release(), parent_channel.reset_and_get_address()));
zx_status_t call_status = ZX_OK;
auto resp = fidl::WireCall<fuchsia_device::Controller>(zx::unowned(parent_channel))
->Rebind(::fidl::StringView(""));
ASSERT_OK(resp.status());
if (resp.value().is_error()) {
call_status = resp.value().error_value();
}
ASSERT_EQ(call_status, ZX_ERR_IO);
ASSERT_OK(device_watcher::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
}
} // namespace