| // 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 <fuchsia/driver/test/llcpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/ddk/binding.h> |
| #include <lib/ddk/driver.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fidl/coding.h> |
| #include <lib/fidl/cpp/message.h> |
| #include <lib/fidl/cpp/message_builder.h> |
| #include <string.h> |
| #include <threads.h> |
| #include <zircon/fidl.h> |
| |
| #include <vector> |
| |
| #include <fbl/algorithm.h> |
| #include <fbl/vector.h> |
| #include <zxtest/zxtest.h> |
| |
| #include "coordinator_test_utils.h" |
| #include "devfs.h" |
| #include "driver_host.h" |
| #include "driver_test_reporter.h" |
| #include "fdio.h" |
| |
| constexpr char kDriverPath[] = "/pkg/driver/test/mock-device.so"; |
| constexpr char kLogMessage[] = "log message text"; |
| constexpr char kLogTestCaseName[] = "log test case"; |
| constexpr fuchsia_driver_test::wire::TestCaseResult kLogTestCaseResult = { |
| .passed = 1, |
| .failed = 2, |
| .skipped = 3, |
| }; |
| |
| static CoordinatorConfig NullConfig() { return DefaultConfig(nullptr, nullptr, nullptr); } |
| |
| namespace { |
| |
| class FidlTransaction : public fidl::Transaction { |
| public: |
| FidlTransaction(FidlTransaction&&) = default; |
| explicit FidlTransaction(zx_txid_t transaction_id, zx::unowned_channel channel) |
| : txid_(transaction_id), channel_(channel) {} |
| |
| std::unique_ptr<fidl::Transaction> TakeOwnership() override { |
| return std::make_unique<FidlTransaction>(std::move(*this)); |
| } |
| |
| zx_status_t Reply(fidl::OutgoingMessage* message) override { |
| ZX_ASSERT(txid_ != 0); |
| message->set_txid(txid_); |
| txid_ = 0; |
| message->Write(channel_); |
| return message->status(); |
| } |
| |
| void Close(zx_status_t epitaph) override { ZX_ASSERT(false); } |
| |
| ~FidlTransaction() override = default; |
| |
| private: |
| zx_txid_t txid_; |
| zx::unowned_channel channel_; |
| }; |
| |
| class FakeDevice : public fidl::WireServer<fuchsia_device_manager::DeviceController> { |
| public: |
| FakeDevice(fidl::ServerEnd<fuchsia_driver_test::Logger> test_output, |
| const fidl::StringView expected_driver = {}) |
| : test_output_(std::move(test_output)), expected_driver_(expected_driver) {} |
| |
| void BindDriver(BindDriverRequestView request, BindDriverCompleter::Sync& completer) override { |
| if (expected_driver_.empty() || expected_driver_.get() == request->driver_path.get()) { |
| bind_called_ = true; |
| completer.Reply(ZX_OK, test_output_.TakeChannel()); |
| } else { |
| completer.Reply(ZX_ERR_INTERNAL, zx::channel{}); |
| } |
| } |
| void ConnectProxy(ConnectProxyRequestView request, |
| ConnectProxyCompleter::Sync& _completer) override {} |
| void Init(InitRequestView request, InitCompleter::Sync& completer) override {} |
| void Suspend(SuspendRequestView request, SuspendCompleter::Sync& completer) override {} |
| void Resume(ResumeRequestView request, ResumeCompleter::Sync& completer) override {} |
| void Unbind(UnbindRequestView request, UnbindCompleter::Sync& completer) override {} |
| void CompleteRemoval(CompleteRemovalRequestView request, |
| CompleteRemovalCompleter::Sync& completer) override {} |
| void Open(OpenRequestView request, OpenCompleter::Sync& _completer) override {} |
| |
| bool bind_called() { return bind_called_; } |
| |
| private: |
| fidl::ServerEnd<fuchsia_driver_test::Logger> test_output_; |
| const fidl::StringView expected_driver_; |
| bool bind_called_ = false; |
| }; |
| |
| // Reads a BindDriver request from remote, checks that it is for the expected |
| // driver, and then sends a ZX_OK response. |
| void BindDriverTestOutput( |
| const fidl::ServerEnd<fuchsia_device_manager::DeviceController>& controller, |
| fidl::ServerEnd<fuchsia_driver_test::Logger> test_output) { |
| uint8_t bytes[ZX_CHANNEL_MAX_MSG_BYTES]; |
| zx_handle_info_t handles[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| fidl::IncomingMessage msg = |
| fidl::ChannelReadEtc(controller.channel().get(), 0, fidl::BufferSpan(bytes, std::size(bytes)), |
| cpp20::span(handles)); |
| ASSERT_TRUE(msg.ok()); |
| |
| auto* header = msg.header(); |
| FidlTransaction txn(header->txid, zx::unowned(controller.channel())); |
| |
| FakeDevice fake(std::move(test_output)); |
| ASSERT_EQ(fidl::WireDispatch( |
| static_cast<fidl::WireServer<fuchsia_device_manager::DeviceController>*>(&fake), |
| std::move(msg), &txn), |
| fidl::DispatchResult::kFound); |
| ASSERT_TRUE(fake.bind_called()); |
| } |
| |
| // Reads a BindDriver request from remote, checks that it is for the expected |
| // driver, and then sends a ZX_OK response. |
| void CheckBindDriverReceived( |
| const fidl::ServerEnd<fuchsia_device_manager::DeviceController>& controller, |
| const fidl::StringView expected_driver) { |
| uint8_t bytes[ZX_CHANNEL_MAX_MSG_BYTES]; |
| zx_handle_info_t handles[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| fidl::IncomingMessage msg = |
| fidl::ChannelReadEtc(controller.channel().get(), 0, fidl::BufferSpan(bytes, std::size(bytes)), |
| cpp20::span(handles)); |
| ASSERT_TRUE(msg.ok()); |
| |
| auto* header = msg.header(); |
| FidlTransaction txn(header->txid, zx::unowned(controller.channel())); |
| |
| FakeDevice fake(fidl::ServerEnd<fuchsia_driver_test::Logger>(), expected_driver); |
| ASSERT_EQ(fidl::WireDispatch( |
| static_cast<fidl::WireServer<fuchsia_device_manager::DeviceController>*>(&fake), |
| std::move(msg), &txn), |
| fidl::DispatchResult::kFound); |
| ASSERT_TRUE(fake.bind_called()); |
| } |
| |
| } // namespace |
| |
| class TestDriverTestReporter : public DriverTestReporter { |
| public: |
| explicit TestDriverTestReporter(const fbl::String& driver_name) |
| : DriverTestReporter(driver_name) {} |
| |
| void LogMessage(LogMessageRequestView request, LogMessageCompleter::Sync& completer) override { |
| if (request->msg.get() == kLogMessage) { |
| log_message_called = true; |
| } |
| } |
| |
| void LogTestCase(LogTestCaseRequestView request, LogTestCaseCompleter::Sync& completer) override { |
| if (request->name.get() == kLogTestCaseName && |
| memcmp(&request->result, &kLogTestCaseResult, |
| std::min(sizeof(request->result), sizeof(kLogTestCaseResult))) == 0) { |
| log_test_case_called = true; |
| } |
| } |
| |
| void TestStart() override { start_called = true; } |
| |
| void TestFinished() override { finished_called = true; } |
| |
| bool log_message_called = false; |
| bool log_test_case_called = false; |
| bool start_called = false; |
| bool finished_called = false; |
| }; |
| |
| TEST(MiscTestCase, InitCoreDevices) { |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| InspectManager inspect_manager(loop.dispatcher()); |
| Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher()); |
| |
| zx_status_t status = coordinator.InitCoreDevices(kSystemDriverPath); |
| ASSERT_OK(status); |
| } |
| |
| TEST(MiscTestCase, DumpState) { |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| InspectManager inspect_manager(loop.dispatcher()); |
| Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher()); |
| |
| zx_status_t status = coordinator.InitCoreDevices(kSystemDriverPath); |
| ASSERT_OK(status); |
| |
| constexpr int32_t kBufSize = 256; |
| char buf[kBufSize + 1] = {0}; |
| |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(kBufSize, 0, &vmo)); |
| VmoWriter writer(std::move(vmo)); |
| |
| coordinator.DumpState(&writer); |
| |
| ASSERT_EQ(writer.written(), writer.available()); |
| ASSERT_LT(writer.written(), kBufSize); |
| ASSERT_GT(writer.written(), 0); |
| ASSERT_OK(writer.vmo().read(buf, 0, writer.written())); |
| |
| ASSERT_NE(nullptr, strstr(buf, "[root]")); |
| } |
| |
| TEST(MiscTestCase, LoadDriver) { |
| bool found_driver = false; |
| auto callback = [&found_driver](Driver* drv, const char* version) { |
| delete drv; |
| found_driver = true; |
| }; |
| load_driver(nullptr, kDriverPath, callback); |
| ASSERT_TRUE(found_driver); |
| } |
| |
| TEST(MiscTestCase, LoadDisabledDriver) { |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| ASSERT_OK(loop.StartThread("mock-boot-args")); |
| mock_boot_arguments::Server boot_args{{{"driver.mock_device.disable", "true"}}}; |
| fidl::WireSyncClient<fuchsia_boot::Arguments> client; |
| boot_args.CreateClient(loop.dispatcher(), &client); |
| |
| bool found_driver = false; |
| auto callback = [&found_driver](Driver* drv, const char* version) { |
| delete drv; |
| found_driver = true; |
| }; |
| load_driver(&client, kDriverPath, callback); |
| ASSERT_FALSE(found_driver); |
| } |
| |
| TEST(MiscTestCase, BindDrivers) { |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| InspectManager inspect_manager(loop.dispatcher()); |
| Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher()); |
| |
| zx_status_t status = coordinator.InitCoreDevices(kSystemDriverPath); |
| ASSERT_OK(status); |
| coordinator.set_running(true); |
| |
| Driver* driver; |
| auto callback = [&coordinator, &driver](Driver* drv, const char* version) { |
| driver = drv; |
| return coordinator.DriverAdded(drv, version); |
| }; |
| load_driver(nullptr, kDriverPath, callback); |
| loop.RunUntilIdle(); |
| ASSERT_EQ(1, coordinator.drivers().size_slow()); |
| ASSERT_EQ(driver, &coordinator.drivers().front()); |
| } |
| |
| // Test binding drivers against the root/test/misc devices |
| TEST(MiscTestCase, BindDriversForBuiltins) { |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| InspectManager inspect_manager(loop.dispatcher()); |
| Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher()); |
| |
| zx_status_t status = coordinator.InitCoreDevices(kSystemDriverPath); |
| ASSERT_OK(status); |
| |
| // AttemptBind function that asserts it has only been called once |
| class CallOnce { |
| public: |
| explicit CallOnce(size_t line) : line_number_(line) {} |
| CallOnce(const CallOnce&) = delete; |
| CallOnce& operator=(const CallOnce&) = delete; |
| |
| CallOnce(CallOnce&& other) { *this = std::move(other); } |
| CallOnce& operator=(CallOnce&& other) { |
| if (this != &other) { |
| line_number_ = other.line_number_; |
| call_count_ = other.call_count_; |
| // Ensure the dtor for the other one doesn't run |
| other.call_count_ = 1; |
| } |
| return *this; |
| } |
| |
| ~CallOnce() { EXPECT_EQ(1, call_count_, "Mismatch from line %zu\n", line_number_); } |
| zx_status_t operator()(const Driver* drv, const fbl::RefPtr<Device>& dev) { |
| ++call_count_; |
| return ZX_OK; |
| } |
| |
| private: |
| size_t line_number_; |
| size_t call_count_ = 0; |
| }; |
| |
| auto make_fake_driver = [](auto&& instructions) -> std::unique_ptr<Driver> { |
| size_t instruction_count = std::size(instructions); |
| auto binding = std::make_unique<zx_bind_inst_t[]>(instruction_count); |
| memcpy(binding.get(), instructions, instruction_count * sizeof(instructions[0])); |
| auto drv = std::make_unique<Driver>(); |
| drv->binding = std::move(binding); |
| drv->bytecode_version = 1; |
| drv->binding_size = static_cast<uint32_t>(instruction_count * sizeof(instructions[0])); |
| return drv; |
| }; |
| |
| { |
| zx_bind_inst_t test_drv_bind[] = { |
| BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_TEST_PARENT), |
| }; |
| auto test_drv = make_fake_driver(test_drv_bind); |
| ASSERT_OK(coordinator.BindDriver(test_drv.get(), CallOnce{__LINE__})); |
| } |
| |
| { |
| zx_bind_inst_t root_drv_bind[] = { |
| BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_ROOT), |
| }; |
| auto root_drv = make_fake_driver(root_drv_bind); |
| ASSERT_OK(coordinator.BindDriver(root_drv.get(), CallOnce{__LINE__})); |
| } |
| |
| { |
| zx_bind_inst_t test_drv_bind[] = { |
| BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_TEST_PARENT), |
| BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_I2C), |
| }; |
| auto test_drv = make_fake_driver(test_drv_bind); |
| ASSERT_OK(coordinator.BindDriver(test_drv.get(), CallOnce{__LINE__})); |
| } |
| |
| { |
| zx_bind_inst_t root_drv_bind[] = { |
| BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_ROOT), |
| BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_I2C), |
| }; |
| auto root_drv = make_fake_driver(root_drv_bind); |
| ASSERT_OK(coordinator.BindDriver(root_drv.get(), CallOnce{__LINE__})); |
| } |
| } |
| |
| TEST(MiscTestCase, BindDevices) { |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| InspectManager inspect_manager(loop.dispatcher()); |
| Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher()); |
| |
| ASSERT_NO_FATAL_FAILURES(InitializeCoordinator(&coordinator)); |
| |
| // Add the device. |
| auto controller_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>(); |
| ASSERT_OK(controller_endpoints.status_value()); |
| |
| auto coordinator_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::Coordinator>(); |
| ASSERT_OK(coordinator_endpoints.status_value()); |
| |
| fbl::RefPtr<Device> device; |
| auto status = coordinator.AddDevice( |
| coordinator.test_device(), std::move(controller_endpoints->client), |
| std::move(coordinator_endpoints->server), nullptr /* props_data */, 0 /* props_count */, |
| nullptr /* str_props_data */, 0 /* str_props_count */, "mock-device", ZX_PROTOCOL_TEST, |
| {} /* driver_path */, {} /* args */, false /* skip_autobind */, false /* has_init */, |
| true /* always_init */, zx::vmo() /*inspect*/, zx::channel() /* client_remote */, &device); |
| ASSERT_OK(status); |
| ASSERT_EQ(1, coordinator.devices().size_slow()); |
| |
| // Add the driver. |
| load_driver(nullptr, kDriverPath, fit::bind_member(&coordinator, &Coordinator::DriverAdded)); |
| loop.RunUntilIdle(); |
| ASSERT_FALSE(coordinator.drivers().is_empty()); |
| |
| // The device has no driver_host, so the init task should automatically complete. |
| ASSERT_TRUE(device->is_visible()); |
| ASSERT_EQ(Device::State::kActive, device->state()); |
| |
| // Bind the device to a fake driver_host. |
| fbl::RefPtr<Device> dev = fbl::RefPtr(&coordinator.devices().front()); |
| auto host = fbl::MakeRefCounted<DriverHost>( |
| &coordinator, fidl::ClientEnd<fuchsia_device_manager::DriverHostController>(), |
| fidl::ClientEnd<fuchsia_io::Directory>(), zx::process{}); |
| dev->set_host(std::move(host)); |
| status = coordinator.BindDevice(dev, kDriverPath, true /* new device */); |
| ASSERT_OK(status); |
| |
| // Check the BindDriver request. |
| ASSERT_NO_FATAL_FAILURES( |
| CheckBindDriverReceived(controller_endpoints->server, fidl::StringView(kDriverPath))); |
| loop.RunUntilIdle(); |
| |
| // Reset the fake driver_host connection. |
| dev->set_host(nullptr); |
| coordinator_endpoints->client.reset(); |
| controller_endpoints->server.reset(); |
| loop.RunUntilIdle(); |
| } |
| |
| TEST(MiscTestCase, TestOutput) { |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| InspectManager inspect_manager(loop.dispatcher()); |
| Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher()); |
| |
| ASSERT_NO_FATAL_FAILURES(InitializeCoordinator(&coordinator)); |
| |
| // Add the device. |
| auto controller_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>(); |
| ASSERT_OK(controller_endpoints.status_value()); |
| |
| auto coordinator_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::Coordinator>(); |
| ASSERT_OK(coordinator_endpoints.status_value()); |
| |
| fbl::RefPtr<Device> device; |
| auto status = coordinator.AddDevice( |
| coordinator.test_device(), std::move(controller_endpoints->client), |
| std::move(coordinator_endpoints->server), nullptr /* props_data */, 0 /* props_count */, |
| nullptr /* str_props_data */, 0 /* str_props_count */, "mock-device", ZX_PROTOCOL_TEST, |
| {} /* driver_path */, {} /* args */, false /* skip_autobind */, false /* has_init */, |
| true /* always_init */, zx::vmo() /*inspect*/, zx::channel() /* client_remote */, &device); |
| ASSERT_OK(status); |
| ASSERT_EQ(1, coordinator.devices().size_slow()); |
| |
| fbl::String driver_name; |
| auto test_reporter_ = std::make_unique<TestDriverTestReporter>(driver_name); |
| auto* test_reporter = test_reporter_.get(); |
| device->test_reporter = std::move(test_reporter_); |
| |
| // Add the driver. |
| load_driver(nullptr, kDriverPath, fit::bind_member(&coordinator, &Coordinator::DriverAdded)); |
| loop.RunUntilIdle(); |
| ASSERT_FALSE(coordinator.drivers().is_empty()); |
| |
| // The device has no driver_host, so the init task should automatically complete. |
| ASSERT_TRUE(device->is_visible()); |
| ASSERT_EQ(Device::State::kActive, device->state()); |
| |
| // Bind the device to a fake driver_host. |
| fbl::RefPtr<Device> dev = fbl::RefPtr(&coordinator.devices().front()); |
| auto host = fbl::MakeRefCounted<DriverHost>( |
| &coordinator, fidl::ClientEnd<fuchsia_device_manager::DriverHostController>(), |
| fidl::ClientEnd<fuchsia_io::Directory>(), zx::process{}); |
| dev->set_host(std::move(host)); |
| status = coordinator.BindDevice(dev, kDriverPath, true /* new device */); |
| ASSERT_OK(status); |
| |
| // Check the BindDriver request. |
| zx::status test_endpoints = fidl::CreateEndpoints<fuchsia_driver_test::Logger>(); |
| ASSERT_OK(test_endpoints.status_value()); |
| ASSERT_NO_FATAL_FAILURES( |
| BindDriverTestOutput(controller_endpoints->server, std::move(test_endpoints->server))); |
| loop.RunUntilIdle(); |
| |
| ASSERT_OK(fidl::WireCall(test_endpoints->client).LogMessage(kLogMessage).status()); |
| ASSERT_OK(fidl::WireCall(test_endpoints->client) |
| .LogTestCase(kLogTestCaseName, kLogTestCaseResult) |
| .status()); |
| loop.RunUntilIdle(); |
| |
| // The test logging handlers should not be called until the test is finished and the channel is |
| // closed. |
| EXPECT_FALSE(test_reporter->start_called); |
| EXPECT_FALSE(test_reporter->log_message_called); |
| EXPECT_FALSE(test_reporter->log_test_case_called); |
| EXPECT_FALSE(test_reporter->finished_called); |
| |
| test_endpoints->client.reset(); |
| loop.RunUntilIdle(); |
| EXPECT_TRUE(test_reporter->start_called); |
| EXPECT_TRUE(test_reporter->log_message_called); |
| EXPECT_TRUE(test_reporter->log_test_case_called); |
| EXPECT_TRUE(test_reporter->finished_called); |
| |
| // Reset the fake driver_host connection. |
| dev->set_host(nullptr); |
| controller_endpoints->server.reset(); |
| coordinator_endpoints->client.reset(); |
| loop.RunUntilIdle(); |
| } |
| |
| void CompareStrProperty(const fuchsia_device_manager::wire::DeviceStrProperty expected, |
| const StrProperty actual) { |
| ASSERT_STR_EQ(expected.key.get(), actual.key); |
| |
| if (expected.value.is_int_value()) { |
| auto* value = std::get_if<uint32_t>(&actual.value); |
| ASSERT_TRUE(value); |
| ASSERT_EQ(expected.value.int_value(), *value); |
| } else if (expected.value.is_str_value()) { |
| auto* value = std::get_if<std::string>(&actual.value); |
| ASSERT_TRUE(value); |
| ASSERT_STR_EQ(expected.value.str_value(), *value); |
| } else if (expected.value.is_bool_value()) { |
| auto* value = std::get_if<bool>(&actual.value); |
| ASSERT_TRUE(value); |
| ASSERT_EQ(expected.value.bool_value(), *value); |
| } |
| } |
| |
| // Adds a device with the given properties to the device coordinator, then checks that the |
| // coordinator contains the device, and that its properties are correct. |
| void AddDeviceWithProperties(const fuchsia_device_manager::wire::DeviceProperty* props_data, |
| size_t props_count, |
| const fuchsia_device_manager::wire::DeviceStrProperty* str_props_data, |
| size_t str_props_count) { |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| InspectManager inspect_manager(loop.dispatcher()); |
| Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher()); |
| |
| ASSERT_NO_FATAL_FAILURES(InitializeCoordinator(&coordinator)); |
| |
| auto controller_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>(); |
| ASSERT_OK(controller_endpoints.status_value()); |
| |
| auto coordinator_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::Coordinator>(); |
| ASSERT_OK(coordinator_endpoints.status_value()); |
| |
| fbl::RefPtr<Device> device; |
| auto status = coordinator.AddDevice( |
| coordinator.test_device(), std::move(controller_endpoints->client), |
| std::move(coordinator_endpoints->server), props_data, props_count, str_props_data, |
| str_props_count, "mock-device", ZX_PROTOCOL_TEST, {} /* driver_path */, {} /* args */, |
| false /* skip_autobind */, false /* has_init */, true /* always_init */, |
| zx::vmo() /*inspect*/, zx::channel() /* client_remote */, &device); |
| ASSERT_OK(status); |
| |
| // Check that the device has been added to the coordinator, with the correct properties. |
| ASSERT_EQ(1, coordinator.devices().size_slow()); |
| const Device& dev = coordinator.devices().front(); |
| ASSERT_EQ(dev.props().size(), props_count); |
| for (size_t i = 0; i < props_count; i++) { |
| ASSERT_EQ(dev.props()[i].id, props_data[i].id); |
| ASSERT_EQ(dev.props()[i].reserved, props_data[i].reserved); |
| ASSERT_EQ(dev.props()[i].value, props_data[i].value); |
| } |
| |
| ASSERT_EQ(dev.str_props().size(), str_props_count); |
| for (size_t i = 0; i < str_props_count; i++) { |
| CompareStrProperty(str_props_data[i], dev.str_props()[i]); |
| } |
| |
| controller_endpoints->server.reset(); |
| coordinator_endpoints->client.reset(); |
| loop.RunUntilIdle(); |
| } |
| |
| TEST(MiscTestCase, DeviceProperties) { |
| // No properties. |
| AddDeviceWithProperties(nullptr, 0, nullptr, 0); |
| |
| // Multiple device properties. No string properties. |
| fuchsia_device_manager::wire::DeviceProperty props[] = { |
| fuchsia_device_manager::wire::DeviceProperty{1, 0, 1}, |
| fuchsia_device_manager::wire::DeviceProperty{2, 0, 1}, |
| }; |
| AddDeviceWithProperties(props, std::size(props), nullptr, 0); |
| |
| uint32_t int_val = 1000; |
| auto str_val = fidl::StringView::FromExternal("timberdoodle"); |
| |
| // Multiple device string properties. No device properties. |
| fuchsia_device_manager::wire::DeviceStrProperty str_props[] = { |
| fuchsia_device_manager::wire::DeviceStrProperty{ |
| "snipe", fuchsia_device_manager::wire::PropertyValue::WithStrValue( |
| fidl::ObjectView<fidl::StringView>::FromExternal(&str_val))}, |
| fuchsia_device_manager::wire::DeviceStrProperty{ |
| "sandpiper", fuchsia_device_manager::wire::PropertyValue::WithIntValue( |
| fidl::ObjectView<uint32_t>::FromExternal(&int_val))}, |
| }; |
| AddDeviceWithProperties(nullptr, 0, str_props, std::size(str_props)); |
| |
| // Multiple device properties and device string properties. |
| AddDeviceWithProperties(props, std::size(props), str_props, std::size(str_props)); |
| } |
| |
| TEST(MiscTestCase, InvalidStringProperties) { |
| async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread); |
| InspectManager inspect_manager(loop.dispatcher()); |
| Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher()); |
| |
| ASSERT_NO_FATAL_FAILURES(InitializeCoordinator(&coordinator)); |
| |
| auto controller_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>(); |
| ASSERT_OK(controller_endpoints.status_value()); |
| |
| auto coordinator_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::Coordinator>(); |
| ASSERT_OK(coordinator_endpoints.status_value()); |
| |
| // Create an invalid string with invalid UTF-8 characters. |
| const char kInvalidStr[] = {static_cast<char>(0xC0), 0}; |
| auto str_val = fidl::StringView::FromExternal("ovenbird"); |
| fuchsia_device_manager::wire::DeviceStrProperty str_props[] = { |
| fuchsia_device_manager::wire::DeviceStrProperty{ |
| fidl::StringView::FromExternal(kInvalidStr, std::size(kInvalidStr)), |
| fuchsia_device_manager::wire::PropertyValue::WithStrValue( |
| fidl::ObjectView<fidl::StringView>::FromExternal(&str_val))}, |
| }; |
| |
| fbl::RefPtr<Device> device; |
| auto status = coordinator.AddDevice( |
| coordinator.test_device(), std::move(controller_endpoints->client), |
| std::move(coordinator_endpoints->server), nullptr /* props */, 0 /* props_count */, str_props, |
| std::size(str_props), "mock-device", ZX_PROTOCOL_TEST, {} /* driver_path */, {} /* args */, |
| false /* skip_autobind */, false /* has_init */, true /* always_init */, |
| zx::vmo() /*inspect*/, zx::channel() /* client_remote */, &device); |
| ASSERT_EQ(ZX_ERR_INVALID_ARGS, status); |
| } |
| |
| int main(int argc, char** argv) { return RUN_ALL_TESTS(argc, argv); } |