#include "unity/unity.h" #include "zlib.h" #include #include #include #include #include #include #include #include /* Declare the provided test wrapper without relying on internal types */ int test_gz_load(void *state, unsigned char *buf, unsigned len, unsigned *have); void setUp(void) { /* Setup code here, or leave empty */ } void tearDown(void) { /* Cleanup code here, or leave empty */ } /* Helper: create a temporary file with given data, return fd and path. The caller is responsible for unlinking the path and closing/unwrapping fd. */ static int make_temp_file_with_data(const unsigned char *data, size_t len, char *out_path, size_t out_path_size) { const char *tmpdir = getenv("TMPDIR"); if (!tmpdir) tmpdir = "/tmp"; snprintf(out_path, out_path_size, "%s/gzread_test_%ld_%d_XXXXXX", tmpdir, (long)getpid(), rand()); int fd = mkstemp(out_path); if (fd == -1) { return -1; } ssize_t w = write(fd, data, len); if (w < 0 || (size_t)w != len) { close(fd); unlink(out_path); return -1; } if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { close(fd); unlink(out_path); return -1; } return fd; } /* Helper: open a gzFile for reading using gzdopen() on an existing fd */ static gzFile open_gzfile_from_fd_for_reading(int fd) { /* gzdopen takes ownership of fd and will close it on gzclose_r */ gzFile file = gzdopen(fd, "rb"); return file; } /* Test 1: Basic sequential reads and EOF behavior on a regular file */ void test_gz_load_basic_sequential_reads(void) { const unsigned char content[] = "ABCDEFGHIJ"; /* 10 bytes */ char path[256]; int fd = make_temp_file_with_data(content, sizeof(content) - 1, path, sizeof(path)); TEST_ASSERT_MESSAGE(fd != -1, "Failed to create temp file"); gzFile file = open_gzfile_from_fd_for_reading(fd); TEST_ASSERT_MESSAGE(file != NULL, "gzdopen failed"); /* Unlink immediately; fd remains valid until gzclose_r */ unlink(path); unsigned char buf[8]; unsigned have = 123; /* ensure function resets it */ int ret; /* Read first 4 bytes */ ret = test_gz_load((void*)file, buf, 4U, &have); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_UINT(4U, have); TEST_ASSERT_EQUAL_UINT8_ARRAY(content + 0, buf, 4); /* Read next 4 bytes */ have = 0; ret = test_gz_load((void*)file, buf, 4U, &have); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_UINT(4U, have); TEST_ASSERT_EQUAL_UINT8_ARRAY(content + 4, buf, 4); /* Request 4 more, but only 2 remain */ have = 0; ret = test_gz_load((void*)file, buf, 4U, &have); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_UINT(2U, have); TEST_ASSERT_EQUAL_UINT8_ARRAY(content + 8, buf, 2); /* Next call at EOF should return 0 with have==0 */ have = 777; ret = test_gz_load((void*)file, buf, 4U, &have); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_UINT(0U, have); /* Cleanup */ int c = gzclose_r(file); TEST_ASSERT_MESSAGE(c == Z_OK || c == Z_BUF_ERROR, "gzclose_r failed"); } /* Test 2: Partial read and EOF via a pipe (writer provides fewer bytes than requested) */ void test_gz_load_handles_partial_read_with_pipe(void) { int pfd[2]; int r = pipe(pfd); TEST_ASSERT_MESSAGE(r == 0, "pipe() failed"); const unsigned char msg[] = "hello"; /* 5 bytes */ ssize_t w = write(pfd[1], msg, sizeof(msg) - 1); TEST_ASSERT_MESSAGE(w == (ssize_t)(sizeof(msg) - 1), "write to pipe failed"); close(pfd[1]); /* Ensure EOF after available data */ gzFile file = open_gzfile_from_fd_for_reading(pfd[0]); TEST_ASSERT_MESSAGE(file != NULL, "gzdopen failed on pipe"); unsigned char buf[16]; unsigned have = 0; int ret = test_gz_load((void*)file, buf, 10U, &have); /* request more than available */ TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_UINT(5U, have); TEST_ASSERT_EQUAL_UINT8_ARRAY(msg, buf, 5); int c = gzclose_r(file); TEST_ASSERT_MESSAGE(c == Z_OK || c == Z_BUF_ERROR, "gzclose_r failed"); } /* Test 3: Error path - use a write-only file descriptor to force read() error (EBADF) */ void test_gz_load_returns_error_on_unreadable_fd(void) { const unsigned char content[] = "data"; char path[256]; int fdw = make_temp_file_with_data(content, sizeof(content) - 1, path, sizeof(path)); TEST_ASSERT_MESSAGE(fdw != -1, "Failed to create temp file for error test"); close(fdw); /* Open write-only to make read() fail with EBADF */ int fd = open(path, O_WRONLY); TEST_ASSERT_MESSAGE(fd != -1, "Failed to reopen temp file write-only"); /* Unlink immediately; fd remains valid until gzclose_r */ unlink(path); gzFile file = open_gzfile_from_fd_for_reading(fd); TEST_ASSERT_MESSAGE(file != NULL, "gzdopen failed on write-only fd"); unsigned char buf[8]; unsigned have = 42; int ret = test_gz_load((void*)file, buf, 5U, &have); TEST_ASSERT_EQUAL_INT(-1, ret); TEST_ASSERT_EQUAL_UINT(0U, have); int c = gzclose_r(file); TEST_ASSERT_MESSAGE(c == Z_OK || c == Z_BUF_ERROR, "gzclose_r failed"); } /* Test 4: Zero-length request returns 0 and does not consume data */ void test_gz_load_zero_length_request(void) { const unsigned char content[] = "WXYZ"; /* 4 bytes */ char path[256]; int fd = make_temp_file_with_data(content, sizeof(content) - 1, path, sizeof(path)); TEST_ASSERT_MESSAGE(fd != -1, "Failed to create temp file"); gzFile file = open_gzfile_from_fd_for_reading(fd); TEST_ASSERT_MESSAGE(file != NULL, "gzdopen failed"); unlink(path); unsigned char buf[8]; unsigned have = 99; /* len == 0 should return 0 and set have == 0 */ int ret = test_gz_load((void*)file, buf, 0U, &have); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_UINT(0U, have); /* Subsequent normal read should still return initial data */ have = 0; ret = test_gz_load((void*)file, buf, 4U, &have); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_UINT(4U, have); TEST_ASSERT_EQUAL_UINT8_ARRAY(content, buf, 4); int c = gzclose_r(file); TEST_ASSERT_MESSAGE(c == Z_OK || c == Z_BUF_ERROR, "gzclose_r failed"); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_gz_load_basic_sequential_reads); RUN_TEST(test_gz_load_handles_partial_read_with_pipe); RUN_TEST(test_gz_load_returns_error_on_unreadable_fd); RUN_TEST(test_gz_load_zero_length_request); return UNITY_END(); }