blob: 4c657b9651e30ab901194d27f5c3a00223ad3576 [file] [log] [blame] [edit]
// 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