|
|
#include "../../unity/unity.h" |
|
|
#include <unistd.h> |
|
|
#include <fcntl.h> |
|
|
#include <string.h> |
|
|
#include <stdlib.h> |
|
|
#include <stdio.h> |
|
|
#include <errno.h> |
|
|
#include <sys/wait.h> |
|
|
#include <signal.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int saved_stdin_fd = -1; |
|
|
|
|
|
|
|
|
static int make_pipe_and_set_stdin(void) { |
|
|
int fds[2]; |
|
|
if (pipe(fds) != 0) { |
|
|
TEST_FAIL_MESSAGE("pipe() failed"); |
|
|
} |
|
|
|
|
|
if (dup2(fds[0], STDIN_FILENO) < 0) { |
|
|
close(fds[0]); |
|
|
close(fds[1]); |
|
|
TEST_FAIL_MESSAGE("dup2() failed to set STDIN"); |
|
|
} |
|
|
close(fds[0]); |
|
|
return fds[1]; |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
saved_stdin_fd = dup(STDIN_FILENO); |
|
|
if (saved_stdin_fd < 0) { |
|
|
TEST_FAIL_MESSAGE("dup(STDIN_FILENO) failed in setUp"); |
|
|
} |
|
|
|
|
|
|
|
|
have_read_eof = false; |
|
|
|
|
|
|
|
|
sigemptyset(&caught_signals); |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
if (saved_stdin_fd >= 0) { |
|
|
if (dup2(saved_stdin_fd, STDIN_FILENO) < 0) { |
|
|
TEST_FAIL_MESSAGE("dup2() failed restoring STDIN"); |
|
|
} |
|
|
close(saved_stdin_fd); |
|
|
saved_stdin_fd = -1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void test_read_input_zero_max_does_not_change_eof(void) { |
|
|
have_read_eof = false; |
|
|
char buf[4] = {0}; |
|
|
idx_t n = read_input(buf, 0); |
|
|
TEST_ASSERT_EQUAL_INT(0, (int)n); |
|
|
TEST_ASSERT_FALSE(have_read_eof); |
|
|
|
|
|
have_read_eof = true; |
|
|
n = read_input(buf, 0); |
|
|
TEST_ASSERT_EQUAL_INT(0, (int)n); |
|
|
TEST_ASSERT_TRUE(have_read_eof); |
|
|
} |
|
|
|
|
|
|
|
|
void test_read_input_reads_available_bytes(void) { |
|
|
int w = make_pipe_and_set_stdin(); |
|
|
const char *msg = "hello world"; |
|
|
size_t len = strlen(msg); |
|
|
ssize_t wr = write(w, msg, len); |
|
|
close(w); |
|
|
TEST_ASSERT_EQUAL_INT((int)len, (int)wr); |
|
|
|
|
|
char buf[64]; |
|
|
memset(buf, 0, sizeof buf); |
|
|
idx_t n = read_input(buf, (idx_t)sizeof buf); |
|
|
TEST_ASSERT_EQUAL_INT((int)len, (int)n); |
|
|
TEST_ASSERT_EQUAL_MEMORY(msg, buf, len); |
|
|
TEST_ASSERT_FALSE(have_read_eof); |
|
|
} |
|
|
|
|
|
|
|
|
void test_read_input_sets_eof_when_no_data(void) { |
|
|
int w = make_pipe_and_set_stdin(); |
|
|
|
|
|
close(w); |
|
|
|
|
|
char buf[8]; |
|
|
idx_t n1 = read_input(buf, (idx_t)sizeof buf); |
|
|
TEST_ASSERT_EQUAL_INT(0, (int)n1); |
|
|
TEST_ASSERT_TRUE(have_read_eof); |
|
|
|
|
|
|
|
|
idx_t n2 = read_input(buf, (idx_t)sizeof buf); |
|
|
TEST_ASSERT_EQUAL_INT(0, (int)n2); |
|
|
TEST_ASSERT_TRUE(have_read_eof); |
|
|
} |
|
|
|
|
|
|
|
|
void test_read_input_respects_max_bytes_limit(void) { |
|
|
int w = make_pipe_and_set_stdin(); |
|
|
const char *msg = "abcdef"; |
|
|
ssize_t wr = write(w, msg, 6); |
|
|
close(w); |
|
|
TEST_ASSERT_EQUAL_INT(6, (int)wr); |
|
|
|
|
|
char buf[4] = {0}; |
|
|
idx_t n = read_input(buf, 3); |
|
|
TEST_ASSERT_EQUAL_INT(3, (int)n); |
|
|
TEST_ASSERT_EQUAL_MEMORY("abc", buf, 3); |
|
|
TEST_ASSERT_FALSE(have_read_eof); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_read_input_on_bad_fd_exits_failure(void) { |
|
|
fflush(stdout); |
|
|
fflush(stderr); |
|
|
pid_t pid = fork(); |
|
|
TEST_ASSERT_TRUE_MESSAGE(pid >= 0, "fork() failed"); |
|
|
|
|
|
if (pid == 0) { |
|
|
|
|
|
sigemptyset(&caught_signals); |
|
|
close(STDIN_FILENO); |
|
|
char buf[1]; |
|
|
|
|
|
(void)read_input(buf, 1); |
|
|
|
|
|
|
|
|
_exit(0); |
|
|
} else { |
|
|
int status = 0; |
|
|
pid_t wp = waitpid(pid, &status, 0); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(pid, wp, "waitpid failed"); |
|
|
TEST_ASSERT_TRUE(WIFEXITED(status)); |
|
|
TEST_ASSERT_EQUAL_INT(EXIT_FAILURE, WEXITSTATUS(status)); |
|
|
} |
|
|
} |
|
|
|
|
|
int main(void) { |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_read_input_zero_max_does_not_change_eof); |
|
|
RUN_TEST(test_read_input_reads_available_bytes); |
|
|
RUN_TEST(test_read_input_sets_eof_when_no_data); |
|
|
RUN_TEST(test_read_input_respects_max_bytes_limit); |
|
|
RUN_TEST(test_read_input_on_bad_fd_exits_failure); |
|
|
return UNITY_END(); |
|
|
} |