blob: b83c6a89ea545817ef36b412e98bef1b5acf3875 [file] [log] [blame]
// Copyright 2022 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 <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/zx/time.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <algorithm>
#include <future>
#include <optional>
#include <test/unknown/interactions/cpp/fidl.h>
#include <test/unknown/interactions/cpp/fidl_test_base.h>
#include <zxtest/zxtest.h>
namespace {
namespace test = ::test::unknown_::interactions;
class UnknownInteractionsImpl : public test::testing::UnknownInteractionsProtocol_TestBase {
void NotImplemented_(const std::string& name) override {
ADD_FAILURE("Method %s called unexpectedly", name.c_str());
}
};
class UnknownInteractions : public ::zxtest::Test {
protected:
void SetUp() override {
loop_.emplace(&kAsyncLoopConfigAttachToCurrentThread);
ASSERT_EQ(zx::channel::create(0, &client_end_, &server_end_), ZX_OK);
}
async::Loop& loop() { return loop_.value(); }
fidl::InterfaceRequest<test::UnknownInteractionsProtocol> TakeServerEnd() {
EXPECT_TRUE(server_end_.is_valid());
return fidl::InterfaceRequest<test::UnknownInteractionsProtocol>(std::move(server_end_));
}
zx::channel TakeServerChannel() {
EXPECT_TRUE(server_end_.is_valid());
return std::move(server_end_);
}
zx::channel TakeClientChannel() {
EXPECT_TRUE(client_end_.is_valid());
return std::move(client_end_);
}
fidl::SynchronousInterfacePtr<test::UnknownInteractionsProtocol> SyncClient() {
EXPECT_TRUE(client_end_.is_valid());
fidl::SynchronousInterfacePtr<test::UnknownInteractionsProtocol> client;
client.Bind(std::move(client_end_));
return client;
}
fidl::InterfacePtr<test::UnknownInteractionsProtocol> AsyncClient() {
EXPECT_TRUE(client_end_.is_valid());
fidl::InterfacePtr<test::UnknownInteractionsProtocol> client;
client.Bind(std::move(client_end_), loop_->dispatcher());
return client;
}
private:
std::optional<async::Loop> loop_;
zx::channel client_end_;
zx::channel server_end_;
};
constexpr std::array<uint8_t, 4> zero_txid = {0, 0, 0, 0};
// Helper for receiving raw data from a channel.
template <uint32_t N>
struct ReadResult {
// Status from reading.
zx_status_t status;
// Bytes from the read.
std::array<uint8_t, N> buf;
// Number of bytes read.
uint32_t num_bytes;
// Number of handles read.
uint32_t num_handles;
ReadResult() = delete;
// Construct a ReadResult by reading from a channel.
explicit ReadResult(const zx::channel& channel) {
status = channel.wait_one(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(),
nullptr);
if (status != ZX_OK)
return;
status = channel.read(/* flags= */ 0, buf.data(), /* handles= */ nullptr, N,
/* num_handles= */ 0, &num_bytes, &num_handles);
}
// Get the contents of the buffer excluding the transaction ID.
std::array<uint8_t, N - 4> buf_excluding_txid() {
std::array<uint8_t, N - 4> without_txid;
std::copy(buf.begin() + 4, buf.end(), without_txid.begin());
return without_txid;
}
// Get the transaction id portion of the buffer.
std::array<uint8_t, 4> buf_txid() {
std::array<uint8_t, 4> txid;
std::copy(buf.begin(), buf.begin() + 4, txid.begin());
return txid;
}
};
template <uint32_t N>
struct TwoWayServerResult : public ReadResult<N> {
// Status from sending a reply.
zx_status_t reply_status;
TwoWayServerResult() = delete;
using ReadResult<N>::ReadResult;
// Helper to send a reply to the read as a two-way message.
// Copies the txid (first four) bytes from |buf| into |reply_bytes| and sends
// the result on the channel, storing the status in |reply_status|.
template <uint32_t M>
void reply(const zx::channel& channel, std::array<uint8_t, M> reply_bytes) {
std::copy(this->buf.begin(), this->buf.begin() + 4, reply_bytes.begin());
reply_status = channel.write(/* flags= */ 0, reply_bytes.data(), M, /* handles= */ nullptr,
/* num_handles= */ 0);
}
};
TEST_F(UnknownInteractions, OneWayStrictSyncSend) {
auto client = SyncClient();
auto server = TakeServerChannel();
EXPECT_EQ(client->StrictOneWay(), ZX_OK);
ReadResult<16> received(server);
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
std::array<uint8_t, 16> expected{
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, //
0xd5, 0x82, 0xb3, 0x4c, 0x50, 0x81, 0xa5, 0x1f, //
};
EXPECT_EQ(received.buf, expected);
}
TEST_F(UnknownInteractions, OneWayFlexibleSyncSend) {
auto client = SyncClient();
auto server = TakeServerChannel();
EXPECT_EQ(client->FlexibleOneWay(), ZX_OK);
ReadResult<16> received(server);
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
std::array<uint8_t, 16> expected{
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x01, //
0xfc, 0x90, 0xbb, 0xe2, 0x7a, 0x27, 0x93, 0x27, //
};
EXPECT_EQ(received.buf, expected);
}
TEST_F(UnknownInteractions, TwoWayStrictSyncSend) {
auto client = SyncClient();
auto server = std::async(std::launch::async, [server = TakeServerChannel()]() {
TwoWayServerResult<16> result(server);
result.reply<16>(server, {
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, //
0xdc, 0xb0, 0x55, 0x70, 0x95, 0x6f, 0xba, 0x73, //
});
return result;
});
EXPECT_EQ(client->StrictTwoWay(), ZX_OK);
auto received = server.get();
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
EXPECT_EQ(received.reply_status, ZX_OK);
std::array<uint8_t, 12> expected{
0x02, 0x00, 0x00, 0x01, //
0xdc, 0xb0, 0x55, 0x70, 0x95, 0x6f, 0xba, 0x73, //
};
EXPECT_EQ(received.buf_excluding_txid(), expected);
EXPECT_NE(received.buf_txid(), zero_txid);
}
TEST_F(UnknownInteractions, TwoWayStrictErrSyncSend) {
auto client = SyncClient();
auto server = std::async(std::launch::async, [server = TakeServerChannel()]() {
TwoWayServerResult<16> result(server);
result.reply<32>(server, {
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, //
0xbb, 0x58, 0xe0, 0x08, 0x4e, 0xeb, 0x9b, 0x2e, //
// Result union with success envelope to satisfy client side:
// ordinal ---------------------------------|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
// inline value -----| nhandles | flags ---|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, //
});
return result;
});
test::UnknownInteractionsProtocol_StrictTwoWayErr_Result result;
EXPECT_EQ(client->StrictTwoWayErr(&result), ZX_OK);
auto received = server.get();
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
EXPECT_EQ(received.reply_status, ZX_OK);
std::array<uint8_t, 12> expected{
0x02, 0x00, 0x00, 0x01, //
0xbb, 0x58, 0xe0, 0x08, 0x4e, 0xeb, 0x9b, 0x2e, //
};
EXPECT_EQ(received.buf_excluding_txid(), expected);
EXPECT_NE(received.buf_txid(), zero_txid);
}
TEST_F(UnknownInteractions, TwoWayFlexibleSyncSend) {
auto client = SyncClient();
auto server = std::async(std::launch::async, [server = TakeServerChannel()]() {
TwoWayServerResult<16> result(server);
result.reply<32>(server, {
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x01, //
0x9d, 0x60, 0x95, 0x03, 0x7a, 0x51, 0x33, 0x1f, //
// Result union with success envelope to satisfy client side:
// ordinal ---------------------------------|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
// inline value -----| nhandles | flags ---|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, //
});
return result;
});
test::UnknownInteractionsProtocol_FlexibleTwoWay_Result result;
EXPECT_EQ(client->FlexibleTwoWay(&result), ZX_OK);
auto received = server.get();
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
EXPECT_EQ(received.reply_status, ZX_OK);
std::array<uint8_t, 12> expected{
0x02, 0x00, 0x80, 0x01, //
0x9d, 0x60, 0x95, 0x03, 0x7a, 0x51, 0x33, 0x1f, //
};
EXPECT_EQ(received.buf_excluding_txid(), expected);
EXPECT_NE(received.buf_txid(), zero_txid);
}
TEST_F(UnknownInteractions, OneWayStrictAsyncSend) {
auto client = AsyncClient();
auto server = TakeServerChannel();
client.set_error_handler(
[](zx_status_t status) { ADD_FAILURE("Got an error status: %d", status); });
client->StrictOneWay();
ReadResult<16> received(server);
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
std::array<uint8_t, 16> expected{
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, //
0xd5, 0x82, 0xb3, 0x4c, 0x50, 0x81, 0xa5, 0x1f, //
};
EXPECT_EQ(received.buf, expected);
}
TEST_F(UnknownInteractions, OneWayFlexibleAsyncSend) {
auto client = AsyncClient();
auto server = TakeServerChannel();
client.set_error_handler(
[](zx_status_t status) { ADD_FAILURE("Got an error status: %d", status); });
client->FlexibleOneWay();
ReadResult<16> received(server);
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
std::array<uint8_t, 16> expected{
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x01, //
0xfc, 0x90, 0xbb, 0xe2, 0x7a, 0x27, 0x93, 0x27, //
};
EXPECT_EQ(received.buf, expected);
}
TEST_F(UnknownInteractions, TwoWayStrictAsyncSend) {
auto client = AsyncClient();
auto server = TakeServerChannel();
client.set_error_handler(
[](zx_status_t status) { ADD_FAILURE("Got an error status: %d", status); });
bool response_received = false;
client->StrictTwoWay([&response_received]() { response_received = true; });
TwoWayServerResult<16> received(server);
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
std::array<uint8_t, 12> expected{
0x02, 0x00, 0x00, 0x01, //
0xdc, 0xb0, 0x55, 0x70, 0x95, 0x6f, 0xba, 0x73, //
};
EXPECT_EQ(received.buf_excluding_txid(), expected);
EXPECT_NE(received.buf_txid(), zero_txid);
received.reply<16>(server, {
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, //
0xdc, 0xb0, 0x55, 0x70, 0x95, 0x6f, 0xba, 0x73, //
});
EXPECT_EQ(received.reply_status, ZX_OK);
loop().RunUntilIdle();
EXPECT_TRUE(response_received);
}
TEST_F(UnknownInteractions, TwoWayStrictErrAsyncSend) {
auto client = AsyncClient();
auto server = TakeServerChannel();
client.set_error_handler(
[](zx_status_t status) { ADD_FAILURE("Got an error status: %d", status); });
bool response_received = false;
client->StrictTwoWayErr([&response_received](auto response) {
response_received = true;
EXPECT_TRUE(response.is_response());
});
TwoWayServerResult<16> received(server);
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
std::array<uint8_t, 12> expected{
0x02, 0x00, 0x00, 0x01, //
0xbb, 0x58, 0xe0, 0x08, 0x4e, 0xeb, 0x9b, 0x2e, //
};
EXPECT_EQ(received.buf_excluding_txid(), expected);
EXPECT_NE(received.buf_txid(), zero_txid);
received.reply<32>(server, {
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, //
0xbb, 0x58, 0xe0, 0x08, 0x4e, 0xeb, 0x9b, 0x2e, //
// Result union with success envelope to satisfy client side:
// ordinal ---------------------------------|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
// inline value -----| nhandles | flags ---|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, //
});
EXPECT_EQ(received.reply_status, ZX_OK);
loop().RunUntilIdle();
EXPECT_TRUE(response_received);
}
TEST_F(UnknownInteractions, TwoWayFlexibleAsyncSend) {
auto client = AsyncClient();
auto server = TakeServerChannel();
client.set_error_handler(
[](zx_status_t status) { ADD_FAILURE("Got an error status: %d", status); });
bool response_received = false;
client->FlexibleTwoWay([&response_received](auto response) { response_received = true; });
TwoWayServerResult<16> received(server);
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
std::array<uint8_t, 12> expected{
0x02, 0x00, 0x80, 0x01, //
0x9d, 0x60, 0x95, 0x03, 0x7a, 0x51, 0x33, 0x1f, //
};
EXPECT_EQ(received.buf_excluding_txid(), expected);
EXPECT_NE(received.buf_txid(), zero_txid);
received.reply<32>(server, {
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x01, //
0x9d, 0x60, 0x95, 0x03, 0x7a, 0x51, 0x33, 0x1f, //
// Result union with success envelope to satisfy client side:
// ordinal ---------------------------------|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
// inline value -----| nhandles | flags ---|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, //
});
EXPECT_EQ(received.reply_status, ZX_OK);
loop().RunUntilIdle();
EXPECT_TRUE(response_received);
}
TEST_F(UnknownInteractions, SendStrictEvent) {
auto client = TakeClientChannel();
UnknownInteractionsImpl impl;
fidl::Binding<test::UnknownInteractionsProtocol> binding(&impl);
auto& event_sender = binding.events();
binding.set_error_handler(
[](zx_status_t status) { ADD_FAILURE("Got an error status: %d", status); });
binding.Bind(TakeServerEnd());
event_sender.StrictEvent();
ReadResult<16> received(client);
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
std::array<uint8_t, 16> expected{
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, //
0x38, 0x27, 0xa3, 0x91, 0x98, 0x41, 0x4b, 0x58, //
};
EXPECT_EQ(received.buf, expected);
}
TEST_F(UnknownInteractions, SendFlexibleEvent) {
auto client = TakeClientChannel();
UnknownInteractionsImpl impl;
fidl::Binding<test::UnknownInteractionsProtocol> binding(&impl);
auto& event_sender = binding.events();
binding.set_error_handler(
[](zx_status_t status) { ADD_FAILURE("Got an error status: %d", status); });
binding.Bind(TakeServerEnd());
event_sender.FlexibleEvent();
ReadResult<16> received(client);
EXPECT_EQ(received.status, ZX_OK);
EXPECT_EQ(received.num_bytes, 16u);
EXPECT_EQ(received.num_handles, 0u);
std::array<uint8_t, 16> expected{
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x01, //
0x6c, 0x2c, 0x80, 0x0b, 0x8e, 0x1a, 0x7a, 0x31, //
};
EXPECT_EQ(received.buf, expected);
}
} // namespace