blob: 5dbab1d5d77a64907fd8058dc0615ba7b9382c4f [file] [log] [blame]
// Copyright 2016 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 "echo.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zircon/syscalls.h>
#include <unittest/unittest.h>
#include "message.h"
#include "struct.h"
bool wait_for_readable(zx_handle_t handle) {
unittest_printf("waiting for handle %u to be readable (or closed)\n", handle);
// Wait for |handle| to become readable or closed.
zx_signals_t signals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
zx_signals_t pending;
zx_status_t wait_status = zx_object_wait_one(handle, signals, ZX_TIME_INFINITE,
&pending);
if (wait_status != ZX_OK) {
return false;
}
if (!(pending & ZX_CHANNEL_READABLE)) {
return false;
}
return true;
}
bool serve_echo_request(zx_handle_t handle) {
ASSERT_TRUE(wait_for_readable(handle), "handle not readable");
// Try to read a message from |in_handle|.
// First, figure out size.
uint32_t in_msg_size = 0u;
zx_status_t read_status = zx_channel_read(handle, 0u, NULL, NULL, 0, 0, &in_msg_size, NULL);
ASSERT_NE(read_status, ZX_ERR_NO_MEMORY, "unexpected sizing read status");
unittest_printf("reading message of size %u\n", in_msg_size);
void* in_msg_buf = calloc(in_msg_size, 1u);
read_status = zx_channel_read(handle, 0u, in_msg_buf, NULL, in_msg_size, 0, &in_msg_size, NULL);
ASSERT_EQ(read_status, ZX_OK, "read failed with status");
// Try to parse message data.
ASSERT_TRUE(mojo_validate_struct_header(in_msg_buf, in_msg_size),
"validation failed on read message");
mojo_struct_header_t* in_struct_header = (mojo_struct_header_t*)in_msg_buf;
ASSERT_EQ(in_struct_header->version, 1u, "Header verison incorrect");
mojo_message_header_with_request_id_t* in_msg_header =
(mojo_message_header_with_request_id_t*)in_struct_header;
ASSERT_EQ(in_msg_header->message_header.name, 0u, "Name should be null");
ASSERT_EQ(in_msg_header->message_header.flags,
(uint32_t)MOJO_MESSAGE_HEADER_FLAGS_EXPECTS_RESPONSE,
"Invalid header flag");
uint64_t request_id = in_msg_header->request_id;
void* in_payload = in_msg_header + 1u;
uint32_t in_string_header_num_bytes = *(uint32_t*)in_payload;
uint32_t in_string_header_num_elems = *((uint32_t*)in_payload + 1u);
void* in_string_data = ((uint32_t*)in_payload) + 2u;
unittest_printf("got string: ");
for (uint32_t i = 0u; i < in_string_header_num_elems; ++i) {
unittest_printf("%c", ((char*)in_string_data)[i]);
}
unittest_printf("\n");
// TODO: Validate array header
// Incoming message seems fine, form an outgoing message and send it.
void* out_msg_buf = malloc(in_msg_size);
uint32_t out_msg_size = in_msg_size;
// Write header
mojo_message_header_with_request_id_t* out_msg_header =
(mojo_message_header_with_request_id_t*)out_msg_buf;
// Struct header
out_msg_header->message_header.struct_header.num_bytes =
sizeof(mojo_message_header_with_request_id_t);
out_msg_header->message_header.struct_header.version = 1u;
// Message header
out_msg_header->message_header.name = 0u;
out_msg_header->message_header.flags = MOJO_MESSAGE_HEADER_FLAGS_IS_RESPONSE;
out_msg_header->request_id = request_id;
uint32_t* out_string_header = (uint32_t*)out_msg_header + 1u;
*out_string_header = in_string_header_num_bytes;
*(out_string_header + 1u) = in_string_header_num_elems;
if (in_string_header_num_bytes != 0u) {
char* out_string_dest = (char*)(out_string_header + 2u);
memcpy(out_string_dest, (char*)(in_string_data), in_string_header_num_bytes);
}
free(in_msg_buf);
zx_status_t write_status =
zx_channel_write(handle, 0u, out_msg_buf, out_msg_size, NULL, 0u);
free(out_msg_buf);
ASSERT_EQ(write_status, ZX_OK, "Error while message writing");
unittest_printf("served request!\n\n");
return true;
}
bool echo_test(void) {
BEGIN_TEST;
zx_handle_t handles[2] = {0};
zx_status_t status = zx_channel_create(0, handles, handles + 1);
ASSERT_EQ(status, 0, "could not create channel");
unittest_printf("created channel with handle values %u and %u\n", handles[0], handles[1]);
for (int i = 0; i < 3; i++) {
unittest_printf("loop %d\n", i);
static const uint32_t buf[9] = {
24, // struct header, num_bytes
1, // struct header: version
0, // struct header: flags
1, // message header: name
0, 0, // message header: request id (8 bytes)
4, // array header: num bytes
4, // array header: num elems
0x42424143, // array contents: 'CABB'
};
zx_status_t status = zx_channel_write(handles[1], 0u, (void*)buf, sizeof(buf), NULL, 0u);
ASSERT_EQ(status, ZX_OK, "could not write echo request");
ASSERT_TRUE(serve_echo_request(handles[0]), "serve_echo_request failed");
}
zx_handle_close(handles[1]);
EXPECT_FALSE(wait_for_readable(handles[0]), "handle should not readable");
zx_handle_close(handles[0]);
END_TEST;
}
BEGIN_TEST_CASE(echo_tests)
RUN_TEST(echo_test)
END_TEST_CASE(echo_tests)