blob: dc4f832b038100c5f514fcfd456ec98b2aeddcdc [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 <string.h>
#include <threads.h>
#include <zircon/fidl.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <fidl/test/echo/c/fidl.h>
#include <fidl/test/ctransformer/c/fidl.h>
#include <lib/async-loop/default.h>
#include <lib/async-loop/loop.h>
#include <lib/fidl-async/bind.h>
#include <lib/fidl/coding.h>
#include <lib/fidl/runtime_flag.h>
#include <lib/fidl/txn_header.h>
#include <unittest/unittest.h>
// V1 version of |example/Sandwich4|.
// This excerpt of bytes is taken directly from zircon/system/utest/fidl/transformer_tests.cc.
const uint8_t sandwich4_case1_v1[] = {
0x01, 0x02, 0x03, 0x04, // Sandwich4.before
0x00, 0x00, 0x00, 0x00, // Sandwich4.before (padding)
0x04, 0x00, 0x00, 0x00, // UnionSize36Alignment4.tag, i.e. Sandwich4.the_union
0x00, 0x00, 0x00, 0x00, // UnionSize36Alignment4.tag (padding)
0x20, 0x00, 0x00, 0x00, // UnionSize36Alignment4.env.num_bytes
0x00, 0x00, 0x00, 0x00, // UnionSize36Alignment4.env.num_handle
0xff, 0xff, 0xff, 0xff, // UnionSize36Alignment4.env.presence
0xff, 0xff, 0xff, 0xff, // UnionSize36Alignment4.env.presence [cont.]
0x05, 0x06, 0x07, 0x08, // Sandwich4.after
0x00, 0x00, 0x00, 0x00, // Sandwich4.after (padding)
0xa0, 0xa1, 0xa2, 0xa3, // UnionSize36Alignment4.data, i.e. Sandwich4.the_union.data
0xa4, 0xa5, 0xa6, 0xa7, // UnionSize36Alignment4.data [cont.]
0xa8, 0xa9, 0xaa, 0xab, // UnionSize36Alignment4.data [cont.]
0xac, 0xad, 0xae, 0xaf, // UnionSize36Alignment4.data [cont.]
0xb0, 0xb1, 0xb2, 0xb3, // UnionSize36Alignment4.data [cont.]
0xb4, 0xb5, 0xb6, 0xb7, // UnionSize36Alignment4.data [cont.]
0xb8, 0xb9, 0xba, 0xbb, // UnionSize36Alignment4.data [cont.]
0xbc, 0xbd, 0xbe, 0xbf, // UnionSize36Alignment4.data [cont.]
};
static int xunion_to_union_test_server(void* ctx) {
zx_handle_t server = *(zx_handle_t*)ctx;
zx_status_t status = ZX_OK;
while (status == ZX_OK) {
zx_signals_t observed;
status = zx_object_wait_one(server, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
ZX_TIME_INFINITE, &observed);
if ((observed & ZX_CHANNEL_READABLE) == 0) {
break;
}
ASSERT_EQ(ZX_OK, status, "");
char msg[1024];
zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
uint32_t actual_bytes = 0u;
uint32_t actual_handles = 0u;
status = zx_channel_read(server, 0, msg, handles, sizeof(msg), ZX_CHANNEL_MAX_MSG_HANDLES,
&actual_bytes, &actual_handles);
ASSERT_EQ(ZX_OK, status, "");
ASSERT_GE(actual_bytes, sizeof(fidl_message_header_t), "");
ASSERT_EQ(actual_handles, 0u, "");
fidl_message_header_t* req = (fidl_message_header_t*)msg;
// Respond with v1 version of |example/Sandwich4|.
uint8_t response[sizeof(fidl_message_header_t) + sizeof(sandwich4_case1_v1)] = {};
fidl_message_header_t* response_hdr = (fidl_message_header_t*)response;
fidl_init_txn_header(response_hdr, req->txid, req->ordinal);
// Set the flag indicating unions are encoded as xunions.
response_hdr->flags[0] |= FIDL_TXN_HEADER_UNION_FROM_XUNION_FLAG;
memcpy(&response[sizeof(fidl_message_header_t)], sandwich4_case1_v1,
sizeof(sandwich4_case1_v1));
status = zx_channel_write(server, 0, response, sizeof(response), NULL, 0);
ASSERT_EQ(ZX_OK, status, "");
}
zx_handle_close(server);
return 0;
}
static bool xunion_to_union(void) {
BEGIN_TEST;
zx_handle_t client, server;
zx_status_t status = zx_channel_create(0, &client, &server);
ASSERT_EQ(ZX_OK, status, "");
thrd_t thread;
int rv = thrd_create(&thread, xunion_to_union_test_server, &server);
ASSERT_EQ(thrd_success, rv, "");
// Server is responding in v1 wire-format, but we should be able to receive it
// as the old wire-format.
example_Sandwich4 sandwich4;
status = fidl_test_ctransformer_TestReceiveUnion(client, &sandwich4);
EXPECT_EQ(ZX_OK, status, "");
EXPECT_EQ(0x04030201, sandwich4.before, "");
EXPECT_EQ(0x08070605, sandwich4.after, "");
EXPECT_EQ(example_UnionSize36Alignment4Tag_variant, sandwich4.the_union.tag, "");
for (uint8_t i = 0; i < sizeof(sandwich4.the_union.variant); i++) {
EXPECT_EQ(sandwich4.the_union.variant[i], 0xa0 + i, "");
}
status = zx_handle_close(client);
ASSERT_EQ(ZX_OK, status, "");
int result = 0;
rv = thrd_join(thread, &result);
ASSERT_EQ(thrd_success, rv, "");
END_TEST;
}
zx_status_t union_to_xunion_receive_union(void* ctx, fidl_txn_t* txn) {
example_Sandwich4 sandwich = {};
sandwich.before = 0x04030201;
sandwich.after = 0x08070605;
sandwich.the_union.tag = example_UnionSize36Alignment4Tag_variant;
// Initialize the array in the union per the example on-the-wire content.
for (uint8_t i = 0; i < sizeof(sandwich.the_union.variant); i++) {
sandwich.the_union.variant[i] = 0xa0 + i;
}
return fidl_test_ctransformer_TestReceiveUnion_reply(txn, &sandwich);
}
static const fidl_test_ctransformer_Test_ops_t union_to_xunion_server_ops = {
.ReceiveUnion = union_to_xunion_receive_union
};
static bool union_to_xunion(void) {
BEGIN_TEST;
// Note: Take care to reset the "encode union as xunion" global flag on all return paths.
#define RETURN_IF_FAILURE(expr) \
do { \
zx_status_t _status = (expr); \
EXPECT_EQ(ZX_OK, _status, ""); \
if (_status != ZX_OK) { \
fidl_global_set_should_write_union_as_xunion(old_flag); \
if (loop) { \
async_loop_destroy(loop); \
} \
return false; \
} \
} while (0);
const bool old_flag = fidl_global_get_should_write_union_as_xunion();
async_loop_t* loop = NULL;
zx_handle_t client, server;
RETURN_IF_FAILURE(zx_channel_create(0, &client, &server));
RETURN_IF_FAILURE(async_loop_create(&kAsyncLoopConfigNoAttachToCurrentThread, &loop));
RETURN_IF_FAILURE(async_loop_start_thread(loop, "union-to-xunion-test-dispatcher", NULL));
async_dispatcher_t* dispatcher = async_loop_get_dispatcher(loop);
RETURN_IF_FAILURE(fidl_bind(dispatcher, server,
(fidl_dispatch_t*)fidl_test_ctransformer_Test_dispatch, NULL,
&union_to_xunion_server_ops));
// Send a request to the server and manually read out the response.
// We should get the v1 wire-format because the server is configured to write xunions via the
// global flag.
{
fidl_global_set_should_write_union_as_xunion(true);
FIDL_ALIGNDECL fidl_test_ctransformer_TestReceiveUnionRequest request = {};
zx_handle_t handles[1] = {};
fidl_init_txn_header(&request.hdr, /* txid */ 1,
fidl_test_ctransformer_TestReceiveUnionOrdinal);
RETURN_IF_FAILURE(zx_channel_write(client, 0, &request, sizeof(request), handles, 0));
FIDL_ALIGNDECL uint8_t response_buf[512] = {};
zx_signals_t observed;
RETURN_IF_FAILURE(zx_object_wait_one(client, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
ZX_TIME_INFINITE, &observed));
if ((observed & ZX_CHANNEL_READABLE) == 0) {
RETURN_IF_FAILURE(ZX_ERR_INTERNAL);
}
uint32_t actual_bytes = 0;
uint32_t actual_handles = 0;
RETURN_IF_FAILURE(zx_channel_read(client, 0, response_buf, handles, sizeof(response_buf), 0,
&actual_bytes, &actual_handles));
// Compare against golden bytes
EXPECT_EQ(actual_handles, 0, "");
EXPECT_EQ(actual_bytes, sizeof(fidl_message_header_t) + sizeof(sandwich4_case1_v1), "");
for (uint32_t i = 0; i < sizeof(sandwich4_case1_v1); i++) {
EXPECT_EQ(response_buf[sizeof(fidl_message_header_t) + i], sandwich4_case1_v1[i], "");
}
}
// Send a request to the server and manually read out the response.
// We should get the old wire-format because the server is configured to no longer write xunions.
{
fidl_global_set_should_write_union_as_xunion(false);
FIDL_ALIGNDECL fidl_test_ctransformer_TestReceiveUnionRequest request = {};
zx_handle_t handles[1] = {};
fidl_init_txn_header(&request.hdr, /* txid */ 2,
fidl_test_ctransformer_TestReceiveUnionOrdinal);
RETURN_IF_FAILURE(zx_channel_write(client, 0, &request, sizeof(request), handles, 0));
FIDL_ALIGNDECL uint8_t response_buf[512] = {};
zx_signals_t observed;
RETURN_IF_FAILURE(zx_object_wait_one(client, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
ZX_TIME_INFINITE, &observed));
if ((observed & ZX_CHANNEL_READABLE) == 0) {
RETURN_IF_FAILURE(ZX_ERR_INTERNAL);
}
uint32_t actual_bytes = 0;
uint32_t actual_handles = 0;
RETURN_IF_FAILURE(zx_channel_read(client, 0, response_buf, handles, sizeof(response_buf), 0,
&actual_bytes, &actual_handles));
// Attempt to decode the union normally, using coding table for the old wire-format
RETURN_IF_FAILURE(fidl_decode(&fidl_test_ctransformer_TestReceiveUnionResponseTable,
response_buf, actual_bytes, handles, actual_handles, NULL));
example_Sandwich4 sandwich4 =
((fidl_test_ctransformer_TestReceiveUnionResponse*)response_buf)->sandwich4;
EXPECT_EQ(example_UnionSize36Alignment4Tag_variant, sandwich4.the_union.tag, "");
EXPECT_EQ(0x04030201, sandwich4.before, "");
EXPECT_EQ(0x08070605, sandwich4.after, "");
for (uint8_t i = 0; i < sizeof(sandwich4.the_union.variant); i++) {
EXPECT_EQ(sandwich4.the_union.variant[i], 0xa0 + i, "");
}
}
RETURN_IF_FAILURE(zx_handle_close(client));
async_loop_destroy(loop);
fidl_global_set_should_write_union_as_xunion(old_flag);
END_TEST;
}
BEGIN_TEST_CASE(c_transformer_smoke_tests)
RUN_NAMED_TEST("Test xunion -> union transformer integration", xunion_to_union)
RUN_NAMED_TEST("Test union -> xunion transformer integration", union_to_xunion)
END_TEST_CASE(c_transformer_smoke_tests);