blob: 0ab9d4a5a64f41e0ba2f6a4ea741a6e4811771d2 [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 <fuchsia/device/instancelifecycle/test/llcpp/fidl.h>
#include <lib/ddk/platform-defs.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 <zxtest/zxtest.h>
namespace {
using driver_integration_test::IsolatedDevmgr;
using fuchsia_device_instancelifecycle_test::InstanceDevice;
using fuchsia_device_instancelifecycle_test::Lifecycle;
using 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(), device_.channel().reset_and_get_address()));
ASSERT_TRUE(device_.is_valid());
}
protected:
enum class Event { Open, Close, Unbind, Release };
// Remove the parent, and then close the instance
void VerifyPostOpenLifecycleViaRemove(fidl::UnownedClientEnd<Lifecycle> lifecycle_chan,
fidl::ClientEnd<InstanceDevice> instance_client);
// Close the instance
void VerifyPostOpenLifecycleViaClose(fidl::UnownedClientEnd<Lifecycle> lifecycle_chan,
fidl::ClientEnd<InstanceDevice> instance_client);
static void WaitForEvent(fidl::UnownedClientEnd<Lifecycle> lifecycle_client, Event event);
static bool AreEventsPending(fidl::UnownedClientEnd<Lifecycle> lifecycle_client) {
return lifecycle_client.channel()->wait_one(ZX_CHANNEL_READABLE, zx::time{}, nullptr) == ZX_OK;
}
fidl::ClientEnd<TestDevice> device_;
IsolatedDevmgr devmgr_;
};
void InstanceLifecycleTest::WaitForEvent(fidl::UnownedClientEnd<Lifecycle> lifecycle_client,
Event expected_event) {
class EventHandler : public fidl::WireSyncEventHandler<Lifecycle> {
public:
explicit EventHandler(Event expected_event) : expected_event_(expected_event) {}
bool ok() const { return ok_; }
void OnOpen(fidl::WireResponse<Lifecycle::OnOpen>* event) override {
ok_ = expected_event_ == Event::Open;
}
void OnClose(fidl::WireResponse<Lifecycle::OnClose>* event) override {
ok_ = expected_event_ == Event::Close;
};
void OnUnbind(fidl::WireResponse<Lifecycle::OnUnbind>* event) override {
ok_ = expected_event_ == Event::Unbind;
}
void OnRelease(fidl::WireResponse<Lifecycle::OnRelease>* 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(lifecycle_client).status());
ASSERT_TRUE(event_handler.ok());
}
void InstanceLifecycleTest::VerifyPostOpenLifecycleViaRemove(
fidl::UnownedClientEnd<Lifecycle> lifecycle_chan,
fidl::ClientEnd<InstanceDevice> instance_client) {
ASSERT_NO_FATAL_FAILURES(WaitForEvent(lifecycle_chan, Event::Open));
auto endpoints = fidl::CreateEndpoints<Lifecycle>();
ASSERT_OK(endpoints.status_value());
auto [instance_lifecycle_chan, remote] = *std::move(endpoints);
{
auto result = fidl::WireCall(instance_client).SubscribeToLifecycle(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 = fidl::WireCall(instance_client).RemoveDevice();
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(
fidl::UnownedClientEnd<Lifecycle> lifecycle_chan,
fidl::ClientEnd<InstanceDevice> instance_client) {
ASSERT_NO_FATAL_FAILURES(WaitForEvent(lifecycle_chan, Event::Open));
auto endpoints = fidl::CreateEndpoints<Lifecycle>();
ASSERT_OK(endpoints.status_value());
auto [instance_lifecycle_chan, remote] = *std::move(endpoints);
{
auto result = fidl::WireCall(instance_client).SubscribeToLifecycle(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.
auto endpoints = fidl::CreateEndpoints<Lifecycle>();
ASSERT_OK(endpoints.status_value());
auto [lifecycle_chan, remote] = *std::move(endpoints);
auto result = fidl::WireCall(device_).CreateDevice(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));
fidl::ClientEnd<InstanceDevice> 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.channel().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.
auto lifecycle_endpoints = fidl::CreateEndpoints<Lifecycle>();
ASSERT_OK(lifecycle_endpoints.status_value());
auto [lifecycle_chan, lifecycle_remote] = *std::move(lifecycle_endpoints);
auto instance_endpoints = fidl::CreateEndpoints<InstanceDevice>();
ASSERT_OK(instance_endpoints.status_value());
auto [instance_client, instance_client_remote] = *std::move(instance_endpoints);
auto result = fidl::WireCall(device_).CreateDevice(std::move(lifecycle_remote),
instance_client_remote.TakeChannel());
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.
auto endpoints = fidl::CreateEndpoints<Lifecycle>();
ASSERT_OK(endpoints.status_value());
auto [lifecycle_chan, remote] = *std::move(endpoints);
auto result = fidl::WireCall(device_).CreateDevice(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));
fidl::ClientEnd<InstanceDevice> 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.channel().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.
auto lifecycle_endpoints = fidl::CreateEndpoints<Lifecycle>();
ASSERT_OK(lifecycle_endpoints.status_value());
auto [lifecycle_chan, lifecycle_remote] = *std::move(lifecycle_endpoints);
auto instance_endpoints = fidl::CreateEndpoints<InstanceDevice>();
ASSERT_OK(instance_endpoints.status_value());
auto [instance_client, instance_client_remote] = *std::move(instance_endpoints);
auto result = fidl::WireCall(device_).CreateDevice(std::move(lifecycle_remote),
instance_client_remote.TakeChannel());
ASSERT_OK(result.status());
ASSERT_FALSE(result->result.is_err());
ASSERT_NO_FATAL_FAILURES(
VerifyPostOpenLifecycleViaRemove(lifecycle_chan, std::move(instance_client)));
}
} // namespace