#include "../../unity/unity.h" #include #include #include #include #include #include #include #include #include /* The test file is included into the od source, so we can access the internal globals and functions directly. We declare externs only for clarity; they are defined above in the included program. */ /* Globals from od.c (visible since this test is included into the TU) */ extern FILE *in_stream; extern char const *const *file_list; extern char const *input_filename; extern bool have_read_stdin; extern intmax_t end_offset; extern bool flag_dump_strings; /* Functions from od.c */ extern bool open_next_file (void); extern bool check_and_close (int in_errno); extern bool skip (intmax_t n_skip); /* Helpers for tests */ static char *create_temp_file_with_size(size_t size) { char tmpl[] = "/tmp/od_skip_testXXXXXX"; int fd = mkstemp(tmpl); if (fd < 0) { return NULL; } /* Write 'size' bytes of predictable data */ size_t remaining = size; char buf[4096]; memset(buf, 'A', sizeof(buf)); while (remaining > 0) { size_t chunk = remaining < sizeof(buf) ? remaining : sizeof(buf); ssize_t w = write(fd, buf, chunk); if (w < 0) { int e = errno; close(fd); unlink(tmpl); errno = e; return NULL; } remaining -= (size_t)w; } if (fsync(fd) != 0) { /* Not fatal for tests, continue */ } if (close(fd) != 0) { unlink(tmpl); return NULL; } /* Return a heap-allocated copy of the path so caller can free */ char *path = strdup(tmpl); return path; } static long current_offset(FILE *f) { off_t off = ftello(f); if (off == (off_t)-1) return -1; return (long)off; } void setUp(void) { /* Reset core globals to a safe baseline for each test. */ in_stream = NULL; have_read_stdin = false; end_offset = -1; /* No end limit */ flag_dump_strings = false; } void tearDown(void) { /* If a stream is still open, close it safely. Note: in tests, we prefer to close via check_and_close within each test before local arrays (for file_list) go out of scope. */ if (in_stream) { check_and_close(0); in_stream = NULL; } } /* Test: n_skip == 0 is a no-op and returns true, without requiring an open stream. */ static void test_skip_zero_no_stream(void) { in_stream = NULL; bool ok = skip(0); TEST_ASSERT_TRUE(ok); TEST_ASSERT_NULL(in_stream); } /* Test: Skip within a single large file using seek optimization. */ static void test_skip_within_single_file_seek(void) { char *p1 = create_temp_file_with_size(100000); /* large to trigger seek path */ TEST_ASSERT_NOT_NULL_MESSAGE(p1, "failed to create temp file"); /* Set up file list and open first file */ const char *flist[] = { p1, NULL }; file_list = flist; bool ok = open_next_file(); TEST_ASSERT_TRUE(ok); TEST_ASSERT_NOT_NULL(in_stream); TEST_ASSERT_EQUAL_INT(0, current_offset(in_stream)); ok = skip(12345); TEST_ASSERT_TRUE(ok); TEST_ASSERT_NOT_NULL(in_stream); TEST_ASSERT_EQUAL_INT(12345, current_offset(in_stream)); /* Cleanup while file_list is still valid */ check_and_close(0); unlink(p1); free(p1); } /* Test: Skip across two files, requiring skipping past the entire first file and partially into the second file. */ static void test_skip_across_two_files_boundary_seek_on_first(void) { char *p1 = create_temp_file_with_size(5000); char *p2 = create_temp_file_with_size(2000); TEST_ASSERT_NOT_NULL(p1); TEST_ASSERT_NOT_NULL(p2); const char *flist[] = { p1, p2, NULL }; file_list = flist; bool ok = open_next_file(); TEST_ASSERT_TRUE(ok); TEST_ASSERT_NOT_NULL(in_stream); TEST_ASSERT_EQUAL_INT(0, current_offset(in_stream)); /* Skip more than file1, but less than total */ ok = skip(5200); TEST_ASSERT_TRUE(ok); /* Expect to be in the second file at offset 200. */ TEST_ASSERT_NOT_NULL(in_stream); long off = current_offset(in_stream); TEST_ASSERT_EQUAL_INT(200, off); /* Cleanup */ check_and_close(0); unlink(p1); unlink(p2); free(p1); free(p2); } /* Test: Read-based skipping on a small regular file (size <= block size), exercising the fread path. */ static void test_skip_read_based_small_file(void) { char *p1 = create_temp_file_with_size(10); TEST_ASSERT_NOT_NULL(p1); const char *flist[] = { p1, NULL }; file_list = flist; bool ok = open_next_file(); TEST_ASSERT_TRUE(ok); TEST_ASSERT_NOT_NULL(in_stream); TEST_ASSERT_EQUAL_INT(0, current_offset(in_stream)); ok = skip(6); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_INT(6, current_offset(in_stream)); /* Cleanup */ check_and_close(0); unlink(p1); free(p1); } /* Test: Skipping exactly the size of the first file remains at EOF of first file. The code seeks within the first file and does not immediately open the next. */ static void test_skip_exact_file_size_stays_on_first_at_eof(void) { char *p1 = create_temp_file_with_size(1000); char *p2 = create_temp_file_with_size(100); TEST_ASSERT_NOT_NULL(p1); TEST_ASSERT_NOT_NULL(p2); const char *flist[] = { p1, p2, NULL }; file_list = flist; bool ok = open_next_file(); TEST_ASSERT_TRUE(ok); TEST_ASSERT_NOT_NULL(in_stream); ok = skip(1000); TEST_ASSERT_TRUE(ok); /* Expect still on first file at offset 1000 (EOF), not yet transitioned */ TEST_ASSERT_NOT_NULL(in_stream); TEST_ASSERT_EQUAL_INT(1000, current_offset(in_stream)); /* Cleanup */ check_and_close(0); unlink(p1); unlink(p2); free(p1); free(p2); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_skip_zero_no_stream); RUN_TEST(test_skip_within_single_file_seek); RUN_TEST(test_skip_across_two_files_boundary_seek_on_first); RUN_TEST(test_skip_read_based_small_file); RUN_TEST(test_skip_exact_file_size_stays_on_first_at_eof); return UNITY_END(); }