blob: 3e85240f2bcd45045a63fa2e90f0e3c227a11451 [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 <atomic>
#include <cstdlib>
#include <fidl/test/llcpp/dirent/c/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/loop.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/fidl-utils/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_dirent.h"
// Namespace shorthand for bindings generated code
namespace gen = fidl::test::llcpp::dirent;
// Toy test data
namespace {
static_assert(gen::SMALL_DIR_VECTOR_SIZE == 3);
gen::DirEnt golden_dirents_array[gen::SMALL_DIR_VECTOR_SIZE] = {
gen::DirEnt {
.is_dir = false,
.name = fidl::StringView { 2, "ab" },
.some_flags = 0,
},
gen::DirEnt {
.is_dir = true,
.name = fidl::StringView { 3, "cde" },
.some_flags = 1,
},
gen::DirEnt {
.is_dir = false,
.name = fidl::StringView { 4, "fghi" },
.some_flags = 2,
},
};
fidl::VectorView golden_dirents = fidl::VectorView { 3, golden_dirents_array };
}
// Manual server implementation, since the C binding does not support
// types with more than one level of indirection.
// The server is an async loop that reads messages from the channel.
// It uses the llcpp raw API to decode the message, then calls one of the handlers.
namespace manual_server {
class Server {
public:
Server(zx::channel chan)
: chan_(std::move(chan)), loop_(&kAsyncLoopConfigNoAttachToThread) {}
zx_status_t Start() {
zx_status_t status = loop_.StartThread("llcpp_manual_server");
if (status != ZX_OK) {
return status;
}
return fidl_bind(loop_.dispatcher(),
chan_.get(),
Server::FidlDispatch,
this,
nullptr);
}
uint64_t CountNumDirectoriesNumCalls() const { return count_num_directories_num_calls_.load(); }
uint64_t ReadDirNumCalls() const { return read_dir_num_calls_.load(); }
uint64_t ConsumeDirectoriesNumCalls() const { return consume_directories_num_calls_.load(); }
uint64_t OneWayDirentsNumCalls() const { return one_way_dirents_num_calls_.load(); }
private:
template <typename FidlType>
zx_status_t Reply(fidl_txn_t* txn, fidl::DecodedMessage<FidlType> response_message) {
auto encode_result = fidl::Encode(std::move(response_message));
if (encode_result.status != ZX_OK) {
return encode_result.status;
}
auto& message = encode_result.message;
fidl_msg_t msg = {
.bytes = message.bytes().data(),
.handles = message.handles().data(),
.num_bytes = message.bytes().actual(),
.num_handles = message.handles().actual()
};
zx_status_t status = txn->reply(txn, &msg);
message.ReleaseBytesAndHandles();
return status;
}
zx_status_t DoCountNumDirectories(
fidl_txn_t* txn,
fidl::DecodedMessage<gen::DirEntTestInterface::CountNumDirectoriesRequest> decoded
) {
count_num_directories_num_calls_.fetch_add(1);
const auto& request = *decoded.message();
int64_t count = 0;
for (const auto& dirent : request.dirents) {
if (dirent.is_dir) {
count++;
}
}
gen::DirEntTestInterface::CountNumDirectoriesResponse response = {};
response.num_dir = count;
response._hdr.txid = request._hdr.txid;
fidl::DecodedMessage<gen::DirEntTestInterface::CountNumDirectoriesResponse> response_msg;
response_msg.Reset(fidl::BytePart(reinterpret_cast<uint8_t*>(&response),
sizeof(response),
sizeof(response)));
return Reply(txn, std::move(response_msg));
}
zx_status_t DoReadDir(
fidl_txn_t* txn,
fidl::DecodedMessage<gen::DirEntTestInterface::ReadDirRequest> decoded
) {
read_dir_num_calls_.fetch_add(1);
gen::DirEntTestInterface::ReadDirResponse response = {};
response._hdr.txid = decoded.message()->_hdr.txid;
response.dirents = golden_dirents;
uint8_t storage[256];
auto result = fidl::Linearize(&response, fidl::BytePart(storage, sizeof(storage)));
if (result.status != ZX_OK) {
return result.status;
}
return Reply(txn, std::move(result.message));
}
zx_status_t DoConsumeDirectories(
fidl_txn_t* txn,
fidl::DecodedMessage<gen::DirEntTestInterface::ConsumeDirectoriesRequest> decoded
) {
consume_directories_num_calls_.fetch_add(1);
EXPECT_EQ(decoded.message()->dirents.count(), 3);
gen::DirEntTestInterface::ConsumeDirectoriesResponse response = {};
response._hdr.ordinal = decoded.message()->_hdr.ordinal;
fidl::DecodedMessage<gen::DirEntTestInterface::ConsumeDirectoriesResponse> response_msg;
response_msg.Reset(fidl::BytePart(reinterpret_cast<uint8_t*>(&response),
sizeof(response),
sizeof(response)));
return Reply(txn, std::move(response_msg));
}
zx_status_t DoOneWayDirents(
fidl_txn_t* txn,
fidl::DecodedMessage<gen::DirEntTestInterface::OneWayDirentsRequest> decoded
) {
one_way_dirents_num_calls_.fetch_add(1);
EXPECT_EQ(decoded.message()->dirents.count(), 3);
EXPECT_EQ(decoded.message()->ep.signal_peer(0, ZX_EVENTPAIR_SIGNALED), ZX_OK);
// No response required for one-way calls.
return ZX_OK;
}
template <typename FidlType>
static fidl::DecodeResult<FidlType> DecodeAs(fidl_msg_t* msg) {
if (msg->num_handles > fidl::EncodedMessage<FidlType>::kResolvedMaxHandles) {
zx_handle_close_many(msg->handles, msg->num_handles);
return fidl::DecodeResult<FidlType>(ZX_ERR_INVALID_ARGS, "too many handles");
}
return fidl::Decode(fidl::EncodedMessage<FidlType>(msg));
}
static zx_status_t FidlDispatch(void* ctx,
fidl_txn_t* txn,
fidl_msg_t* msg,
const void* ops) {
if (msg->num_bytes < sizeof(fidl_message_header_t)) {
zx_handle_close_many(msg->handles, msg->num_handles);
return ZX_ERR_INVALID_ARGS;
}
fidl_message_header_t* hdr = reinterpret_cast<fidl_message_header_t*>(msg->bytes);
Server* server = reinterpret_cast<Server*>(ctx);
switch (hdr->ordinal) {
case fidl_test_llcpp_dirent_DirEntTestInterfaceCountNumDirectoriesOrdinal: {
auto result = DecodeAs<gen::DirEntTestInterface::CountNumDirectoriesRequest>(msg);
if (result.status != ZX_OK) {
return result.status;
}
return server->DoCountNumDirectories(txn, std::move(result.message));
}
case fidl_test_llcpp_dirent_DirEntTestInterfaceReadDirOrdinal: {
auto result = DecodeAs<gen::DirEntTestInterface::ReadDirRequest>(msg);
if (result.status != ZX_OK) {
return result.status;
}
return server->DoReadDir(txn, std::move(result.message));
}
case fidl_test_llcpp_dirent_DirEntTestInterfaceConsumeDirectoriesOrdinal: {
auto result = DecodeAs<gen::DirEntTestInterface::ConsumeDirectoriesRequest>(msg);
if (result.status != ZX_OK) {
return result.status;
}
return server->DoConsumeDirectories(txn, std::move(result.message));
}
case fidl_test_llcpp_dirent_DirEntTestInterfaceOneWayDirentsOrdinal: {
auto result = DecodeAs<gen::DirEntTestInterface::OneWayDirentsRequest>(msg);
if (result.status != ZX_OK) {
return result.status;
}
return server->DoOneWayDirents(txn, std::move(result.message));
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
zx::channel chan_;
async::Loop loop_;
std::atomic<uint64_t> count_num_directories_num_calls_ = 0;
std::atomic<uint64_t> read_dir_num_calls_ = 0;
std::atomic<uint64_t> consume_directories_num_calls_ = 0;
std::atomic<uint64_t> one_way_dirents_num_calls_ = 0;
};
} // namespace internal_server
// Server implemented with low-level C++ FIDL bindings
namespace llcpp_server {
class ServerBase : public gen::DirEntTestInterface::Interface {
public:
ServerBase(zx::channel chan)
: chan_(std::move(chan)), loop_(&kAsyncLoopConfigNoAttachToThread) {}
zx_status_t Start() {
zx_status_t status = loop_.StartThread("llcpp_bindings_server");
if (status != ZX_OK) {
return status;
}
return fidl::Bind(loop_.dispatcher(),
std::move(chan_),
this);
}
uint64_t CountNumDirectoriesNumCalls() const { return count_num_directories_num_calls_.load(); }
uint64_t ReadDirNumCalls() const { return read_dir_num_calls_.load(); }
uint64_t ConsumeDirectoriesNumCalls() const { return consume_directories_num_calls_.load(); }
uint64_t OneWayDirentsNumCalls() const { return one_way_dirents_num_calls_.load(); }
protected:
async_dispatcher_t* dispatcher() const { return loop_.dispatcher(); }
std::atomic<uint64_t> count_num_directories_num_calls_ = 0;
std::atomic<uint64_t> read_dir_num_calls_ = 0;
std::atomic<uint64_t> consume_directories_num_calls_ = 0;
std::atomic<uint64_t> one_way_dirents_num_calls_ = 0;
private:
zx::channel chan_;
async::Loop loop_;
};
// There are three implementations each exercising a different flavor of the reply API:
// C-style, caller-allocating, in-place, and async.
class CFlavorServer : public ServerBase {
public:
CFlavorServer(zx::channel chan) : ServerBase(std::move(chan)) {}
void CountNumDirectories(fidl::VectorView<gen::DirEnt> dirents,
CountNumDirectoriesCompleter::Sync txn) override {
count_num_directories_num_calls_.fetch_add(1);
int64_t count = 0;
for (const auto& dirent : dirents) {
if (dirent.is_dir) {
count++;
}
}
txn.Reply(count);
}
void ReadDir(ReadDirCompleter::Sync txn) override {
read_dir_num_calls_.fetch_add(1);
txn.Reply(golden_dirents);
}
// |ConsumeDirectories| has zero number of arguments in its return value, hence only the
// C-flavor reply API is generated.
void ConsumeDirectories(fidl::VectorView<gen::DirEnt> dirents,
ConsumeDirectoriesCompleter::Sync txn) override {
consume_directories_num_calls_.fetch_add(1);
EXPECT_EQ(dirents.count(), 3);
txn.Reply();
}
// |OneWayDirents| has no return value, hence there is no reply API generated
void OneWayDirents(fidl::VectorView<gen::DirEnt> dirents,
zx::eventpair ep,
OneWayDirentsCompleter::Sync txn) override {
one_way_dirents_num_calls_.fetch_add(1);
EXPECT_EQ(dirents.count(), 3);
EXPECT_EQ(ep.signal_peer(0, ZX_EVENTPAIR_SIGNALED), ZX_OK);
// No response required for one-way calls.
}
};
class CallerAllocateServer : public ServerBase {
public:
CallerAllocateServer(zx::channel chan) : ServerBase(std::move(chan)) {}
void CountNumDirectories(fidl::VectorView<gen::DirEnt> dirents,
CountNumDirectoriesCompleter::Sync txn) override {
count_num_directories_num_calls_.fetch_add(1);
int64_t count = 0;
for (const auto& dirent : dirents) {
if (dirent.is_dir) {
count++;
}
}
uint8_t storage[256];
txn.Reply(::fidl::BytePart(storage, sizeof(storage)), count);
}
void ReadDir(ReadDirCompleter::Sync txn) override {
read_dir_num_calls_.fetch_add(1);
uint8_t storage[256];
txn.Reply(fidl::BytePart(storage, sizeof(storage)), golden_dirents);
}
// |ConsumeDirectories| has zero number of arguments in its return value, hence only the
// C-flavor reply API is applicable.
void ConsumeDirectories(fidl::VectorView<gen::DirEnt> dirents,
ConsumeDirectoriesCompleter::Sync txn) override {
ZX_ASSERT_MSG(false, "Never used by unit tests");
}
// |OneWayDirents| has no return value, hence there is no reply API generated
void OneWayDirents(fidl::VectorView<gen::DirEnt> dirents,
zx::eventpair ep,
OneWayDirentsCompleter::Sync) override {
ZX_ASSERT_MSG(false, "Never used by unit tests");
}
};
class InPlaceServer : public ServerBase {
public:
InPlaceServer(zx::channel chan) : ServerBase(std::move(chan)) {}
void CountNumDirectories(fidl::VectorView<gen::DirEnt> dirents,
CountNumDirectoriesCompleter::Sync txn) override {
count_num_directories_num_calls_.fetch_add(1);
int64_t count = 0;
for (const auto& dirent : dirents) {
if (dirent.is_dir) {
count++;
}
}
gen::DirEntTestInterface::CountNumDirectoriesResponse response = {};
response.num_dir = count;
fidl::DecodedMessage<gen::DirEntTestInterface::CountNumDirectoriesResponse> response_msg;
response_msg.Reset(fidl::BytePart(reinterpret_cast<uint8_t*>(&response),
sizeof(response),
sizeof(response)));
txn.Reply(std::move(response_msg));
}
void ReadDir(ReadDirCompleter::Sync txn) override {
read_dir_num_calls_.fetch_add(1);
gen::DirEntTestInterface::ReadDirResponse response = {};
response.dirents = golden_dirents;
uint8_t storage[256];
auto result = fidl::Linearize(&response, fidl::BytePart(storage, sizeof(storage)));
if (result.status != ZX_OK) {
txn.Close(result.status);
return;
}
txn.Reply(std::move(result.message));
}
// |ConsumeDirectories| has zero number of arguments in its return value, hence only the
// C-flavor reply API is applicable.
void ConsumeDirectories(fidl::VectorView<gen::DirEnt> dirents,
ConsumeDirectoriesCompleter::Sync txn) override {
ZX_ASSERT_MSG(false, "Never used by unit tests");
}
// |OneWayDirents| has no return value, hence there is no reply API generated
void OneWayDirents(fidl::VectorView<gen::DirEnt> dirents,
zx::eventpair ep,
OneWayDirentsCompleter::Sync) override {
ZX_ASSERT_MSG(false, "Never used by unit tests");
}
};
// Every reply is delayed using async::PostTask
class AsyncReplyServer : public ServerBase {
public:
AsyncReplyServer(zx::channel chan) : ServerBase(std::move(chan)) {}
void CountNumDirectories(fidl::VectorView<gen::DirEnt> dirents,
CountNumDirectoriesCompleter::Sync txn) override {
count_num_directories_num_calls_.fetch_add(1);
int64_t count = 0;
for (const auto& dirent : dirents) {
if (dirent.is_dir) {
count++;
}
}
async::PostTask(dispatcher(), [txn = txn.ToAsync(), count] () mutable {
txn.Reply(count);
});
}
void ReadDir(ReadDirCompleter::Sync txn) override {
read_dir_num_calls_.fetch_add(1);
async::PostTask(dispatcher(), [txn = txn.ToAsync()] () mutable {
txn.Reply(golden_dirents);
});
}
void ConsumeDirectories(fidl::VectorView<gen::DirEnt> dirents,
ConsumeDirectoriesCompleter::Sync txn) override {
consume_directories_num_calls_.fetch_add(1);
EXPECT_EQ(dirents.count(), 3);
async::PostTask(dispatcher(), [txn = txn.ToAsync()] () mutable {
txn.Reply();
});
}
// |OneWayDirents| has no return value, hence there is no reply API generated
void OneWayDirents(fidl::VectorView<gen::DirEnt> dirents,
zx::eventpair ep,
OneWayDirentsCompleter::Sync) override {
ZX_ASSERT_MSG(false, "Never used by unit tests");
}
};
} // namespace llcpp_server
// Parametric tests allowing choosing a custom server implementation
namespace {
class Random {
public:
Random(unsigned int seed = static_cast<unsigned int>(zx_ticks_get()))
: seed_(seed) {}
unsigned int seed() const { return seed_; }
unsigned int UpTo(unsigned int limit) {
unsigned int next = rand_r(&seed_);
return next % limit;
}
private:
unsigned int seed_;
};
template <size_t kNumDirents>
fidl::Array<gen::DirEnt, kNumDirents> RandomlyFillDirEnt(char* name,
char* seed_description) {
Random random;
sprintf(seed_description, "Seed: %d", random.seed());
fidl::Array<gen::DirEnt, kNumDirents> dirents;
for (size_t i = 0; i < kNumDirents; i++) {
int str_len = random.UpTo(gen::TEST_MAX_PATH) + 1;
bool is_dir = random.UpTo(2) == 0;
int32_t flags = static_cast<int32_t>(random.UpTo(1000));
dirents[i] = gen::DirEnt {
.is_dir = is_dir,
.name = fidl::StringView { static_cast<uint64_t>(str_len), name },
.some_flags = flags
};
}
return dirents;
}
template <typename Server>
bool SimpleCountNumDirectories() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
constexpr size_t kNumDirents = 80;
std::unique_ptr<char[]> name(new char[gen::TEST_MAX_PATH]);
for (uint32_t i = 0; i < gen::TEST_MAX_PATH; i++) {
name[i] = 'A';
}
ASSERT_EQ(server.CountNumDirectoriesNumCalls(), 0);
constexpr uint64_t kNumIterations = 100;
// Stress test linearizing dirents
for (uint64_t iter = 0; iter < kNumIterations; iter++) {
char seed_description[100] = {};
auto dirents = RandomlyFillDirEnt<kNumDirents>(name.get(), seed_description);
int64_t num_dir;
zx_status_t status = client.CountNumDirectories(fidl::VectorView<gen::DirEnt> {
static_cast<uint64_t>(dirents.size()),
dirents.data()
},
&num_dir);
int64_t expected_num_dir = 0;
for (const auto& dirent : dirents) {
if (dirent.is_dir) {
expected_num_dir++;
}
}
ASSERT_EQ(status, ZX_OK, seed_description);
ASSERT_EQ(expected_num_dir, num_dir, seed_description);
}
ASSERT_EQ(server.CountNumDirectoriesNumCalls(), kNumIterations);
END_TEST;
}
template <typename Server>
bool CallerAllocateCountNumDirectories() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
Random random;
constexpr size_t kNumDirents = 80;
std::unique_ptr<char[]> name(new char[gen::TEST_MAX_PATH]);
for (uint32_t i = 0; i < gen::TEST_MAX_PATH; i++) {
name[i] = 'B';
}
ASSERT_EQ(server.CountNumDirectoriesNumCalls(), 0);
constexpr uint64_t kNumIterations = 100;
// Stress test linearizing dirents
for (uint64_t iter = 0; iter < kNumIterations; iter++) {
char seed_description[100] = {};
auto dirents = RandomlyFillDirEnt<kNumDirents>(name.get(), seed_description);
int64_t num_dir;
std::unique_ptr<uint8_t[]> request_buf(new uint8_t[ZX_CHANNEL_MAX_MSG_BYTES]);
FIDL_ALIGNDECL uint8_t response_buf[128];
auto result = client.CountNumDirectories(fidl::BytePart(request_buf.get(),
ZX_CHANNEL_MAX_MSG_BYTES),
fidl::VectorView<gen::DirEnt> {
static_cast<uint64_t>(dirents.size()),
dirents.data()
},
fidl::BytePart(response_buf,
sizeof(response_buf)),
&num_dir);
int64_t expected_num_dir = 0;
for (const auto& dirent : dirents) {
if (dirent.is_dir) {
expected_num_dir++;
}
}
ASSERT_EQ(result.status, ZX_OK, seed_description);
ASSERT_NULL(result.error, seed_description);
ASSERT_EQ(expected_num_dir, num_dir, seed_description);
}
ASSERT_EQ(server.CountNumDirectoriesNumCalls(), kNumIterations);
END_TEST;
}
template <typename Server>
bool CallerAllocateReadDir() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
ASSERT_EQ(server.ReadDirNumCalls(), 0);
constexpr uint64_t kNumIterations = 100;
// Stress test server-linearizing dirents
for (uint64_t iter = 0; iter < kNumIterations; iter++) {
std::unique_ptr<uint8_t[]> response_buf(new uint8_t[ZX_CHANNEL_MAX_MSG_BYTES]);
fidl::VectorView<gen::DirEnt> dirents;
auto result = client.ReadDir(fidl::BytePart(response_buf.get(), ZX_CHANNEL_MAX_MSG_BYTES),
&dirents);
ASSERT_EQ(result.status, ZX_OK);
ASSERT_NULL(result.error, result.error);
ASSERT_EQ(dirents.count(), golden_dirents.count());
for (uint64_t i = 0; i < dirents.count(); i++) {
auto actual = dirents[i];
auto expected = golden_dirents[i];
EXPECT_EQ(actual.is_dir, expected.is_dir);
EXPECT_EQ(actual.some_flags, expected.some_flags);
ASSERT_EQ(actual.name.size(), expected.name.size());
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(actual.name.data()),
reinterpret_cast<const uint8_t*>(expected.name.data()),
actual.name.size(),
"dirent name mismatch");
}
}
ASSERT_EQ(server.ReadDirNumCalls(), kNumIterations);
END_TEST;
}
template <typename Server>
bool InPlaceReadDir() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
ASSERT_EQ(server.ReadDirNumCalls(), 0);
constexpr uint64_t kNumIterations = 100;
// Stress test server-linearizing dirents
for (uint64_t iter = 0; iter < kNumIterations; iter++) {
std::unique_ptr<uint8_t[]> response_buf(new uint8_t[ZX_CHANNEL_MAX_MSG_BYTES]);
auto result = client.ReadDir(fidl::BytePart(response_buf.get(), ZX_CHANNEL_MAX_MSG_BYTES));
ASSERT_EQ(result.status, ZX_OK);
const auto& dirents = result.message.message()->dirents;
ASSERT_EQ(dirents.count(), golden_dirents.count());
for (uint64_t i = 0; i < dirents.count(); i++) {
auto actual = dirents[i];
auto expected = golden_dirents[i];
EXPECT_EQ(actual.is_dir, expected.is_dir);
EXPECT_EQ(actual.some_flags, expected.some_flags);
ASSERT_EQ(actual.name.size(), expected.name.size());
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(actual.name.data()),
reinterpret_cast<const uint8_t*>(expected.name.data()),
actual.name.size(),
"dirent name mismatch");
}
}
ASSERT_EQ(server.ReadDirNumCalls(), kNumIterations);
END_TEST;
}
template <typename Server>
bool SimpleConsumeDirectories() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
ASSERT_EQ(server.ConsumeDirectoriesNumCalls(), 0);
ASSERT_EQ(client.ConsumeDirectories(golden_dirents), ZX_OK);
ASSERT_EQ(server.ConsumeDirectoriesNumCalls(), 1);
END_TEST;
}
template <typename Server>
bool CallerAllocateConsumeDirectories() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
ASSERT_EQ(server.ConsumeDirectoriesNumCalls(), 0);
std::unique_ptr<uint8_t[]> request_buf(new uint8_t[ZX_CHANNEL_MAX_MSG_BYTES]);
auto result = client.ConsumeDirectories(fidl::BytePart(request_buf.get(),
ZX_CHANNEL_MAX_MSG_BYTES),
golden_dirents);
ASSERT_EQ(result.status, ZX_OK);
ASSERT_NULL(result.error, result.error);
ASSERT_EQ(server.ConsumeDirectoriesNumCalls(), 1);
END_TEST;
}
template <typename Server>
bool InPlaceConsumeDirectories() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
ASSERT_EQ(server.ConsumeDirectoriesNumCalls(), 0);
std::unique_ptr<uint8_t[]> request_buf(new uint8_t[ZX_CHANNEL_MAX_MSG_BYTES]);
gen::DirEntTestInterface::ConsumeDirectoriesRequest request = {};
request.dirents = golden_dirents;
auto linearize_result = fidl::Linearize(&request,
fidl::BytePart(request_buf.get(),
ZX_CHANNEL_MAX_MSG_BYTES));
ASSERT_EQ(linearize_result.status, ZX_OK);
ASSERT_EQ(client.ConsumeDirectories(std::move(linearize_result.message)), ZX_OK);
ASSERT_EQ(server.ConsumeDirectoriesNumCalls(), 1);
END_TEST;
}
template <typename Server>
bool SimpleOneWayDirents() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
zx::eventpair client_ep, server_ep;
ASSERT_EQ(zx::eventpair::create(0, &client_ep, &server_ep), ZX_OK);
ASSERT_EQ(server.OneWayDirentsNumCalls(), 0);
ASSERT_EQ(client.OneWayDirents(golden_dirents, std::move(server_ep)), ZX_OK);
zx_signals_t signals = 0;
client_ep.wait_one(ZX_EVENTPAIR_SIGNALED, zx::time::infinite(), &signals);
ASSERT_EQ(signals & ZX_EVENTPAIR_SIGNALED, ZX_EVENTPAIR_SIGNALED);
ASSERT_EQ(server.OneWayDirentsNumCalls(), 1);
END_TEST;
}
template <typename Server>
bool CallerAllocateOneWayDirents() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
zx::eventpair client_ep, server_ep;
ASSERT_EQ(zx::eventpair::create(0, &client_ep, &server_ep), ZX_OK);
ASSERT_EQ(server.OneWayDirentsNumCalls(), 0);
uint8_t request_buf[512];
ASSERT_EQ(client.OneWayDirents(fidl::BytePart(request_buf, sizeof(request_buf)),
golden_dirents, std::move(server_ep)),
ZX_OK);
zx_signals_t signals = 0;
client_ep.wait_one(ZX_EVENTPAIR_SIGNALED, zx::time::infinite(), &signals);
ASSERT_EQ(signals & ZX_EVENTPAIR_SIGNALED, ZX_EVENTPAIR_SIGNALED);
ASSERT_EQ(server.OneWayDirentsNumCalls(), 1);
END_TEST;
}
template <typename Server>
bool InPlaceOneWayDirents() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
Server server(std::move(server_chan));
ASSERT_EQ(server.Start(), ZX_OK);
gen::DirEntTestInterface::SyncClient client(std::move(client_chan));
constexpr uint64_t kNumIterations = 100;
for (uint64_t iter = 0; iter < kNumIterations; iter++) {
zx::eventpair client_ep, server_ep;
ASSERT_EQ(zx::eventpair::create(0, &client_ep, &server_ep), ZX_OK);
ASSERT_EQ(server.OneWayDirentsNumCalls(), iter);
std::unique_ptr<uint8_t[]> request_buf(new uint8_t[ZX_CHANNEL_MAX_MSG_BYTES]);
gen::DirEntTestInterface::OneWayDirentsRequest request = {};
request.dirents = golden_dirents;
request.ep = std::move(server_ep);
auto linearize_result = fidl::Linearize(&request,
fidl::BytePart(request_buf.get(),
ZX_CHANNEL_MAX_MSG_BYTES));
ASSERT_EQ(linearize_result.status, ZX_OK);
ASSERT_EQ(client.OneWayDirents(std::move(linearize_result.message)), ZX_OK);
zx_signals_t signals = 0;
client_ep.wait_one(ZX_EVENTPAIR_SIGNALED, zx::time::infinite(), &signals);
ASSERT_EQ(signals & ZX_EVENTPAIR_SIGNALED, ZX_EVENTPAIR_SIGNALED);
ASSERT_EQ(server.OneWayDirentsNumCalls(), iter + 1);
}
END_TEST;
}
template <typename DirentArray>
bool AssertReadOnDirentsEvent(zx::channel chan, const DirentArray& expected_dirents) {
BEGIN_HELPER;
gen::DirEntTestInterface::SyncClient client(std::move(chan));
bool ok = client.HandleEvents(gen::DirEntTestInterface::EventHandlers {
.on_dirents = [&](::fidl::VectorView<gen::DirEnt> dirents) {
ASSERT_EQ(dirents.count(), expected_dirents.size());
for (uint64_t i = 0; i < dirents.count(); i++) {
ASSERT_EQ(dirents[i].is_dir, expected_dirents[i].is_dir);
ASSERT_EQ(dirents[i].some_flags, expected_dirents[i].some_flags);
ASSERT_EQ(dirents[i].name.size(), expected_dirents[i].name.size());
ASSERT_BYTES_EQ(reinterpret_cast<const uint8_t*>(dirents[i].name.data()),
reinterpret_cast<const uint8_t*>(expected_dirents[i].name.data()),
dirents[i].name.size(),
"dirent name mismatch");
}
return true;
},
.unknown = [&]() {
ASSERT_FALSE(true, "unknown event received; expected OnDirents");
}
});
ASSERT_TRUE(ok);
END_HELPER;
}
bool CFlavorSendOnDirents() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
constexpr size_t kNumDirents = 80;
std::unique_ptr<char[]> name(new char[gen::TEST_MAX_PATH]);
for (uint32_t i = 0; i < gen::TEST_MAX_PATH; i++) {
name[i] = 'A';
}
char seed_description[100] = {};
auto dirents = RandomlyFillDirEnt<kNumDirents>(name.get(), seed_description);
auto status = gen::DirEntTestInterface::SendOnDirentsEvent(zx::unowned_channel(server_chan),
fidl::VectorView<gen::DirEnt> {
static_cast<uint64_t>(
dirents.size()),
dirents.data()
});
ASSERT_EQ(status, ZX_OK, seed_description);
ASSERT_TRUE(AssertReadOnDirentsEvent(std::move(client_chan), dirents), seed_description);
END_TEST;
}
bool CallerAllocateSendOnDirents() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
constexpr size_t kNumDirents = 80;
std::unique_ptr<char[]> name(new char[gen::TEST_MAX_PATH]);
for (uint32_t i = 0; i < gen::TEST_MAX_PATH; i++) {
name[i] = 'B';
}
char seed_description[100] = {};
auto dirents = RandomlyFillDirEnt<kNumDirents>(name.get(), seed_description);
auto storage = std::make_unique<uint8_t[]>(ZX_CHANNEL_MAX_MSG_BYTES);
::fidl::BytePart bytes(storage.get(), ZX_CHANNEL_MAX_MSG_BYTES);
auto status = gen::DirEntTestInterface::SendOnDirentsEvent(zx::unowned_channel(server_chan),
std::move(bytes),
fidl::VectorView<gen::DirEnt> {
static_cast<uint64_t>(
dirents.size()),
dirents.data()
});
ASSERT_EQ(status, ZX_OK, seed_description);
ASSERT_TRUE(AssertReadOnDirentsEvent(std::move(client_chan), dirents), seed_description);
END_TEST;
}
bool InPlaceSendOnDirents() {
BEGIN_TEST;
zx::channel client_chan, server_chan;
ASSERT_EQ(zx::channel::create(0, &client_chan, &server_chan), ZX_OK);
constexpr size_t kNumDirents = 80;
std::unique_ptr<char[]> name(new char[gen::TEST_MAX_PATH]);
for (uint32_t i = 0; i < gen::TEST_MAX_PATH; i++) {
name[i] = 'C';
}
char seed_description[100] = {};
auto dirents = RandomlyFillDirEnt<kNumDirents>(name.get(), seed_description);
auto storage = std::make_unique<uint8_t[]>(ZX_CHANNEL_MAX_MSG_BYTES);
::gen::DirEntTestInterface::OnDirentsResponse event = {};
event.dirents = fidl::VectorView<gen::DirEnt> {
static_cast<uint64_t>(
dirents.size()),
dirents.data()
};
auto linearize_result = ::fidl::Linearize(&event,
::fidl::BytePart(storage.get(),
ZX_CHANNEL_MAX_MSG_BYTES));
ASSERT_EQ(linearize_result.status, ZX_OK, linearize_result.error);
auto status = gen::DirEntTestInterface::SendOnDirentsEvent(zx::unowned_channel(server_chan),
std::move(linearize_result.message));
ASSERT_EQ(status, ZX_OK, seed_description);
ASSERT_TRUE(AssertReadOnDirentsEvent(std::move(client_chan), dirents), seed_description);
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(llcpp_interface_dirent_tests)
RUN_NAMED_TEST_SMALL("client: CountNumDirectories, C-flavor",
SimpleCountNumDirectories<manual_server::Server>)
RUN_NAMED_TEST_SMALL("client: CountNumDirectories, caller-allocating",
CallerAllocateCountNumDirectories<manual_server::Server>)
RUN_NAMED_TEST_SMALL("client: ReadDir, caller-allocating",
CallerAllocateReadDir<manual_server::Server>)
RUN_NAMED_TEST_SMALL("client: ReadDir, in-place",
InPlaceReadDir<manual_server::Server>)
RUN_NAMED_TEST_SMALL("client: ConsumeDirectories, C-flavor",
SimpleConsumeDirectories<manual_server::Server>)
RUN_NAMED_TEST_SMALL("client: ConsumeDirectories, caller-allocating",
CallerAllocateConsumeDirectories<manual_server::Server>)
RUN_NAMED_TEST_SMALL("client: ConsumeDirectories, in-place",
InPlaceConsumeDirectories<manual_server::Server>)
RUN_NAMED_TEST_SMALL("client: OneWayDirents, C-flavor",
SimpleOneWayDirents<manual_server::Server>)
RUN_NAMED_TEST_SMALL("client: OneWayDirents, caller-allocating",
CallerAllocateOneWayDirents<manual_server::Server>)
RUN_NAMED_TEST_SMALL("client: OneWayDirents, in-place",
InPlaceOneWayDirents<manual_server::Server>)
RUN_NAMED_TEST_SMALL("server: CountNumDirectories, C-flavor",
SimpleCountNumDirectories<llcpp_server::CFlavorServer>)
RUN_NAMED_TEST_SMALL("server: CountNumDirectories, caller-allocating",
SimpleCountNumDirectories<llcpp_server::CallerAllocateServer>)
RUN_NAMED_TEST_SMALL("server: CountNumDirectories, in-place",
SimpleCountNumDirectories<llcpp_server::InPlaceServer>)
RUN_NAMED_TEST_SMALL("server: CountNumDirectories, async",
SimpleCountNumDirectories<llcpp_server::AsyncReplyServer>)
RUN_NAMED_TEST_SMALL("server: ReadDir, C-flavor",
InPlaceReadDir<llcpp_server::CFlavorServer>)
RUN_NAMED_TEST_SMALL("server: ReadDir, caller-allocating",
InPlaceReadDir<llcpp_server::CallerAllocateServer>)
RUN_NAMED_TEST_SMALL("server: ReadDir, in-place",
InPlaceReadDir<llcpp_server::InPlaceServer>)
RUN_NAMED_TEST_SMALL("server: ReadDir, async",
InPlaceReadDir<llcpp_server::AsyncReplyServer>)
RUN_NAMED_TEST_SMALL("server: ConsumeDirectories, C-flavor",
SimpleConsumeDirectories<llcpp_server::CFlavorServer>)
RUN_NAMED_TEST_SMALL("server: ConsumeDirectories, async",
SimpleConsumeDirectories<llcpp_server::AsyncReplyServer>)
RUN_NAMED_TEST_SMALL("server: OneWayDirents, C-flavor",
SimpleOneWayDirents<llcpp_server::CFlavorServer>)
RUN_NAMED_TEST_SMALL("server: Send OnDirents event, C-flavor",
CFlavorSendOnDirents)
RUN_NAMED_TEST_SMALL("server: Send OnDirents event, caller-allocating",
CallerAllocateSendOnDirents)
RUN_NAMED_TEST_SMALL("server: Send OnDirents event, in-place",
InPlaceSendOnDirents)
END_TEST_CASE(llcpp_interface_dirent_tests)