blob: 35ad4b51b8c43599513c925039a94d5c7a7defe2 [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/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;
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
ASSERT_OK(
fdio_open("/pkg/data", ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_EXECUTABLE, remote.release()));
args.flat_namespace.push_back({"/drivers", std::move(local)});
args.load_drivers.push_back("/drivers/ddk-instance-lifecycle-test.so");
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 event) {
Lifecycle::EventHandlers event_handlers;
event_handlers.on_open = [&]() -> zx_status_t {
if (event == Event::Open) {
return ZX_OK;
}
return ZX_ERR_BAD_STATE;
};
event_handlers.on_close = [&]() -> zx_status_t {
if (event == Event::Close) {
return ZX_OK;
}
return ZX_ERR_BAD_STATE;
};
event_handlers.on_unbind = [&]() -> zx_status_t {
if (event == Event::Unbind) {
return ZX_OK;
}
return ZX_ERR_BAD_STATE;
};
event_handlers.on_release = [&]() -> zx_status_t {
if (event == Event::Release) {
return ZX_OK;
}
return ZX_ERR_BAD_STATE;
};
ASSERT_OK(Lifecycle::Call::HandleEvents(zx::unowned_channel(channel), std::move(event_handlers)));
}
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