| // 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/device/instancelifecycle/test/llcpp/fidl.h> |
| #include <lib/driver-integration-test/fixture.h> |
| #include <lib/fdio/directory.h> |
| #include <zircon/device/vfs.h> |
| #include <zircon/processargs.h> |
| #include <zircon/syscalls.h> |
| |
| #include <vector> |
| |
| #include <ddk/platform-defs.h> |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| using driver_integration_test::IsolatedDevmgr; |
| using llcpp::fuchsia::device::instancelifecycle::test::InstanceDevice; |
| using llcpp::fuchsia::device::instancelifecycle::test::Lifecycle; |
| using llcpp::fuchsia::device::instancelifecycle::test::TestDevice; |
| |
| class InstanceLifecycleTest : public zxtest::Test { |
| public: |
| ~InstanceLifecycleTest() override = default; |
| void SetUp() override { |
| IsolatedDevmgr::Args args; |
| |
| board_test::DeviceEntry dev = {}; |
| dev.vid = PDEV_VID_TEST; |
| dev.pid = PDEV_PID_INSTANCE_LIFECYCLE_TEST; |
| dev.did = 0; |
| args.device_list.push_back(dev); |
| |
| zx_status_t status = IsolatedDevmgr::Create(&args, &devmgr_); |
| ASSERT_OK(status); |
| fbl::unique_fd fd; |
| ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile( |
| devmgr_.devfs_root(), "sys/platform/11:12:0/instance-test", &fd)); |
| ASSERT_GT(fd.get(), 0); |
| ASSERT_OK(fdio_get_service_handle(fd.release(), chan_.reset_and_get_address())); |
| ASSERT_NE(chan_.get(), ZX_HANDLE_INVALID); |
| } |
| |
| protected: |
| enum class Event { Open, Close, Unbind, Release }; |
| |
| // Remove the parent, and then close the instance |
| void VerifyPostOpenLifecycleViaRemove(const zx::channel& lifecycle_chan, |
| zx::channel instance_client); |
| |
| // Close the instance |
| void VerifyPostOpenLifecycleViaClose(const zx::channel& lifecycle_chan, |
| zx::channel instance_client); |
| |
| static void WaitForEvent(const zx::channel& channel, Event event); |
| static bool AreEventsPending(const zx::channel& channel) { |
| return channel.wait_one(ZX_CHANNEL_READABLE, zx::time{}, nullptr) == ZX_OK; |
| } |
| |
| zx::channel chan_; |
| IsolatedDevmgr devmgr_; |
| }; |
| |
| void InstanceLifecycleTest::WaitForEvent(const zx::channel& channel, Event expected_event) { |
| class EventHandler : public Lifecycle::EventHandler { |
| public: |
| explicit EventHandler(Event expected_event) : expected_event_(expected_event) {} |
| |
| bool ok() const { return ok_; } |
| |
| void OnOpen(Lifecycle::OnOpenResponse* event) override { ok_ = expected_event_ == Event::Open; } |
| |
| void OnClose(Lifecycle::OnCloseResponse* event) override { |
| ok_ = expected_event_ == Event::Close; |
| }; |
| |
| void OnUnbind(Lifecycle::OnUnbindResponse* event) override { |
| ok_ = expected_event_ == Event::Unbind; |
| } |
| |
| void OnRelease(Lifecycle::OnReleaseResponse* event) override { |
| ok_ = expected_event_ == Event::Release; |
| } |
| |
| zx_status_t Unknown() override { return ZX_ERR_NOT_SUPPORTED; } |
| |
| private: |
| const Event expected_event_; |
| bool ok_ = false; |
| }; |
| |
| EventHandler event_handler(expected_event); |
| ASSERT_OK(event_handler.HandleOneEvent(zx::unowned_channel(channel)).status()); |
| ASSERT_TRUE(event_handler.ok()); |
| } |
| |
| void InstanceLifecycleTest::VerifyPostOpenLifecycleViaRemove(const zx::channel& lifecycle_chan, |
| zx::channel instance_client) { |
| ASSERT_NO_FATAL_FAILURES(WaitForEvent(lifecycle_chan, Event::Open)); |
| |
| zx::channel instance_lifecycle_chan, remote; |
| { |
| ASSERT_OK(zx::channel::create(0, &instance_lifecycle_chan, &remote)); |
| auto result = |
| InstanceDevice::Call::SubscribeToLifecycle(zx::unowned(instance_client), std::move(remote)); |
| ASSERT_OK(result.status()); |
| ASSERT_FALSE(result->result.is_err()); |
| } |
| |
| // There shouldn't be anymore pending events yet |
| ASSERT_FALSE(AreEventsPending(lifecycle_chan)); |
| ASSERT_FALSE(AreEventsPending(instance_lifecycle_chan)); |
| |
| // Request the device begin removal |
| { |
| auto result = InstanceDevice::Call::RemoveDevice(zx::unowned(instance_client)); |
| ASSERT_OK(result.status()); |
| } |
| |
| // We should see unbind, followed by close, then release. |
| ASSERT_NO_FATAL_FAILURES(WaitForEvent(lifecycle_chan, Event::Unbind)); |
| ASSERT_NO_FATAL_FAILURES(WaitForEvent(instance_lifecycle_chan, Event::Close)); |
| ASSERT_NO_FATAL_FAILURES(WaitForEvent(instance_lifecycle_chan, Event::Release)); |
| ASSERT_NO_FATAL_FAILURES(WaitForEvent(lifecycle_chan, Event::Release)); |
| } |
| |
| void InstanceLifecycleTest::VerifyPostOpenLifecycleViaClose(const zx::channel& lifecycle_chan, |
| zx::channel instance_client) { |
| ASSERT_NO_FATAL_FAILURES(WaitForEvent(lifecycle_chan, Event::Open)); |
| |
| zx::channel instance_lifecycle_chan, remote; |
| { |
| ASSERT_OK(zx::channel::create(0, &instance_lifecycle_chan, &remote)); |
| auto result = |
| InstanceDevice::Call::SubscribeToLifecycle(zx::unowned(instance_client), std::move(remote)); |
| ASSERT_OK(result.status()); |
| ASSERT_FALSE(result->result.is_err()); |
| } |
| |
| // There shouldn't be anymore pending events yet |
| ASSERT_FALSE(AreEventsPending(lifecycle_chan)); |
| ASSERT_FALSE(AreEventsPending(instance_lifecycle_chan)); |
| |
| // Close the connection to the instance. |
| instance_client.reset(); |
| ASSERT_NO_FATAL_FAILURES(WaitForEvent(instance_lifecycle_chan, Event::Close)); |
| ASSERT_NO_FATAL_FAILURES(WaitForEvent(instance_lifecycle_chan, Event::Release)); |
| ASSERT_FALSE(AreEventsPending(lifecycle_chan)); |
| } |
| |
| // Test the lifecycle of an instance device that's obtained via fuchsia.io/Open |
| TEST_F(InstanceLifecycleTest, NonPipelinedClientClose) { |
| // Subscribe to the device lifecycle events. |
| zx::channel lifecycle_chan, remote; |
| ASSERT_OK(zx::channel::create(0, &lifecycle_chan, &remote)); |
| |
| auto result = |
| TestDevice::Call::CreateDevice(zx::unowned(chan_), std::move(remote), zx::channel{}); |
| ASSERT_OK(result.status()); |
| ASSERT_FALSE(result->result.is_err()); |
| |
| // There shouldn't be any pending events yet |
| ASSERT_FALSE(AreEventsPending(lifecycle_chan)); |
| |
| zx::channel instance_client; |
| { |
| fbl::unique_fd fd; |
| ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile( |
| devmgr_.devfs_root(), "sys/platform/11:12:0/instance-test/child", &fd)); |
| ASSERT_GT(fd.get(), 0); |
| ASSERT_OK(fdio_get_service_handle(fd.release(), instance_client.reset_and_get_address())); |
| } |
| |
| ASSERT_NO_FATAL_FAILURES( |
| VerifyPostOpenLifecycleViaClose(lifecycle_chan, std::move(instance_client))); |
| } |
| |
| // Test the lifecycle of an instance device that's obtained via device_add |
| TEST_F(InstanceLifecycleTest, PipelinedClientClose) { |
| // Subscribe to the device lifecycle events. |
| zx::channel lifecycle_chan, lifecycle_remote; |
| ASSERT_OK(zx::channel::create(0, &lifecycle_chan, &lifecycle_remote)); |
| |
| zx::channel instance_client, instance_client_remote; |
| ASSERT_OK(zx::channel::create(0, &instance_client, &instance_client_remote)); |
| |
| auto result = TestDevice::Call::CreateDevice(zx::unowned(chan_), std::move(lifecycle_remote), |
| std::move(instance_client_remote)); |
| ASSERT_OK(result.status()); |
| ASSERT_FALSE(result->result.is_err()); |
| |
| ASSERT_NO_FATAL_FAILURES( |
| VerifyPostOpenLifecycleViaClose(lifecycle_chan, std::move(instance_client))); |
| } |
| |
| // Test the lifecycle of an instance device that's obtained via fuchsia.io/Open |
| TEST_F(InstanceLifecycleTest, NonPipelinedClientRemoveAndClose) { |
| // Subscribe to the device lifecycle events. |
| zx::channel lifecycle_chan, remote; |
| ASSERT_OK(zx::channel::create(0, &lifecycle_chan, &remote)); |
| |
| auto result = |
| TestDevice::Call::CreateDevice(zx::unowned(chan_), std::move(remote), zx::channel{}); |
| ASSERT_OK(result.status()); |
| ASSERT_FALSE(result->result.is_err()); |
| |
| // There shouldn't be any pending events yet |
| ASSERT_FALSE(AreEventsPending(lifecycle_chan)); |
| |
| zx::channel instance_client; |
| { |
| fbl::unique_fd fd; |
| ASSERT_OK(devmgr_integration_test::RecursiveWaitForFile( |
| devmgr_.devfs_root(), "sys/platform/11:12:0/instance-test/child", &fd)); |
| ASSERT_GT(fd.get(), 0); |
| ASSERT_OK(fdio_get_service_handle(fd.release(), instance_client.reset_and_get_address())); |
| } |
| |
| ASSERT_NO_FATAL_FAILURES( |
| VerifyPostOpenLifecycleViaRemove(lifecycle_chan, std::move(instance_client))); |
| } |
| |
| // Test the lifecycle of an instance device that's obtained via device_add |
| TEST_F(InstanceLifecycleTest, PipelinedClientRemoveAndClose) { |
| // Subscribe to the device lifecycle events. |
| zx::channel lifecycle_chan, lifecycle_remote; |
| ASSERT_OK(zx::channel::create(0, &lifecycle_chan, &lifecycle_remote)); |
| |
| zx::channel instance_client, instance_client_remote; |
| ASSERT_OK(zx::channel::create(0, &instance_client, &instance_client_remote)); |
| |
| auto result = TestDevice::Call::CreateDevice(zx::unowned(chan_), std::move(lifecycle_remote), |
| std::move(instance_client_remote)); |
| ASSERT_OK(result.status()); |
| ASSERT_FALSE(result->result.is_err()); |
| |
| ASSERT_NO_FATAL_FAILURES( |
| VerifyPostOpenLifecycleViaRemove(lifecycle_chan, std::move(instance_client))); |
| } |
| |
| } // namespace |