File size: 5,121 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 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
#include "../../unity/unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
/* The test file is included into the od translation unit, so it has
access to the internal functions and globals declared above, such as:
- static bool read_char (int *c);
- static bool open_next_file (void);
- static bool check_and_close (int in_errno);
- static FILE *in_stream;
- static char const *const *file_list; */
/* Helper: create a temporary file with the specified content. Returns a
malloc-allocated string containing the path which the caller must free. */
static char *make_tempfile_with_content(const char *content)
{
char tmpl[] = "/tmp/od_read_char_test_XXXXXX";
int fd = mkstemp(tmpl);
TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "mkstemp failed");
if (content && content[0] != '\0') {
size_t len = strlen(content);
ssize_t wr = write(fd, content, len);
TEST_ASSERT_EQUAL_INT_MESSAGE((int)len, (int)wr, "write failed");
}
close(fd);
char *path = (char *)malloc(strlen(tmpl) + 1);
TEST_ASSERT_NOT_NULL(path);
strcpy(path, tmpl);
return path;
}
/* Helper: close stream if open and reset pointer. */
static void close_stream_if_open(void)
{
if (in_stream != NULL) {
/* Pass 0 so check_and_close checks ferror itself */
(void)check_and_close(0);
}
}
void setUp(void) {
/* Ensure a clean slate before each test */
close_stream_if_open();
/* Reset file_list to a benign state (no files). We use a static
local array here to avoid lifetime issues. */
static const char *empty_list[] = { NULL };
file_list = empty_list;
}
void tearDown(void) {
/* Close any stream that may still be open */
close_stream_if_open();
}
/* Test: basic single byte read from a file. */
void test_read_char_single_byte(void)
{
char *p = make_tempfile_with_content("Z");
const char *flist[] = { p, NULL };
file_list = flist;
/* Open first (and only) file via program helper to set globals consistently */
TEST_ASSERT_TRUE(open_next_file());
int ch = 0;
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT('Z', ch);
/* Cleanup */
close_stream_if_open();
unlink(p);
free(p);
}
/* Test: sequential reads across multiple bytes without hitting EOF in read_char. */
void test_read_char_three_bytes(void)
{
char *p = make_tempfile_with_content("ABC");
const char *flist[] = { p, NULL };
file_list = flist;
TEST_ASSERT_TRUE(open_next_file());
int ch = 0;
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT('A', ch);
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT('B', ch);
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT('C', ch);
/* Do not call a 4th time to avoid transitioning to EOF here. */
close_stream_if_open();
unlink(p);
free(p);
}
/* Test: handle EOF on an empty file with no next file. Should return true and set *c = EOF. */
void test_read_char_eof_no_next_file(void)
{
char *p = make_tempfile_with_content("");
const char *flist[] = { p, NULL };
file_list = flist;
TEST_ASSERT_TRUE(open_next_file());
int ch = 123; /* init to non-EOF */
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT(EOF, ch);
/* Subsequent call with in_stream now NULL should also yield EOF and true. */
ch = 456;
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT(EOF, ch);
close_stream_if_open();
unlink(p);
free(p);
}
/* Test: transition across two files: first has 'A', second has 'B'. */
void test_read_char_transition_to_next_file(void)
{
char *p1 = make_tempfile_with_content("A");
char *p2 = make_tempfile_with_content("B");
const char *flist[] = { p1, p2, NULL };
file_list = flist;
TEST_ASSERT_TRUE(open_next_file());
int ch = 0;
/* Read from first file */
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT('A', ch);
/* Next call should hit EOF on first, advance to second, and read 'B' */
ch = 0;
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT('B', ch);
/* Third call: both files exhausted -> EOF */
ch = 0;
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT(EOF, ch);
close_stream_if_open();
unlink(p1);
unlink(p2);
free(p1);
free(p2);
}
/* Test: when in_stream is initially NULL (no file opened), read_char should return true and EOF. */
void test_read_char_with_null_stream(void)
{
/* Ensure no open stream and no files */
close_stream_if_open();
static const char *empty_list[] = { NULL };
file_list = empty_list;
int ch = 0;
TEST_ASSERT_TRUE(read_char(&ch));
TEST_ASSERT_EQUAL_INT(EOF, ch);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_read_char_single_byte);
RUN_TEST(test_read_char_three_bytes);
RUN_TEST(test_read_char_eof_no_next_file);
RUN_TEST(test_read_char_transition_to_next_file);
RUN_TEST(test_read_char_with_null_stream);
return UNITY_END();
} |