blob: b5b77b7d2536013f2810a6e558b5b8a10b09c381 [file] [log] [blame]
// Copyright 2018 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 <atomic>
#include <fidl/test/llcpp/basictypes/c/fidl.h>
#include <lib/async-loop/loop.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fidl-async/bind.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/fidl/llcpp/coding.h>
#include <lib/zx/channel.h>
#include <lib/zx/eventpair.h>
#include <lib/zx/time.h>
#include <memory>
#include <string.h>
#include <unittest/unittest.h>
#include <utility>
#include <zircon/fidl.h>
#include <zircon/syscalls.h>
// Interface under test
#include "generated/fidl_llcpp_basictypes.h"
// test utility functions
namespace {
bool IsPeerValid(const zx::unowned_eventpair& handle) {
zx_signals_t observed_signals = {};
switch (handle->wait_one(ZX_EVENTPAIR_PEER_CLOSED,
zx::deadline_after(zx::msec(1)),
&observed_signals)) {
case ZX_ERR_TIMED_OUT:
// timeout implies peer-closed was not observed
return true;
case ZX_OK:
return (observed_signals & ZX_EVENTPAIR_PEER_CLOSED) == 0;
default:
return false;
}
}
bool IsPeerValid(zx_handle_t handle) {
return IsPeerValid(zx::unowned_eventpair(handle));
}
template <typename T, size_t N>
constexpr uint32_t ArrayCount(T const (&array)[N]) {
static_assert(N < UINT32_MAX, "Array is too large!");
return N;
}
}
// C server implementation
namespace internal_c {
zx_status_t ConsumeSimpleStruct(void* ctx,
const fidl_test_llcpp_basictypes_SimpleStruct* arg,
fidl_txn_t* txn) {
// Verify that all the handles are valid channels
if (!IsPeerValid(arg->ep)) {
return fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleStruct_reply(
txn, ZX_ERR_INVALID_ARGS, -1);
}
for (auto& row : arg->arr) {
for (auto& handle : row) {
if (!IsPeerValid(handle)) {
return fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleStruct_reply(
txn, ZX_ERR_INVALID_ARGS, -1);
}
}
}
// Close all the handles as part of consumption
zx_handle_close(arg->ep);
for (auto& row : arg->arr) {
for (auto& handle : row) {
zx_handle_close(handle);
}
}
// Loop back field argument
return fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleStruct_reply(
txn, ZX_OK, arg->field);
}
zx_status_t ConsumeSimpleUnion(void* ctx,
const fidl_test_llcpp_basictypes_SimpleUnion* arg,
fidl_txn_t* txn) {
if (arg->tag == fidl_test_llcpp_basictypes_SimpleUnionTag_field_a) {
return fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleUnion_reply(
txn, 0, arg->field_a);
} else if (arg->tag == fidl_test_llcpp_basictypes_SimpleUnionTag_field_b) {
return fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleUnion_reply(
txn, 1, arg->field_b);
} else {
return fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleUnion_reply(
txn, UINT32_MAX, -1);
}
}
const fidl_test_llcpp_basictypes_TestInterface_ops_t kOps = {
.ConsumeSimpleStruct = ConsumeSimpleStruct,
.ConsumeSimpleUnion = ConsumeSimpleUnion,
};
zx_status_t ServerDispatch(void* ctx,
fidl_txn_t* txn,
fidl_msg_t* msg,
const fidl_test_llcpp_basictypes_TestInterface_ops_t* ops) {
zx_status_t status = fidl_test_llcpp_basictypes_TestInterface_try_dispatch(ctx, txn, msg, ops);
if (status == ZX_ERR_NOT_SUPPORTED) {
zx_handle_close_many(msg->handles, msg->num_handles);
}
return status;
}
}
// LLCPP client tests: interop between C server and LLCPP client
namespace {
bool SpinUpAsyncCServerHelper(zx::channel server, async_loop_t** out_loop) {
BEGIN_HELPER;
async_loop_t* loop = nullptr;
ASSERT_EQ(ZX_OK, async_loop_create(&kAsyncLoopConfigNoAttachToThread, &loop), "");
ASSERT_EQ(ZX_OK, async_loop_start_thread(loop, "basictypes-dispatcher", NULL), "");
async_dispatcher_t* dispatcher = async_loop_get_dispatcher(loop);
fidl_bind(dispatcher,
server.release(),
(fidl_dispatch_t*) internal_c::ServerDispatch,
NULL,
&internal_c::kOps);
*out_loop = loop;
END_HELPER;
}
void TearDownAsyncCServerHelper(async_loop_t* loop) {
async_loop_destroy(loop);
}
bool RawChannelCallStructTest() {
BEGIN_TEST;
zx::channel client, server;
ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
async_loop_t* loop = nullptr;
ASSERT_TRUE(SpinUpAsyncCServerHelper(std::move(server), &loop));
// manually call the server using generated message definitions
FIDL_ALIGNDECL uint8_t storage[512] = {};
fidl::BytePart bytes(&storage[0], sizeof(storage));
// trivial linearization except to set message length
bytes.set_actual(
sizeof(fidl::test::llcpp::basictypes::TestInterface::ConsumeSimpleStructRequest));
fidl::DecodedMessage<fidl::test::llcpp::basictypes::TestInterface::ConsumeSimpleStructRequest>
request(std::move(bytes));
request.message()->_hdr.ordinal =
fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleStructOrdinal;
request.message()->arg.field = 123;
// make sure array shape is as expected (5 by 4)
constexpr size_t kNumRow = 5;
constexpr size_t kNumCol = 4;
constexpr size_t kNumHandlesInArray = kNumRow * kNumCol;
static_assert(decltype(request.message()->arg.arr)::size() == kNumRow);
static_assert(
std::remove_reference_t<decltype(request.message()->arg.arr[0])>::size() == kNumCol);
// insert handles to be sent over
zx::eventpair single_handle_payload;
zx::eventpair single_handle_ourside;
ASSERT_EQ(zx::eventpair::create(0, &single_handle_ourside, &single_handle_payload), ZX_OK);
std::unique_ptr<zx::eventpair[]> handle_payload(new zx::eventpair[kNumHandlesInArray]);
std::unique_ptr<zx::eventpair[]> handle_our_side(new zx::eventpair[kNumHandlesInArray]);
for (size_t i = 0; i < kNumHandlesInArray; i++) {
ASSERT_EQ(zx::eventpair::create(0, &handle_our_side[i], &handle_payload[i]), ZX_OK);
}
// fill the |ep| field
request.message()->arg.ep = std::move(single_handle_payload);
// fill the 2D handles array
for (size_t i = 0; i < kNumRow; i++) {
for (size_t j = 0; j < kNumCol; j++) {
request.message()->arg.arr[i][j] = std::move(handle_payload[i * kNumCol + j]);
}
}
auto encode_result = fidl::Encode(std::move(request));
ASSERT_EQ(encode_result.status, ZX_OK);
FIDL_ALIGNDECL uint8_t response_storage[512];
fidl::BytePart response_bytes(&response_storage[0], sizeof(response_storage));
auto response = fidl::Call(client, std::move(encode_result.message), std::move(response_bytes));
ASSERT_EQ(response.status, ZX_OK);
auto decode_result = fidl::Decode(std::move(response.message));
ASSERT_EQ(decode_result.message.message()->field, 123);
TearDownAsyncCServerHelper(loop);
END_TEST;
}
bool RawChannelCallUnionTest() {
BEGIN_TEST;
zx::channel client, server;
ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
async_loop_t* loop = nullptr;
ASSERT_TRUE(SpinUpAsyncCServerHelper(std::move(server), &loop));
// manually call the server using generated message definitions
FIDL_ALIGNDECL uint8_t storage[512] = {};
fidl::BytePart bytes(&storage[0], sizeof(storage));
// trivial linearization except to set message length
bytes.set_actual(
sizeof(fidl::test::llcpp::basictypes::TestInterface::ConsumeSimpleUnionRequest));
fidl::DecodedMessage<fidl::test::llcpp::basictypes::TestInterface::ConsumeSimpleUnionRequest>
request(std::move(bytes));
request.message()->_hdr.ordinal =
fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleUnionOrdinal;
request.message()->arg.mutable_field_b() = 456;
auto encode_result = fidl::Encode(std::move(request));
ASSERT_EQ(encode_result.status, ZX_OK);
FIDL_ALIGNDECL uint8_t response_storage[512];
fidl::BytePart response_bytes(&response_storage[0], sizeof(response_storage));
auto response = fidl::Call(client, std::move(encode_result.message), std::move(response_bytes));
ASSERT_EQ(response.status, ZX_OK);
auto decode_result = fidl::Decode(std::move(response.message));
ASSERT_EQ(decode_result.message.message()->index, 1);
ASSERT_EQ(decode_result.message.message()->field, 456);
TearDownAsyncCServerHelper(loop);
END_TEST;
}
bool SyncCallStructTest() {
BEGIN_TEST;
zx::channel client, server;
ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
async_loop_t* loop = nullptr;
ASSERT_TRUE(SpinUpAsyncCServerHelper(std::move(server), &loop));
// generated interface API
fidl::test::llcpp::basictypes::TestInterface::SyncClient test(std::move(client));
int32_t out_status;
int32_t out_field;
fidl::test::llcpp::basictypes::SimpleStruct simple_struct = {};
simple_struct.field = 123;
// make sure array shape is as expected (5 by 4)
constexpr size_t kNumRow = 5;
constexpr size_t kNumCol = 4;
constexpr size_t kNumHandlesInArray = kNumRow * kNumCol;
static_assert(decltype(simple_struct.arr)::size() == kNumRow);
static_assert(
std::remove_reference_t<decltype(simple_struct.arr[0])>::size() == kNumCol);
// insert handles to be sent over
zx::eventpair single_handle_payload;
zx::eventpair single_handle_ourside;
ASSERT_EQ(zx::eventpair::create(0, &single_handle_ourside, &single_handle_payload), ZX_OK);
std::unique_ptr<zx::eventpair[]> handle_payload(new zx::eventpair[kNumHandlesInArray]);
std::unique_ptr<zx::eventpair[]> handle_our_side(new zx::eventpair[kNumHandlesInArray]);
for (size_t i = 0; i < kNumHandlesInArray; i++) {
ASSERT_EQ(zx::eventpair::create(0, &handle_our_side[i], &handle_payload[i]), ZX_OK);
}
// fill the |ep| field
simple_struct.ep = std::move(single_handle_payload);
// fill the 2D handles array
for (size_t i = 0; i < kNumRow; i++) {
for (size_t j = 0; j < kNumCol; j++) {
simple_struct.arr[i][j] = std::move(handle_payload[i * kNumCol + j]);
}
}
// perform call
zx_status_t status = test.ConsumeSimpleStruct(std::move(simple_struct),
&out_status,
&out_field);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(out_status, ZX_OK);
ASSERT_EQ(out_field, 123);
TearDownAsyncCServerHelper(loop);
END_TEST;
}
bool SyncCallerAllocateCallStructTest() {
BEGIN_TEST;
zx::channel client, server;
ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
async_loop_t* loop = nullptr;
ASSERT_TRUE(SpinUpAsyncCServerHelper(std::move(server), &loop));
// generated interface API
fidl::test::llcpp::basictypes::TestInterface::SyncClient test(std::move(client));
int32_t out_status;
int32_t out_field;
fidl::test::llcpp::basictypes::SimpleStruct simple_struct = {};
simple_struct.field = 123;
// make sure array shape is as expected (5 by 4)
constexpr size_t kNumRow = 5;
constexpr size_t kNumCol = 4;
constexpr size_t kNumHandlesInArray = kNumRow * kNumCol;
static_assert(decltype(simple_struct.arr)::size() == kNumRow);
static_assert(
std::remove_reference_t<decltype(simple_struct.arr[0])>::size() == kNumCol);
// insert handles to be sent over
zx::eventpair single_handle_payload;
zx::eventpair single_handle_ourside;
ASSERT_EQ(zx::eventpair::create(0, &single_handle_ourside, &single_handle_payload), ZX_OK);
std::unique_ptr<zx::eventpair[]> handle_payload(new zx::eventpair[kNumHandlesInArray]);
std::unique_ptr<zx::eventpair[]> handle_our_side(new zx::eventpair[kNumHandlesInArray]);
for (size_t i = 0; i < kNumHandlesInArray; i++) {
ASSERT_EQ(zx::eventpair::create(0, &handle_our_side[i], &handle_payload[i]), ZX_OK);
}
// fill the |ep| field
simple_struct.ep = std::move(single_handle_payload);
// fill the 2D handles array
for (size_t i = 0; i < kNumRow; i++) {
for (size_t j = 0; j < kNumCol; j++) {
simple_struct.arr[i][j] = std::move(handle_payload[i * kNumCol + j]);
}
}
// perform call
FIDL_ALIGNDECL uint8_t request_buf[512] = {};
FIDL_ALIGNDECL uint8_t response_buf[512] = {};
auto result = test.ConsumeSimpleStruct(fidl::BytePart(request_buf, sizeof(request_buf)),
std::move(simple_struct),
fidl::BytePart(response_buf,
sizeof(response_buf)),
&out_status, &out_field);
ASSERT_EQ(result.status, ZX_OK);
ASSERT_NULL(result.error, result.error);
ASSERT_EQ(out_status, ZX_OK);
ASSERT_EQ(out_field, 123);
TearDownAsyncCServerHelper(loop);
END_TEST;
}
bool SyncCallUnionTest() {
BEGIN_TEST;
zx::channel client, server;
ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
async_loop_t* loop = nullptr;
ASSERT_TRUE(SpinUpAsyncCServerHelper(std::move(server), &loop));
// generated interface API
fidl::test::llcpp::basictypes::TestInterface::SyncClient test(std::move(client));
uint32_t out_index;
int32_t out_field;
fidl::test::llcpp::basictypes::SimpleUnion simple_union;
simple_union.mutable_field_b() = 456;
// perform call
zx_status_t status = test.ConsumeSimpleUnion(std::move(simple_union), &out_index, &out_field);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(out_index, 1);
ASSERT_EQ(out_field, 456);
TearDownAsyncCServerHelper(loop);
END_TEST;
}
bool SyncCallerAllocateCallUnionTest() {
BEGIN_TEST;
zx::channel client, server;
ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
async_loop_t* loop = nullptr;
ASSERT_TRUE(SpinUpAsyncCServerHelper(std::move(server), &loop));
// generated interface API
fidl::test::llcpp::basictypes::TestInterface::SyncClient test(std::move(client));
uint32_t out_index;
int32_t out_field;
fidl::test::llcpp::basictypes::SimpleUnion simple_union;
simple_union.mutable_field_b() = 456;
// perform call
FIDL_ALIGNDECL uint8_t request_buf[512] = {};
FIDL_ALIGNDECL uint8_t response_buf[512] = {};
auto result = test.ConsumeSimpleUnion(fidl::BytePart(request_buf, sizeof(request_buf)),
std::move(simple_union),
fidl::BytePart(response_buf, sizeof(response_buf)),
&out_index, &out_field);
ASSERT_EQ(result.status, ZX_OK);
ASSERT_NULL(result.error, result.error);
ASSERT_EQ(out_index, 1);
ASSERT_EQ(out_field, 456);
TearDownAsyncCServerHelper(loop);
END_TEST;
}
}
// LLCPP sync server tests: interop between C client and LLCPP server
namespace {
namespace gen = fidl::test::llcpp::basictypes;
class Server : public gen::TestInterface::Interface {
public:
void ConsumeSimpleStruct(gen::SimpleStruct arg,
ConsumeSimpleStructCompleter::Sync txn) override {
num_struct_calls_.fetch_add(1);
// Verify that all the handles are valid channels
if (!IsPeerValid(zx::unowned_eventpair(arg.ep))) {
txn.Reply(ZX_ERR_INVALID_ARGS, -1);
return;
}
for (auto& row : arg.arr) {
for (auto& handle : row) {
if (!IsPeerValid(zx::unowned_eventpair(handle))) {
txn.Reply(ZX_ERR_INVALID_ARGS, -1);
return;
}
}
}
// Loop back field argument
txn.Reply(ZX_OK, arg.field);
}
void ConsumeSimpleUnion(gen::SimpleUnion arg,
ConsumeSimpleUnionCompleter::Sync txn) override {
num_union_calls_.fetch_add(1);
if (arg.is_field_a()) {
txn.Reply(0, arg.field_a());
} else if (arg.is_field_b()) {
txn.Reply(1, arg.field_b());
} else {
txn.Reply(std::numeric_limits<uint32_t>::max(), -1);
}
}
uint64_t num_struct_calls() const { return num_struct_calls_.load(); }
uint64_t num_union_calls() const { return num_union_calls_.load(); }
private:
std::atomic<uint64_t> num_struct_calls_ = 0;
std::atomic<uint64_t> num_union_calls_ = 0;
};
bool SpinUp(zx::channel server, Server* impl, std::unique_ptr<async::Loop> *out_loop) {
BEGIN_HELPER;
auto loop = std::make_unique<async::Loop>(&kAsyncLoopConfigAttachToThread);
zx_status_t status = fidl::Bind(loop->dispatcher(),
std::move(server),
impl);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(loop->StartThread("test_llcpp_basictypes_server"), ZX_OK);
*out_loop = std::move(loop);
END_HELPER;
}
bool ServerUnionTest() {
BEGIN_TEST;
Server server_impl;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
std::unique_ptr<async::Loop> loop;
ASSERT_TRUE(SpinUp(std::move(server_chan), &server_impl, &loop));
constexpr uint32_t kNumIterations = 100;
for (uint32_t i = 0; i < kNumIterations; i++) {
ASSERT_EQ(server_impl.num_struct_calls(), 0);
ASSERT_EQ(server_impl.num_union_calls(), i);
fidl_test_llcpp_basictypes_SimpleUnion simple_union = {};
simple_union.tag = fidl_test_llcpp_basictypes_SimpleUnionTag_field_a;
simple_union.field_a = 5;
uint32_t index = std::numeric_limits<uint32_t>::max();
int32_t field;
ASSERT_EQ(fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleUnion(client_chan.get(),
&simple_union,
&index,
&field),
ZX_OK);
ASSERT_EQ(index, 0);
ASSERT_EQ(field, 5);
}
ASSERT_EQ(server_impl.num_union_calls(), kNumIterations);
END_TEST;
}
bool ServerStructTest() {
BEGIN_TEST;
Server server_impl;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
std::unique_ptr<async::Loop> loop;
ASSERT_TRUE(SpinUp(std::move(server_chan), &server_impl, &loop));
fidl_test_llcpp_basictypes_SimpleStruct simple_struct = {};
simple_struct.field = 123;
// make sure array shape is as expected (5 by 4)
constexpr size_t kNumRow = 5;
constexpr size_t kNumCol = 4;
constexpr size_t kNumHandlesInArray = kNumRow * kNumCol;
static_assert(ArrayCount(simple_struct.arr) == kNumRow);
static_assert(ArrayCount(simple_struct.arr[0]) == kNumCol);
// insert handles to be sent over
zx::eventpair single_handle_payload;
zx::eventpair single_handle_ourside;
ASSERT_EQ(zx::eventpair::create(0, &single_handle_ourside, &single_handle_payload), ZX_OK);
std::unique_ptr<zx::eventpair[]> handle_payload(new zx::eventpair[kNumHandlesInArray]);
std::unique_ptr<zx::eventpair[]> handle_our_side(new zx::eventpair[kNumHandlesInArray]);
for (size_t i = 0; i < kNumHandlesInArray; i++) {
ASSERT_EQ(zx::eventpair::create(0, &handle_our_side[i], &handle_payload[i]), ZX_OK);
}
// fill the |ep| field
simple_struct.ep = single_handle_payload.release();
// fill the 2D handles array
for (size_t i = 0; i < kNumRow; i++) {
for (size_t j = 0; j < kNumCol; j++) {
simple_struct.arr[i][j] = handle_payload[i * kNumCol + j].release();
}
}
// call
int32_t out_status;
int32_t out_field;
zx_status_t status = fidl_test_llcpp_basictypes_TestInterfaceConsumeSimpleStruct(
client_chan.get(), &simple_struct, &out_status, &out_field);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(out_status, ZX_OK);
ASSERT_EQ(out_field, 123);
ASSERT_EQ(server_impl.num_struct_calls(), 1);
ASSERT_EQ(server_impl.num_union_calls(), 0);
END_TEST;
}
}
BEGIN_TEST_CASE(llcpp_basictypes_tests)
RUN_NAMED_TEST_SMALL("client: raw channel call (passing struct)", RawChannelCallStructTest)
RUN_NAMED_TEST_SMALL("client: raw channel call (passing union)", RawChannelCallUnionTest)
RUN_NAMED_TEST_SMALL("client: generated binding (passing struct)", SyncCallStructTest)
RUN_NAMED_TEST_SMALL("client: generated binding (passing union)", SyncCallUnionTest)
RUN_NAMED_TEST_SMALL("client: generated binding (passing struct, caller allocating)",
SyncCallerAllocateCallStructTest)
RUN_NAMED_TEST_SMALL("client: generated binding (passing union, caller allocating)",
SyncCallerAllocateCallUnionTest)
RUN_NAMED_TEST_SMALL("server: passing union", ServerUnionTest)
RUN_NAMED_TEST_SMALL("server: passing struct", ServerStructTest)
END_TEST_CASE(llcpp_basictypes_tests)