blob: e3b254c3897b9630faf3ddbb99f090182fa3ae80 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <CUnit/CUnit.h>
#include <guacamole/error.h>
#include <guacamole/parser.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <stdlib.h>
#include <unistd.h>
/**
* Test string which contains exactly four Unicode characters encoded in UTF-8.
* This particular test string uses several characters which encode to multiple
* bytes in UTF-8.
*/
#define UTF8_4 "\xe7\x8a\xac\xf0\x90\xac\x80z\xc3\xa1"
/**
* Writes a series of Guacamole instructions as raw bytes to the given file
* descriptor. The instructions written correspond to the instructions verified
* by read_expected_instructions(). The given file descriptor is automatically
* closed as a result of calling this function.
*
* @param fd
* The file descriptor to write instructions to.
*/
static void write_instructions(int fd) {
char test_string[] = "4.test,6.a" UTF8_4 "b,"
"5.12345,10.a" UTF8_4 UTF8_4 "c;"
"5.test2,10.hellohello,15.worldworldworld;";
char* current = test_string;
int remaining = sizeof(test_string) - 1;
/* Write all bytes in test string */
while (remaining > 0) {
/* Bail out immediately if write fails (test will fail in parent
* process due to failure to read) */
int written = write(fd, current, remaining);
if (written <= 0)
break;
current += written;
remaining -= written;
}
/* Done writing */
close(fd);
}
/**
* Reads and parses instructions from the given file descriptor using a
* guac_socket and guac_parser, verfying that those instructions match the
* series of Guacamole instructions expected to be written by
* write_instructions(). The given file descriptor is automatically closed as a
* result of calling this function.
*
* @param fd
* The file descriptor to read data from.
*/
static void read_expected_instructions(int fd) {
/* Open guac socket */
guac_socket* socket = guac_socket_open(fd);
CU_ASSERT_PTR_NOT_NULL_FATAL(socket);
/* Allocate parser */
guac_parser* parser = guac_parser_alloc();
CU_ASSERT_PTR_NOT_NULL_FATAL(parser);
/* Read and validate first instruction */
CU_ASSERT_EQUAL_FATAL(guac_parser_read(parser, socket, 1000000), 0);
CU_ASSERT_STRING_EQUAL(parser->opcode, "test");
CU_ASSERT_EQUAL_FATAL(parser->argc, 3);
CU_ASSERT_STRING_EQUAL(parser->argv[0], "a" UTF8_4 "b");
CU_ASSERT_STRING_EQUAL(parser->argv[1], "12345");
CU_ASSERT_STRING_EQUAL(parser->argv[2], "a" UTF8_4 UTF8_4 "c");
/* Read and validate second instruction */
CU_ASSERT_EQUAL_FATAL(guac_parser_read(parser, socket, 1000000), 0);
CU_ASSERT_STRING_EQUAL(parser->opcode, "test2");
CU_ASSERT_EQUAL_FATAL(parser->argc, 2);
CU_ASSERT_STRING_EQUAL(parser->argv[0], "hellohello");
CU_ASSERT_STRING_EQUAL(parser->argv[1], "worldworldworld");
/* Done */
guac_parser_free(parser);
guac_socket_free(socket);
}
/**
* Tests that guac_parser_read() correctly reads and parses instructions
* received over a guac_socket. A child process is forked to write a series of
* instructions which are read and verified by the parent process.
*/
void test_parser__read() {
int fd[2];
/* Create pipe */
CU_ASSERT_EQUAL_FATAL(pipe(fd), 0);
int read_fd = fd[0];
int write_fd = fd[1];
/* Fork into writer process (child) and reader process (parent) */
int childpid;
CU_ASSERT_NOT_EQUAL_FATAL((childpid = fork()), -1);
/* Attempt to write a series of instructions within the child process */
if (childpid == 0) {
close(read_fd);
write_instructions(write_fd);
exit(0);
}
/* Read and verify the expected instructions within the parent process */
close(write_fd);
read_expected_instructions(read_fd);
}