read.c (4665B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include <CUnit/CUnit.h> 21#include <guacamole/error.h> 22#include <guacamole/parser.h> 23#include <guacamole/protocol.h> 24#include <guacamole/socket.h> 25 26#include <stdlib.h> 27#include <unistd.h> 28 29/** 30 * Test string which contains exactly four Unicode characters encoded in UTF-8. 31 * This particular test string uses several characters which encode to multiple 32 * bytes in UTF-8. 33 */ 34#define UTF8_4 "\xe7\x8a\xac\xf0\x90\xac\x80z\xc3\xa1" 35 36/** 37 * Writes a series of Guacamole instructions as raw bytes to the given file 38 * descriptor. The instructions written correspond to the instructions verified 39 * by read_expected_instructions(). The given file descriptor is automatically 40 * closed as a result of calling this function. 41 * 42 * @param fd 43 * The file descriptor to write instructions to. 44 */ 45static void write_instructions(int fd) { 46 47 char test_string[] = "4.test,6.a" UTF8_4 "b," 48 "5.12345,10.a" UTF8_4 UTF8_4 "c;" 49 "5.test2,10.hellohello,15.worldworldworld;"; 50 51 char* current = test_string; 52 int remaining = sizeof(test_string) - 1; 53 54 /* Write all bytes in test string */ 55 while (remaining > 0) { 56 57 /* Bail out immediately if write fails (test will fail in parent 58 * process due to failure to read) */ 59 int written = write(fd, current, remaining); 60 if (written <= 0) 61 break; 62 63 current += written; 64 remaining -= written; 65 66 } 67 68 /* Done writing */ 69 close(fd); 70 71} 72 73/** 74 * Reads and parses instructions from the given file descriptor using a 75 * guac_socket and guac_parser, verifying that those instructions match the 76 * series of Guacamole instructions expected to be written by 77 * write_instructions(). The given file descriptor is automatically closed as a 78 * result of calling this function. 79 * 80 * @param fd 81 * The file descriptor to read data from. 82 */ 83static void read_expected_instructions(int fd) { 84 85 /* Open guac socket */ 86 guac_socket* socket = guac_socket_open(fd); 87 CU_ASSERT_PTR_NOT_NULL_FATAL(socket); 88 89 /* Allocate parser */ 90 guac_parser* parser = guac_parser_alloc(); 91 CU_ASSERT_PTR_NOT_NULL_FATAL(parser); 92 93 /* Read and validate first instruction */ 94 CU_ASSERT_EQUAL_FATAL(guac_parser_read(parser, socket, 1000000), 0); 95 CU_ASSERT_STRING_EQUAL(parser->opcode, "test"); 96 CU_ASSERT_EQUAL_FATAL(parser->argc, 3); 97 CU_ASSERT_STRING_EQUAL(parser->argv[0], "a" UTF8_4 "b"); 98 CU_ASSERT_STRING_EQUAL(parser->argv[1], "12345"); 99 CU_ASSERT_STRING_EQUAL(parser->argv[2], "a" UTF8_4 UTF8_4 "c"); 100 101 /* Read and validate second instruction */ 102 CU_ASSERT_EQUAL_FATAL(guac_parser_read(parser, socket, 1000000), 0); 103 CU_ASSERT_STRING_EQUAL(parser->opcode, "test2"); 104 CU_ASSERT_EQUAL_FATAL(parser->argc, 2); 105 CU_ASSERT_STRING_EQUAL(parser->argv[0], "hellohello"); 106 CU_ASSERT_STRING_EQUAL(parser->argv[1], "worldworldworld"); 107 108 /* Done */ 109 guac_parser_free(parser); 110 guac_socket_free(socket); 111 112} 113 114/** 115 * Tests that guac_parser_read() correctly reads and parses instructions 116 * received over a guac_socket. A child process is forked to write a series of 117 * instructions which are read and verified by the parent process. 118 */ 119void test_parser__read() { 120 121 int fd[2]; 122 123 /* Create pipe */ 124 CU_ASSERT_EQUAL_FATAL(pipe(fd), 0); 125 126 int read_fd = fd[0]; 127 int write_fd = fd[1]; 128 129 /* Fork into writer process (child) and reader process (parent) */ 130 int childpid; 131 CU_ASSERT_NOT_EQUAL_FATAL((childpid = fork()), -1); 132 133 /* Attempt to write a series of instructions within the child process */ 134 if (childpid == 0) { 135 close(read_fd); 136 write_instructions(write_fd); 137 exit(0); 138 } 139 140 /* Read and verify the expected instructions within the parent process */ 141 close(write_fd); 142 read_expected_instructions(read_fd); 143 144} 145