#include "../../unity/unity.h" #include #include #include #include #include #include /* 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(); }