blob: eecc68d5c472817246ebb4aec43218038a30bf11 [file] [log] [blame]
// Copyright 2023 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 <fidl/fuchsia.driver.component.test/cpp/driver/wire.h>
#include <fidl/fuchsia.driver.component.test/cpp/wire.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/component/incoming/cpp/service.h>
#include <lib/driver/component/cpp/tests/test_driver.h>
#include <lib/driver/incoming/cpp/namespace.h>
#include <lib/driver/testing/cpp/driver_runtime.h>
#include <lib/driver/testing/cpp/internal/driver_lifecycle.h>
#include <lib/driver/testing/cpp/internal/test_environment.h>
#include <lib/driver/testing/cpp/test_node.h>
#include <lib/fdio/directory.h>
#include <gtest/gtest.h>
// WARNING!
// This test is using the old driver testing libraries. These were verbose and difficult to use
// directly. We have a new driver testing library that wraps the logic provided by these libraries
// and moves these into the internal namespace. The new library is in the header:
// <lib/driver/testing/cpp/driver_test.h>
// This library is the replacement that should be used from now on. It is also referred to
// as the driver test fixture library in some places due to historical reasons. See the tests over
// in fixture_based_tests.cc for example tests using the library.
class ZirconProtocolServer
: public fidl::WireServer<fuchsia_driver_component_test::ZirconProtocol> {
public:
fuchsia_driver_component_test::ZirconService::InstanceHandler GetInstanceHandler() {
return fuchsia_driver_component_test::ZirconService::InstanceHandler({
.device = bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->async_dispatcher(),
fidl::kIgnoreBindingClosure),
});
}
private:
// fidl::WireServer<fuchsia_driver_component_test::ZirconProtocol>
void ZirconMethod(ZirconMethodCompleter::Sync& completer) override { completer.ReplySuccess(); }
fidl::ServerBindingGroup<fuchsia_driver_component_test::ZirconProtocol> bindings_;
};
// [START provide_handler]
class DriverProtocolServer : public fdf::WireServer<fuchsia_driver_component_test::DriverProtocol> {
public:
fuchsia_driver_component_test::DriverService::InstanceHandler GetInstanceHandler() {
return fuchsia_driver_component_test::DriverService::InstanceHandler({
.device = bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->get(),
fidl::kIgnoreBindingClosure),
});
}
private:
// fdf::WireServer<fuchsia_driver_component_test::DriverProtocol>
void DriverMethod(fdf::Arena& arena, DriverMethodCompleter::Sync& completer) override {
fdf::Arena reply_arena('TEST');
completer.buffer(reply_arena).ReplySuccess();
}
fdf::ServerBindingGroup<fuchsia_driver_component_test::DriverProtocol> bindings_;
};
// [END provide_handler]
// SEE WARNING AT TOP. DO NOT COPY INTO NEW TESTS.
// Sets up the environment to have both Zircon and Driver transport services.
class TestIncomingAndOutgoingFidlsBase : public ::testing::Test {
public:
void SetUp() override {
// Create start args
zx::result start_args = node_server_.SyncCall(&fdf_testing::TestNode::CreateStartArgsAndServe);
ASSERT_EQ(ZX_OK, start_args.status_value());
driver_outgoing_ = std::move(start_args->outgoing_directory_client);
// Start the test environment
// [START initialize_test_environment]
zx::result init_result =
test_environment_.SyncCall(&fdf_testing::internal::TestEnvironment::Initialize,
std::move(start_args->incoming_directory_server));
ASSERT_EQ(ZX_OK, init_result.status_value());
// [END initialize_test_environment]
// Add the services to the environment.
// [START get_server_handlers]
fuchsia_driver_component_test::ZirconService::InstanceHandler zircon_proto_handler =
zircon_proto_server_.SyncCall(&ZirconProtocolServer::GetInstanceHandler);
fuchsia_driver_component_test::DriverService::InstanceHandler driver_proto_handler =
driver_proto_server_.SyncCall(&DriverProtocolServer::GetInstanceHandler);
// [END get_server_handlers]
// [START move_server_handlers]
test_environment_.SyncCall([zircon_proto_handler = std::move(zircon_proto_handler),
driver_proto_handler = std::move(driver_proto_handler)](
fdf_testing::internal::TestEnvironment* env) mutable {
zx::result result =
env->incoming_directory().AddService<fuchsia_driver_component_test::ZirconService>(
std::move(zircon_proto_handler));
ASSERT_EQ(ZX_OK, result.status_value());
result = env->incoming_directory().AddService<fuchsia_driver_component_test::DriverService>(
std::move(driver_proto_handler));
ASSERT_EQ(ZX_OK, result.status_value());
});
// [END move_server_handlers]
// test_environment_ and device_server live on the same dispatcher so moving the ptr from one
// to the other is fine to do.
fdf::OutgoingDirectory* outgoing_ptr;
test_environment_.SyncCall([&outgoing_ptr](fdf_testing::internal::TestEnvironment* test_env) {
outgoing_ptr = &test_env->incoming_directory();
});
device_server.SyncCall([outgoing_ptr](compat::DeviceServer* device_server) {
device_server->Initialize(component::kDefaultInstance);
EXPECT_EQ(ZX_OK, device_server->Serve(fdf::Dispatcher::GetCurrent()->async_dispatcher(),
outgoing_ptr));
});
// Store the start_args for the subclasses to use to start the driver.
start_args_ = std::move(start_args->start_args);
}
void TearDown() override {
device_server.reset();
test_environment_.reset();
node_server_.reset();
driver_proto_server_.reset();
zircon_proto_server_.reset();
}
async_patterns::TestDispatcherBound<fdf_testing::TestNode>& node_server() { return node_server_; }
fidl::ClientEnd<fuchsia_io::Directory> CreateDriverSvcClient() {
// Open the svc directory in the driver's outgoing, and store a client to it.
auto [client_end, server_end] = fidl::Endpoints<fuchsia_io::Directory>::Create();
// [START set_up_outgoing_directory_channel]
zx_status_t status = fdio_open3_at(
driver_outgoing_.handle()->get(), "/svc",
uint64_t{fuchsia_io::wire::kPermReadable | fuchsia_io::Flags::kProtocolDirectory},
server_end.TakeChannel().release());
// [END set_up_outgoing_directory_channel]
EXPECT_EQ(ZX_OK, status);
return std::move(client_end);
}
async_dispatcher_t* env_dispatcher() { return env_dispatcher_->async_dispatcher(); }
protected:
fuchsia_driver_framework::DriverStartArgs& start_args() { return start_args_; }
fdf_testing::DriverRuntime& runtime() { return runtime_; }
private:
// Attaches a foreground dispatcher for us automatically.
fdf_testing::DriverRuntime runtime_;
// Env dispatcher. Managed by driver runtime threads.
fdf::UnownedSynchronizedDispatcher env_dispatcher_ = runtime_.StartBackgroundDispatcher();
// Servers for the incoming FIDLs to the driver.
// [START custom_server_classes]
async_patterns::TestDispatcherBound<ZirconProtocolServer> zircon_proto_server_{env_dispatcher(),
std::in_place};
async_patterns::TestDispatcherBound<DriverProtocolServer> driver_proto_server_{env_dispatcher(),
std::in_place};
// [END custom_server_classes]
async_patterns::TestDispatcherBound<compat::DeviceServer> device_server{env_dispatcher(),
std::in_place};
// Serves the fdf::Node protocol to the driver.
async_patterns::TestDispatcherBound<fdf_testing::TestNode> node_server_{
env_dispatcher(), std::in_place, std::string("root")};
// The environment can serve both the Zircon and Driver transport based protocols to the driver.
async_patterns::TestDispatcherBound<fdf_testing::internal::TestEnvironment> test_environment_{
env_dispatcher(), std::in_place};
fidl::ClientEnd<fuchsia_io::Directory> driver_outgoing_;
fuchsia_driver_framework::DriverStartArgs start_args_;
};
// SEE WARNING AT TOP. DO NOT COPY INTO NEW TESTS.
// Set the driver dispatcher to default so we can access |driver()| directly.
class TestIncomingAndOutgoingFidlsDefaultDriver : public TestIncomingAndOutgoingFidlsBase {
public:
// Sync clients into the driver have to be ran on a background thread because the test thread is
// where the driver will handle the call.
static void RunSyncClientTask(fit::closure task) {
// Spawn a separate thread to run the client task using an async::Loop.
async::Loop loop{&kAsyncLoopConfigNeverAttachToThread};
loop.StartThread();
zx::result result = fdf::RunOnDispatcherSync(loop.dispatcher(), std::move(task));
ASSERT_EQ(ZX_OK, result.status_value());
}
void SetUp() override {
TestIncomingAndOutgoingFidlsBase::SetUp();
zx::result result = runtime().RunToCompletion(driver_.Start(std::move(start_args())));
ASSERT_EQ(ZX_OK, result.status_value());
}
void TearDown() override {
zx::result result = runtime().RunToCompletion(driver_.PrepareStop());
ASSERT_EQ(ZX_OK, result.status_value());
// Tear down the environment after the driver goes through PrepareStop.
TestIncomingAndOutgoingFidlsBase::TearDown();
runtime().ShutdownAllDispatchers(fdf::Dispatcher::GetCurrent()->get());
}
TestDriver* driver() { return *driver_; }
private:
// The driver under test.
fdf_testing::internal::DriverUnderTest<TestDriver> driver_;
};
TEST_F(TestIncomingAndOutgoingFidlsDefaultDriver, ValidateDriverIncomingServices) {
zx::result result = driver()->ValidateIncomingDriverService();
ASSERT_EQ(ZX_OK, result.status_value());
result = driver()->ValidateIncomingZirconService();
ASSERT_EQ(ZX_OK, result.status_value());
}
TEST_F(TestIncomingAndOutgoingFidlsDefaultDriver, ConnectWithDevfs) {
zx::result export_result = driver()->ExportDevfsNodeSync();
ASSERT_EQ(ZX_OK, export_result.status_value());
zx::result device_result = node_server().SyncCall([](fdf_testing::TestNode* root_node) {
return root_node->children().at("devfs_node").ConnectToDevice();
});
ASSERT_EQ(ZX_OK, device_result.status_value());
fidl::ClientEnd<fuchsia_driver_component_test::ZirconProtocol> device_client_end(
std::move(device_result.value()));
fidl::WireSyncClient<fuchsia_driver_component_test::ZirconProtocol> zircon_proto_client(
std::move(device_client_end));
RunSyncClientTask([zircon_proto_client = std::move(zircon_proto_client)]() {
fidl::WireResult result = zircon_proto_client->ZirconMethod();
ASSERT_EQ(ZX_OK, result.status());
ASSERT_EQ(true, result.value().is_ok());
});
}
TEST_F(TestIncomingAndOutgoingFidlsDefaultDriver, ConnectWithZirconService) {
zx::result serve_result = driver()->ServeZirconService();
ASSERT_EQ(ZX_OK, serve_result.status_value());
zx::result result =
component::ConnectAtMember<fuchsia_driver_component_test::ZirconService::Device>(
CreateDriverSvcClient());
ASSERT_EQ(ZX_OK, result.status_value());
RunSyncClientTask([client_end = std::move(result.value())]() {
fidl::WireResult wire_result = fidl::WireCall(client_end)->ZirconMethod();
ASSERT_EQ(ZX_OK, wire_result.status());
ASSERT_EQ(true, wire_result.value().is_ok());
});
}
TEST_F(TestIncomingAndOutgoingFidlsDefaultDriver, ConnectWithDriverService) {
zx::result serve_result = driver()->ServeDriverService();
ASSERT_EQ(ZX_OK, serve_result.status_value());
zx::result driver_connect_result =
fdf::internal::DriverTransportConnect<fuchsia_driver_component_test::DriverService::Device>(
CreateDriverSvcClient(), component::kDefaultInstance);
ASSERT_EQ(ZX_OK, driver_connect_result.status_value());
RunSyncClientTask([client_end = std::move(driver_connect_result.value())]() {
fdf::Arena arena('TEST');
fdf::WireUnownedResult wire_result = fdf::WireCall(client_end).buffer(arena)->DriverMethod();
ASSERT_EQ(ZX_OK, wire_result.status());
ASSERT_EQ(true, wire_result.value().is_ok());
});
}
// SEE WARNING AT TOP. DO NOT COPY INTO NEW TESTS.
// Set the driver dispatcher to be managed so that we can make sync client calls into the driver
// hosted services directly from the test instead of using |RunSyncClientTask| from above.
class TestIncomingAndOutgoingFidlsManagedDriver : public TestIncomingAndOutgoingFidlsBase {
public:
void SetUp() override {
TestIncomingAndOutgoingFidlsBase::SetUp();
zx::result result = runtime().RunToCompletion(driver_.SyncCall(
&fdf_testing::internal::DriverUnderTest<TestDriver>::Start, std::move(start_args())));
ASSERT_EQ(ZX_OK, result.status_value());
}
void TearDown() override {
zx::result result = runtime().RunToCompletion(
driver_.SyncCall(&fdf_testing::internal::DriverUnderTest<TestDriver>::PrepareStop));
ASSERT_EQ(ZX_OK, result.status_value());
// Tear down the environment after the driver goes through PrepareStop.
TestIncomingAndOutgoingFidlsBase::TearDown();
runtime().ShutdownAllDispatchers(driver_dispatcher_->get());
}
async_patterns::TestDispatcherBound<fdf_testing::internal::DriverUnderTest<TestDriver>>&
driver() {
return driver_;
}
async_dispatcher_t* driver_dispatcher() { return driver_dispatcher_->async_dispatcher(); }
private:
// Driver dispatcher set as a background dispatcher.
fdf::UnownedSynchronizedDispatcher driver_dispatcher_ = runtime().StartBackgroundDispatcher();
// The driver under test.
async_patterns::TestDispatcherBound<fdf_testing::internal::DriverUnderTest<TestDriver>> driver_{
driver_dispatcher(), std::in_place};
};
TEST_F(TestIncomingAndOutgoingFidlsManagedDriver, ConnectWithDevfs) {
driver().SyncCall([](fdf_testing::internal::DriverUnderTest<TestDriver>* driver) {
zx::result result = (*driver)->ExportDevfsNodeSync();
ASSERT_EQ(ZX_OK, result.status_value());
});
zx::result device_result = node_server().SyncCall([](fdf_testing::TestNode* root_node) {
return root_node->children().at("devfs_node").ConnectToDevice();
});
ASSERT_EQ(ZX_OK, device_result.status_value());
fidl::ClientEnd<fuchsia_driver_component_test::ZirconProtocol> device_client_end(
std::move(device_result.value()));
fidl::WireSyncClient<fuchsia_driver_component_test::ZirconProtocol> zircon_proto_client(
std::move(device_client_end));
fidl::WireResult result = zircon_proto_client->ZirconMethod();
ASSERT_EQ(ZX_OK, result.status());
ASSERT_EQ(true, result.value().is_ok());
}
TEST_F(TestIncomingAndOutgoingFidlsManagedDriver, ConnectWithZirconService) {
driver().SyncCall([](fdf_testing::internal::DriverUnderTest<TestDriver>* driver) {
zx::result result = (*driver)->ServeZirconService();
ASSERT_EQ(ZX_OK, result.status_value());
});
zx::result result =
component::ConnectAtMember<fuchsia_driver_component_test::ZirconService::Device>(
CreateDriverSvcClient());
ASSERT_EQ(ZX_OK, result.status_value());
fidl::WireResult wire_result = fidl::WireCall(result.value())->ZirconMethod();
ASSERT_EQ(ZX_OK, wire_result.status());
ASSERT_EQ(true, wire_result.value().is_ok());
}
TEST_F(TestIncomingAndOutgoingFidlsManagedDriver, ConnectWithDriverService) {
driver().SyncCall([](fdf_testing::internal::DriverUnderTest<TestDriver>* driver) {
zx::result result = (*driver)->ServeDriverService();
ASSERT_EQ(ZX_OK, result.status_value());
});
zx::result driver_connect_result =
fdf::internal::DriverTransportConnect<fuchsia_driver_component_test::DriverService::Device>(
CreateDriverSvcClient(), component::kDefaultInstance);
ASSERT_EQ(ZX_OK, driver_connect_result.status_value());
fdf::Arena arena('TEST');
fdf::WireUnownedResult wire_result =
fdf::WireCall(driver_connect_result.value()).buffer(arena)->DriverMethod();
ASSERT_EQ(ZX_OK, wire_result.status());
ASSERT_EQ(true, wire_result.value().is_ok());
}