1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "echo.h" 6 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10 11#include <zircon/syscalls.h> 12#include <unittest/unittest.h> 13 14#include "message.h" 15#include "struct.h" 16 17bool wait_for_readable(zx_handle_t handle) { 18 unittest_printf("waiting for handle %u to be readable (or closed)\n", handle); 19 // Wait for |handle| to become readable or closed. 20 zx_signals_t signals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED; 21 zx_signals_t pending; 22 zx_status_t wait_status = zx_object_wait_one(handle, signals, ZX_TIME_INFINITE, 23 &pending); 24 if (wait_status != ZX_OK) { 25 return false; 26 } 27 if (!(pending & ZX_CHANNEL_READABLE)) { 28 return false; 29 } 30 return true; 31} 32 33bool serve_echo_request(zx_handle_t handle) { 34 ASSERT_TRUE(wait_for_readable(handle), "handle not readable"); 35 36 // Try to read a message from |in_handle|. 37 // First, figure out size. 38 uint32_t in_msg_size = 0u; 39 zx_status_t read_status = zx_channel_read(handle, 0u, NULL, NULL, 0, 0, &in_msg_size, NULL); 40 ASSERT_NE(read_status, ZX_ERR_NO_MEMORY, "unexpected sizing read status"); 41 42 unittest_printf("reading message of size %u\n", in_msg_size); 43 void* in_msg_buf = calloc(in_msg_size, 1u); 44 read_status = zx_channel_read(handle, 0u, in_msg_buf, NULL, in_msg_size, 0, &in_msg_size, NULL); 45 ASSERT_EQ(read_status, ZX_OK, "read failed with status"); 46 47 // Try to parse message data. 48 ASSERT_TRUE(mojo_validate_struct_header(in_msg_buf, in_msg_size), 49 "validation failed on read message"); 50 51 mojo_struct_header_t* in_struct_header = (mojo_struct_header_t*)in_msg_buf; 52 ASSERT_EQ(in_struct_header->version, 1u, "Header verison incorrect"); 53 54 mojo_message_header_with_request_id_t* in_msg_header = 55 (mojo_message_header_with_request_id_t*)in_struct_header; 56 57 ASSERT_EQ(in_msg_header->message_header.name, 0u, "Name should be null"); 58 59 ASSERT_EQ(in_msg_header->message_header.flags, 60 (uint32_t)MOJO_MESSAGE_HEADER_FLAGS_EXPECTS_RESPONSE, 61 "Invalid header flag"); 62 63 uint64_t request_id = in_msg_header->request_id; 64 65 void* in_payload = in_msg_header + 1u; 66 67 uint32_t in_string_header_num_bytes = *(uint32_t*)in_payload; 68 uint32_t in_string_header_num_elems = *((uint32_t*)in_payload + 1u); 69 void* in_string_data = ((uint32_t*)in_payload) + 2u; 70 unittest_printf("got string: "); 71 for (uint32_t i = 0u; i < in_string_header_num_elems; ++i) { 72 unittest_printf("%c", ((char*)in_string_data)[i]); 73 } 74 unittest_printf("\n"); 75 76 // TODO: Validate array header 77 78 // Incoming message seems fine, form an outgoing message and send it. 79 80 void* out_msg_buf = malloc(in_msg_size); 81 uint32_t out_msg_size = in_msg_size; 82 83 // Write header 84 mojo_message_header_with_request_id_t* out_msg_header = 85 (mojo_message_header_with_request_id_t*)out_msg_buf; 86 87 // Struct header 88 out_msg_header->message_header.struct_header.num_bytes = 89 sizeof(mojo_message_header_with_request_id_t); 90 out_msg_header->message_header.struct_header.version = 1u; 91 92 // Message header 93 out_msg_header->message_header.name = 0u; 94 out_msg_header->message_header.flags = MOJO_MESSAGE_HEADER_FLAGS_IS_RESPONSE; 95 out_msg_header->request_id = request_id; 96 97 uint32_t* out_string_header = (uint32_t*)out_msg_header + 1u; 98 *out_string_header = in_string_header_num_bytes; 99 *(out_string_header + 1u) = in_string_header_num_elems; 100 101 if (in_string_header_num_bytes != 0u) { 102 char* out_string_dest = (char*)(out_string_header + 2u); 103 memcpy(out_string_dest, (char*)(in_string_data), in_string_header_num_bytes); 104 } 105 free(in_msg_buf); 106 107 zx_status_t write_status = 108 zx_channel_write(handle, 0u, out_msg_buf, out_msg_size, NULL, 0u); 109 free(out_msg_buf); 110 111 ASSERT_EQ(write_status, ZX_OK, "Error while message writing"); 112 113 unittest_printf("served request!\n\n"); 114 return true; 115} 116 117bool echo_test(void) { 118 BEGIN_TEST; 119 zx_handle_t handles[2] = {0}; 120 zx_status_t status = zx_channel_create(0, handles, handles + 1); 121 ASSERT_EQ(status, 0, "could not create channel"); 122 unittest_printf("created channel with handle values %u and %u\n", handles[0], handles[1]); 123 for (int i = 0; i < 3; i++) { 124 unittest_printf("loop %d\n", i); 125 static const uint32_t buf[9] = { 126 24, // struct header, num_bytes 127 1, // struct header: version 128 0, // struct header: flags 129 1, // message header: name 130 0, 0, // message header: request id (8 bytes) 131 4, // array header: num bytes 132 4, // array header: num elems 133 0x42424143, // array contents: 'CABB' 134 }; 135 zx_status_t status = zx_channel_write(handles[1], 0u, (void*)buf, sizeof(buf), NULL, 0u); 136 ASSERT_EQ(status, ZX_OK, "could not write echo request"); 137 138 ASSERT_TRUE(serve_echo_request(handles[0]), "serve_echo_request failed"); 139 } 140 zx_handle_close(handles[1]); 141 EXPECT_FALSE(wait_for_readable(handles[0]), "handle should not readable"); 142 zx_handle_close(handles[0]); 143 END_TEST; 144} 145 146BEGIN_TEST_CASE(echo_tests) 147RUN_TEST(echo_test) 148END_TEST_CASE(echo_tests) 149 150 151#ifndef BUILD_COMBINED_TESTS 152int main(int argc, char** argv) { 153 return unittest_run_all_tests(argc, argv) ? 0 : -1; 154} 155#endif 156