blob: 12d792f021562926611f85c89cd02174b16fda9a [file] [log] [blame]
// Copyright 2021 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 <fidl/fuchsia.device.test/cpp/wire.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fuchsia/driver/development/cpp/fidl.h>
#include <fuchsia/driver/test/cpp/fidl.h>
#include <lib/ddk/binding.h>
#include <lib/ddk/driver.h>
#include <lib/driver_test_realm/realm_builder/cpp/lib.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/sys/cpp/component_context.h>
#include <sdk/lib/device-watcher/cpp/device-watcher.h>
#include "lib/fidl/cpp/synchronous_interface_ptr.h"
#include "src/lib/fxl/strings/string_printf.h"
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
const std::string kDevPrefix = "/dev/";
const std::string kDriverUrl = "fuchsia-boot:///#driver/bind-test-v2-driver.so";
const std::string kDriverLibname = "bind-test-v2-driver.so";
const std::string kChildDeviceName = "child";
class BindCompilerV2Test : public gtest::TestLoopFixture {
protected:
void SetUp() override {
auto realm_builder = component_testing::RealmBuilder::Create();
driver_test_realm::Setup(realm_builder);
realm_ = std::make_unique<component_testing::RealmRoot>(realm_builder.Build(dispatcher()));
fidl::SynchronousInterfacePtr<fuchsia::driver::test::Realm> driver_test_realm;
ASSERT_EQ(realm_->Connect(driver_test_realm.NewRequest()), ZX_OK);
fuchsia::driver::test::Realm_Start_Result realm_result;
ASSERT_EQ(driver_test_realm->Start(fuchsia::driver::test::RealmArgs(), &realm_result), ZX_OK);
ASSERT_FALSE(realm_result.is_err());
fidl::InterfaceHandle<fuchsia::io::Directory> dev;
zx_status_t status = realm_->Connect("dev", dev.NewRequest().TakeChannel());
ASSERT_EQ(status, ZX_OK);
fbl::unique_fd root_fd;
status = fdio_fd_create(dev.TakeChannel().release(), root_fd.reset_and_get_address());
ASSERT_EQ(status, ZX_OK);
// Wait for /dev/sys/test/test to appear, then create an endpoint to it.
fbl::unique_fd out;
status = device_watcher::RecursiveWaitForFile(root_fd, "sys/test/test", &out);
ASSERT_EQ(status, ZX_OK);
zx::status root_device_client_end =
fdio_cpp::FdioCaller(std::move(out)).take_as<fuchsia_device_test::RootDevice>();
ASSERT_EQ(root_device_client_end.status_value(), ZX_OK);
auto root_device = fidl::BindSyncClient(std::move(*root_device_client_end));
auto endpoints = fidl::CreateEndpoints<fuchsia_device::Controller>();
ASSERT_EQ(ZX_OK, endpoints.status_value());
// Create the root test device in /dev/sys/test/test, and get its relative path from /dev.
auto result = root_device->CreateDevice(fidl::StringView::FromExternal(kDriverLibname),
endpoints->server.TakeChannel());
ASSERT_EQ(result.status(), ZX_OK);
ASSERT_EQ(result.value().status, ZX_OK);
ASSERT_GE(result.value().path.size(), kDevPrefix.size());
ASSERT_EQ(strncmp(result.value().path.data(), kDevPrefix.c_str(), kDevPrefix.size()), 0);
relative_device_path_ = std::string(result.value().path.data() + kDevPrefix.size(),
result.value().path.size() - kDevPrefix.size());
// Bind the test driver to the new device.
auto response =
fidl::WireCall(endpoints->client)->Bind(::fidl::StringView::FromExternal(kDriverLibname));
status = response.status();
if (status == ZX_OK) {
if (response->is_error()) {
status = response->error_value();
}
}
ASSERT_EQ(status, ZX_OK);
// Connect to the DriverDevelopment service.
status = realm_->Connect(driver_dev_.NewRequest());
ASSERT_EQ(status, ZX_OK);
}
fuchsia::driver::development::DriverDevelopmentSyncPtr driver_dev_;
std::unique_ptr<component_testing::RealmRoot> realm_;
std::string relative_device_path_;
};
// Check that calling GetDriverInfo with an invalid driver path returns ZX_ERR_NOT_FOUND.
TEST_F(BindCompilerV2Test, InvalidDriver) {
fuchsia::driver::development::DriverInfoIteratorSyncPtr iterator;
ASSERT_EQ(driver_dev_->GetDriverInfo({"abc"}, iterator.NewRequest()), ZX_OK);
std::vector<fuchsia::driver::development::DriverInfo> drivers;
ASSERT_NE(iterator->GetNext(&drivers), ZX_OK);
}
// Get the bind program of the test driver and check that it has the expected instructions.
TEST_F(BindCompilerV2Test, ValidDriver) {
fuchsia::driver::development::DriverInfoIteratorSyncPtr iterator;
ASSERT_EQ(driver_dev_->GetDriverInfo({kDriverUrl}, iterator.NewRequest()), ZX_OK);
std::vector<fuchsia::driver::development::DriverInfo> drivers;
ASSERT_EQ(iterator->GetNext(&drivers), ZX_OK);
ASSERT_EQ(drivers.size(), 1u);
auto bytecode = drivers[0].bind_rules().bytecode_v2();
uint8_t expected_bytecode[] = {
0x42, 0x49, 0x4e, 0x44, 0x2, 0x0, 0x0, 0x0, // Bind header
0x53, 0x59, 0x4e, 0x42, 0x36, 0x0, 0x0, 0x0, // Symbol header
0x1, 0x0, 0x0, 0x0, // fuchsia.compat.LIBNAME symbol key
0x66, 0x75, 0x63, 0x68, 0x73, 0x69, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x70, // fuchsia.comp
0x61, 0x74, 0x2e, 0x4c, 0x49, 0x42, 0x4e, 0x41, 0x4d, 0x45, 0x0, // at.LIBNAME
0x2, 0x0, 0x0, 0x0, // bind-test-v2-driver.so symbol key
0x62, 0x69, 0x6e, 0x64, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x2d, 0x76, // bind-test-v
0x32, 0x2d, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x2e, 0x73, 0x6f, 0x0, // 2-driver.so
0x49, 0x4e, 0x53, 0x54, 0x16, 0x0, 0x0, 0x0, // Instruction header
0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x50, 0x0, 0x0, 0x0, // Device protocol condition
0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x0, // fuchsia.compat.LIBNAME ==
// bind-test-v2-driver.so
};
ASSERT_EQ(std::size(expected_bytecode), bytecode.size());
for (size_t i = 0; i < bytecode.size(); i++) {
ASSERT_EQ(expected_bytecode[i], bytecode[i]);
}
}
// Check that calling GetDeviceInfo with an invalid device path returns ZX_ERR_NOT_FOUND.
TEST_F(BindCompilerV2Test, InvalidDevice) {
fuchsia::driver::development::DeviceInfoIteratorSyncPtr iterator;
ASSERT_EQ(driver_dev_->GetDeviceInfo({"abc"}, iterator.NewRequest()), ZX_OK);
std::vector<fuchsia::driver::development::DeviceInfo> devices;
ASSERT_NE(iterator->GetNext(&devices), ZX_OK);
}
// Get the properties of the test driver's child device and check that they are as expected.
TEST_F(BindCompilerV2Test, ValidDevice) {
std::string child_device_path(relative_device_path_ + "/" + kChildDeviceName);
fuchsia::driver::development::DeviceInfoIteratorSyncPtr iterator;
ASSERT_EQ(driver_dev_->GetDeviceInfo({child_device_path}, iterator.NewRequest()), ZX_OK);
std::vector<fuchsia::driver::development::DeviceInfo> devices;
ASSERT_EQ(iterator->GetNext(&devices), ZX_OK);
ASSERT_EQ(devices.size(), 1u);
auto props = devices[0].property_list().props;
zx_device_prop_t expected_props[] = {
{BIND_PROTOCOL, 0, ZX_PROTOCOL_PCI},
{BIND_PCI_VID, 0, 1234},
{BIND_PCI_DID, 0, 1234},
};
ASSERT_EQ(props.size(), std::size(expected_props));
for (size_t i = 0; i < props.size(); i++) {
ASSERT_EQ(props[i].id, expected_props[i].id);
ASSERT_EQ(props[i].reserved, expected_props[i].reserved);
ASSERT_EQ(props[i].value, expected_props[i].value);
}
}