File size: 4,949 Bytes
78d2150 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
#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>
/* Access to static globals/functions is available since this file is included
into the same translation unit after their definitions. */
static int saved_stdin_fd = -1;
/* Helper: set STDIN to the read end of a new pipe; return write end */
static int make_pipe_and_set_stdin(void) {
int fds[2];
if (pipe(fds) != 0) {
TEST_FAIL_MESSAGE("pipe() failed");
}
/* fds[0] is read end, fds[1] is write end */
if (dup2(fds[0], STDIN_FILENO) < 0) {
close(fds[0]);
close(fds[1]);
TEST_FAIL_MESSAGE("dup2() failed to set STDIN");
}
close(fds[0]); /* read end duplicated to STDIN; close original */
return fds[1]; /* return write end to caller */
}
void setUp(void) {
/* Save current STDIN so we can restore it after each test */
saved_stdin_fd = dup(STDIN_FILENO);
if (saved_stdin_fd < 0) {
TEST_FAIL_MESSAGE("dup(STDIN_FILENO) failed in setUp");
}
/* Reset EOF flag before each test */
have_read_eof = false;
/* Ensure caught_signals is an empty set to keep cleanup() safe if triggered */
sigemptyset(&caught_signals);
}
void tearDown(void) {
/* Restore STDIN */
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;
}
}
/* Test: when max_n_bytes == 0, returns 0 and does not touch have_read_eof */
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);
}
/* Test: reads available bytes and does not set 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);
}
/* Test: EOF handling sets have_read_eof and returns 0; remains true on subsequent calls */
void test_read_input_sets_eof_when_no_data(void) {
int w = make_pipe_and_set_stdin();
/* No write; close writer to produce EOF */
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);
/* Subsequent call should also return 0 and keep EOF true */
idx_t n2 = read_input(buf, (idx_t)sizeof buf);
TEST_ASSERT_EQUAL_INT(0, (int)n2);
TEST_ASSERT_TRUE(have_read_eof);
}
/* Test: bounded read respects max_n_bytes less than available data */
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);
}
/* Test: error path when STDIN is invalid causes process to exit with EXIT_FAILURE.
Use fork to isolate the exit from the test runner. */
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) {
/* Child: invalidate STDIN and call read_input */
sigemptyset(&caught_signals); /* ensure cleanup() uses a known signal set */
close(STDIN_FILENO);
char buf[1];
/* This should print an error and exit(EXIT_FAILURE) via cleanup_fatal */
(void)read_input(buf, 1);
/* If we get here, the function did not exit as expected */
_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();
} |