#include "unity/unity.h" #include "zlib.h" #include "gzguts.h" #include #include #include #include #include /* Wrapper provided in gzread.c */ extern int test_gz_fetch(gz_statep state); /* Helpers to create temporary files */ static char *make_temp_plain(const unsigned char *data, size_t len) { char template[] = "gzread_test_plain_XXXXXX"; int fd = mkstemp(template); TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "mkstemp failed for plain file"); ssize_t wrote = write(fd, data, len); TEST_ASSERT_EQUAL_INT64((long long)len, (long long)wrote); close(fd); return strdup(template); } static char *make_temp_gzip(const unsigned char *data, size_t len) { char template[] = "gzread_test_gzip_XXXXXX"; int fd = mkstemp(template); TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "mkstemp failed for gzip file"); close(fd); /* we'll reopen with gzopen */ gzFile gz = gzopen(template, "wb"); TEST_ASSERT_NOT_NULL_MESSAGE(gz, "gzopen for writing failed"); int wrote = gzwrite(gz, data, (unsigned)len); TEST_ASSERT_TRUE_MESSAGE(wrote == 0 || wrote > 0, "gzwrite returned invalid value"); TEST_ASSERT_EQUAL_INT((int)len, wrote); int zret = gzclose(gz); TEST_ASSERT_EQUAL_INT_MESSAGE(Z_OK, zret, "gzclose write failed"); return strdup(template); } static char *make_temp_empty(void) { char template[] = "gzread_test_empty_XXXXXX"; int fd = mkstemp(template); TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "mkstemp failed for empty file"); close(fd); return strdup(template); } static gz_statep open_state_for_read(const char *path) { gzFile f = gzopen(path, "rb"); TEST_ASSERT_NOT_NULL_MESSAGE(f, "gzopen for reading failed"); return (gz_statep)f; } void setUp(void) { /* Setup code here, or leave empty */ } void tearDown(void) { /* Cleanup code here, or leave empty */ } /* Test: Plain (non-gzip) file should lead to COPY mode and load raw bytes */ void test_gz_fetch_copy_mode_reads_plain_file(void) { const char *text = "The quick brown fox jumps over the lazy dog."; size_t text_len = strlen(text); char *path = make_temp_plain((const unsigned char *)text, text_len); gz_statep st = open_state_for_read(path); /* Call fetch: should detect no gzip header and copy */ int ret = test_gz_fetch(st); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_INT(COPY, st->how); TEST_ASSERT_TRUE_MESSAGE(st->x.have > 0, "No data loaded in COPY mode"); /* Ensure loaded data matches file start */ TEST_ASSERT_LESS_OR_EQUAL_size_t(text_len, (size_t)(st->size << 1)); TEST_ASSERT_LESS_OR_EQUAL_size_t(text_len, (size_t)(st->x.have <= text_len ? st->x.have : text_len)); TEST_ASSERT_TRUE_MESSAGE(st->x.next != NULL, "x.next should point to output buffer"); size_t cmp_len = st->x.have < text_len ? st->x.have : text_len; TEST_ASSERT_EQUAL_INT(0, memcmp(st->x.next, text, cmp_len)); int cclose = gzclose_r((gzFile)st); TEST_ASSERT_TRUE_MESSAGE(cclose == Z_OK || cclose == Z_BUF_ERROR, "gzclose_r returned error"); unlink(path); free(path); } /* Test: Gzip file should decompress into x.next/x.have matching uncompressed data */ void test_gz_fetch_gzip_mode_decompresses(void) { const char *text = "Hello, world! This is gzip data.\n"; size_t text_len = strlen(text); char *path = make_temp_gzip((const unsigned char *)text, text_len); gz_statep st = open_state_for_read(path); int ret = test_gz_fetch(st); TEST_ASSERT_EQUAL_INT(0, ret); /* After a successful decode, some output should be available */ TEST_ASSERT_TRUE_MESSAGE(st->x.have > 0, "Expected decompressed output"); TEST_ASSERT_TRUE_MESSAGE(st->x.next != NULL, "x.next should be set"); /* how may be LOOK if stream ended; but direct must be 0 when decoding gzip */ TEST_ASSERT_EQUAL_INT(0, st->direct); size_t cmp_len = st->x.have < text_len ? st->x.have : text_len; TEST_ASSERT_EQUAL_INT(0, memcmp(st->x.next, text, cmp_len)); int cclose = gzclose_r((gzFile)st); TEST_ASSERT_TRUE_MESSAGE(cclose == Z_OK || cclose == Z_BUF_ERROR, "gzclose_r returned error"); unlink(path); free(path); } /* Test: Empty file should result in LOOK returning with EOF and no data */ void test_gz_fetch_look_eof_empty_file(void) { char *path = make_temp_empty(); gz_statep st = open_state_for_read(path); int ret = test_gz_fetch(st); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_UINT(0u, st->x.have); TEST_ASSERT_TRUE_MESSAGE(st->eof != 0, "EOF should be set for empty file"); int cclose = gzclose_r((gzFile)st); TEST_ASSERT_TRUE_MESSAGE(cclose == Z_OK || cclose == Z_BUF_ERROR, "gzclose_r returned error"); unlink(path); free(path); } /* Test: COPY path should report error when underlying read() fails */ void test_gz_fetch_copy_mode_read_error(void) { const char *text = "plain content to read"; size_t text_len = strlen(text); char *path = make_temp_plain((const unsigned char *)text, text_len); gz_statep st = open_state_for_read(path); /* Ensure buffers are allocated by an initial fetch */ (void)test_gz_fetch(st); /* Force COPY path and simulate read error */ st->x.have = 0; st->how = COPY; st->eof = 0; st->strm.avail_in = 0; int origfd = st->fd; st->fd = -1; int ret = test_gz_fetch(st); TEST_ASSERT_EQUAL_INT(-1, ret); TEST_ASSERT_EQUAL_INT(Z_ERRNO, st->err); /* Restore a valid fd to allow clean close */ st->fd = origfd; int cclose = gzclose_r((gzFile)st); TEST_ASSERT_TRUE_MESSAGE(cclose == Z_OK || cclose == Z_BUF_ERROR || cclose == Z_ERRNO, "gzclose_r returned unexpected error"); unlink(path); free(path); } /* Test: GZIP path should report error when read for input fails */ void test_gz_fetch_gzip_mode_read_error(void) { const char *text = "gzip data for error path"; size_t text_len = strlen(text); char *path = make_temp_gzip((const unsigned char *)text, text_len); gz_statep st = open_state_for_read(path); /* Ensure buffers and inflate state are allocated */ (void)test_gz_fetch(st); /* Force GZIP path with no input buffered and simulate read error */ st->how = GZIP; st->x.have = 0; st->strm.avail_in = 0; st->eof = 0; int origfd = st->fd; st->fd = -1; int ret = test_gz_fetch(st); TEST_ASSERT_EQUAL_INT(-1, ret); TEST_ASSERT_EQUAL_INT(Z_ERRNO, st->err); /* Restore fd for clean close */ st->fd = origfd; int cclose = gzclose_r((gzFile)st); TEST_ASSERT_TRUE_MESSAGE(cclose == Z_OK || cclose == Z_BUF_ERROR || cclose == Z_ERRNO, "gzclose_r returned unexpected error"); unlink(path); free(path); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_gz_fetch_copy_mode_reads_plain_file); RUN_TEST(test_gz_fetch_gzip_mode_decompresses); RUN_TEST(test_gz_fetch_look_eof_empty_file); RUN_TEST(test_gz_fetch_copy_mode_read_error); RUN_TEST(test_gz_fetch_gzip_mode_read_error); return UNITY_END(); }