blob: d146460587b4cec8dae9323c4fd11a85ca76147f [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 <fuchsia/device/c/fidl.h>
#include <fuchsia/device/test/c/fidl.h>
#include <lib/devmgr-integration-test/fixture.h>
#include <lib/driver-integration-test/fixture.h>
#include <lib/fdio/fdio.h>
#include <lib/zx/channel.h>
#include <lib/zx/vmo.h>
#include <limits.h>
#include <ddk/metadata/test.h>
#include <ddk/platform-defs.h>
#include <zxtest/zxtest.h>
namespace {
using devmgr_integration_test::IsolatedDevmgr;
static constexpr const char kDevPrefix[] = "/dev/";
static constexpr const char kDriverTestDir[] = "/boot/driver/test";
static constexpr const char kPassDriverName[] = "unit-test-pass.so";
static constexpr const char kFailDriverName[] = "unit-test-fail.so";
zx_status_t GetArguments(const fbl::Vector<const char*>& arguments, zx::vmo* out,
uint32_t* length) {
size_t size = 0;
for (const char* arg : arguments) {
// Add 1 for the null byte.
size += strlen(arg) + 1;
}
zx::vmo vmo;
zx_status_t status = zx::vmo::create(size, 0, &vmo);
size_t offset = 0;
for (const char* arg : arguments) {
// Add 1 for the null byte.
size_t length = strlen(arg) + 1;
status = vmo.write(arg, offset, length);
if (status != ZX_OK) {
return status;
}
offset += length;
}
*length = static_cast<uint32_t>(size);
*out = std::move(vmo);
return status;
}
void CreateTestDevice(const IsolatedDevmgr& devmgr, const char* driver_name,
zx::channel* dev_channel) {
fbl::unique_fd root_fd;
zx_status_t status =
devmgr_integration_test::RecursiveWaitForFile(devmgr.devfs_root(), "test/test", &root_fd);
ASSERT_OK(status);
zx::channel test_root;
status = fdio_get_service_handle(root_fd.release(), test_root.reset_and_get_address());
ASSERT_OK(status);
char devpath[fuchsia_device_test_MAX_DEVICE_PATH_LEN + 1];
size_t devpath_count;
zx_status_t call_status;
status = fuchsia_device_test_RootDeviceCreateDevice(test_root.get(), driver_name,
strlen(driver_name), &call_status, devpath,
sizeof(devpath) - 1, &devpath_count);
ASSERT_OK(status);
ASSERT_OK(call_status);
devpath[devpath_count] = 0;
ASSERT_STR_NE(devpath, kDevPrefix);
const char* relative_devpath = devpath + strlen(kDevPrefix);
fbl::unique_fd fd;
status =
devmgr_integration_test::RecursiveWaitForFile(devmgr.devfs_root(), relative_devpath, &fd);
ASSERT_OK(status);
status = fdio_get_service_handle(fd.release(), dev_channel->reset_and_get_address());
ASSERT_OK(status);
}
// Test binding second time
TEST(DeviceControllerIntegrationTest, TestDuplicateBindSameDriver) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
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;
status = fuchsia_device_ControllerBind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_OK(call_status);
status = fuchsia_device_ControllerBind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_EQ(call_status, ZX_ERR_ALREADY_BOUND);
fuchsia_device_test_DeviceDestroy(dev_channel.get());
}
TEST(DeviceControllerIntegrationTest, TestRebindNoChildrenManualBind) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
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;
status = fuchsia_device_ControllerRebind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_OK(call_status);
fuchsia_device_test_DeviceDestroy(dev_channel.get());
}
TEST(DeviceControllerIntegrationTest, TestRebindChildrenAutoBind) {
using driver_integration_test::IsolatedDevmgr;
driver_integration_test::IsolatedDevmgr::Args args;
driver_integration_test::IsolatedDevmgr devmgr;
board_test::DeviceEntry dev = {};
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 = devmgr_integration_test::RecursiveWaitForFile(devmgr.devfs_root(),
"sys/platform/11:0e:0", &test_fd);
status = devmgr_integration_test::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);
zx_status_t call_status = ZX_OK;
// Do not open the child. Otherwise rebind will be stuck.
status = fuchsia_device_ControllerRebind(parent_channel.get(), "", 0, &call_status);
ASSERT_OK(status);
ASSERT_OK(call_status);
ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
ASSERT_OK(devmgr_integration_test::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 = {};
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(devmgr_integration_test::RecursiveWaitForFile(devmgr.devfs_root(),
"sys/platform/11:0e:0", &test_fd));
ASSERT_OK(devmgr_integration_test::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;
char libpath[PATH_MAX];
int len = snprintf(libpath, sizeof(libpath), "%s/%s", "/boot/driver", "devhost-test-child.so");
// Do not open the child. Otherwise rebind will be stuck.
ASSERT_OK(fuchsia_device_ControllerRebind(parent_channel.get(), libpath, len, &call_status));
ASSERT_OK(call_status);
ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent", &parent_fd));
ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile(
devmgr.devfs_root(), "sys/platform/11:0e:0/devhost-test-parent/devhost-test-child",
&child_fd));
}
// Test binding again, but with different driver
TEST(DeviceControllerIntegrationTest, TestDuplicateBindDifferentDriver) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
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;
status = fuchsia_device_ControllerBind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_OK(call_status);
len = snprintf(libpath, sizeof(libpath), "%s/%s", kDriverTestDir, kFailDriverName);
status = fuchsia_device_ControllerBind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_EQ(call_status, ZX_ERR_ALREADY_BOUND);
fuchsia_device_test_DeviceDestroy(dev_channel.get());
}
TEST(DeviceControllerIntegrationTest, AllTestsEnabledBind) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
fbl::Vector<const char*> arguments;
arguments.push_back("driver.tests.enable=true");
args.get_arguments = [&arguments](zx::vmo* out, uint32_t* length) {
return GetArguments(arguments, out, length);
};
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;
status = fuchsia_device_ControllerBind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_OK(call_status);
fuchsia_device_test_DeviceDestroy(dev_channel.get());
}
TEST(DeviceControllerIntegrationTest, AllTestsEnabledBindFail) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
fbl::Vector<const char*> arguments;
arguments.push_back("driver.tests.enable=true");
args.get_arguments = [&arguments](zx::vmo* out, uint32_t* length) {
return GetArguments(arguments, out, length);
};
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;
status = fuchsia_device_ControllerBind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_EQ(ZX_ERR_BAD_STATE, call_status);
fuchsia_device_test_DeviceDestroy(dev_channel.get());
}
// Test the flag using bind failure as a proxy for "the unit test did run".
TEST(DeviceControllerIntegrationTest, SpecificTestEnabledBindFail) {
IsolatedDevmgr devmgr;
auto args = IsolatedDevmgr::DefaultArgs();
fbl::Vector<const char*> arguments;
arguments.push_back("driver.unit_test_fail.tests.enable=true");
args.get_arguments = [&arguments](zx::vmo* out, uint32_t* length) {
return GetArguments(arguments, out, length);
};
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;
status = fuchsia_device_ControllerBind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_EQ(ZX_ERR_BAD_STATE, call_status);
fuchsia_device_test_DeviceDestroy(dev_channel.get());
}
// 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();
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;
status = fuchsia_device_ControllerBind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_OK(call_status);
fuchsia_device_test_DeviceDestroy(dev_channel.get());
}
// 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();
fbl::Vector<const char*> arguments;
arguments.push_back("driver.tests.enable=true");
arguments.push_back("driver.unit_test_fail.tests.enable=false");
args.get_arguments = [&arguments](zx::vmo* out, uint32_t* length) {
return GetArguments(arguments, out, length);
};
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;
status = fuchsia_device_ControllerBind(dev_channel.get(), libpath, len, &call_status);
ASSERT_OK(status);
ASSERT_OK(call_status);
fuchsia_device_test_DeviceDestroy(dev_channel.get());
}
} // namespace