#include "unity/unity.h" #include "zlib.h" #include #include #include #include /* mkstemp, close, unlink, pipe */ #include #include #include /* Test content */ static const char *kSampleText = "Hello world! This is a test of gzrewind."; static const size_t kSampleLen = 44; /* strlen above, but keep constant to ensure stability */ /* Unity setup/teardown */ void setUp(void) { /* Setup code here, or leave empty */ } void tearDown(void) { /* Cleanup code here, or leave empty */ } /* Helpers */ static void make_temp_path(char *buf, size_t buflen) { /* Create a unique temp file path template */ /* Using /tmp is typical on POSIX. Adjust if environment differs. */ snprintf(buf, buflen, "/tmp/test_gzrewind_%ld_XXXXXX", (long)getpid()); } static void create_gz_file(const char *path, const char *data, size_t len) { gzFile gz = gzopen(path, "wb"); TEST_ASSERT_NOT_NULL(gz); int wrote = gzwrite(gz, data, (unsigned)len); TEST_ASSERT_EQUAL_INT((int)len, wrote); int rc = gzclose(gz); TEST_ASSERT_EQUAL_INT(0, rc); } static void read_exact(gzFile gz, void *buf, size_t len) { unsigned char *p = (unsigned char *)buf; size_t total = 0; while (total < len) { int n = gzread(gz, p + total, (unsigned)(len - total)); TEST_ASSERT_TRUE_MESSAGE(n >= 0, "gzread returned error"); if (n == 0) break; /* EOF */ total += (size_t)n; } TEST_ASSERT_EQUAL_UINT64(len, total); } /* Tests */ void test_gzrewind_null(void) { TEST_ASSERT_EQUAL_INT(-1, gzrewind(NULL)); } void test_gzrewind_write_mode_returns_error(void) { char tmpl[256]; make_temp_path(tmpl, sizeof(tmpl)); int fd = mkstemp(tmpl); TEST_ASSERT_TRUE_MESSAGE(fd != -1, "mkstemp failed"); close(fd); /* we'll use gzopen on the path */ gzFile gz = gzopen(tmpl, "wb"); TEST_ASSERT_NOT_NULL(gz); /* gzrewind should fail for write mode */ int rc = gzrewind(gz); TEST_ASSERT_EQUAL_INT(-1, rc); /* cleanup */ gzclose(gz); unlink(tmpl); } void test_gzrewind_seekable_basic_reset(void) { char tmpl[256]; make_temp_path(tmpl, sizeof(tmpl)); int fd = mkstemp(tmpl); TEST_ASSERT_TRUE_MESSAGE(fd != -1, "mkstemp failed"); close(fd); create_gz_file(tmpl, kSampleText, strlen(kSampleText)); gzFile gz = gzopen(tmpl, "rb"); TEST_ASSERT_NOT_NULL(gz); /* Read first 5 bytes */ char first[6] = {0}; int n = gzread(gz, first, 5); TEST_ASSERT_EQUAL_INT(5, n); TEST_ASSERT_EQUAL_STRING_LEN("Hello", first, 5); /* Position should be 5 */ z_off_t pos = gztell(gz); TEST_ASSERT_EQUAL_INT(5, (int)pos); /* Rewind */ int rc = gzrewind(gz); TEST_ASSERT_EQUAL_INT(0, rc); /* Position should be 0 */ pos = gztell(gz); TEST_ASSERT_EQUAL_INT(0, (int)pos); /* Read first 5 bytes again */ memset(first, 0, sizeof(first)); n = gzread(gz, first, 5); TEST_ASSERT_EQUAL_INT(5, n); TEST_ASSERT_EQUAL_STRING_LEN("Hello", first, 5); gzclose(gz); unlink(tmpl); } void test_gzrewind_clears_eof_and_restarts(void) { char tmpl[256]; make_temp_path(tmpl, sizeof(tmpl)); int fd = mkstemp(tmpl); TEST_ASSERT_TRUE_MESSAGE(fd != -1, "mkstemp failed"); close(fd); const char *data = kSampleText; const size_t len = strlen(kSampleText); create_gz_file(tmpl, data, len); gzFile gz = gzopen(tmpl, "rb"); TEST_ASSERT_NOT_NULL(gz); /* Read entire content */ char *buf = (char *)malloc(len); TEST_ASSERT_NOT_NULL(buf); read_exact(gz, buf, len); /* Should be at EOF now */ int eof = gzeof(gz); TEST_ASSERT_TRUE(eof != 0); /* Rewind should succeed and clear EOF */ int rc = gzrewind(gz); TEST_ASSERT_EQUAL_INT(0, rc); eof = gzeof(gz); TEST_ASSERT_EQUAL_INT(0, eof); /* Read again and compare */ memset(buf, 0, len); read_exact(gz, buf, len); TEST_ASSERT_EQUAL_INT(0, memcmp(buf, data, len)); free(buf); gzclose(gz); unlink(tmpl); } void test_gzrewind_nonseekable_fd_returns_error(void) { int fds[2]; int prc = pipe(fds); TEST_ASSERT_EQUAL_INT(0, prc); int rfd = fds[0]; int wfd = fds[1]; /* Open a gz stream on the read end of a pipe. This is non-seekable. */ gzFile gz = gzdopen(rfd, "rb"); TEST_ASSERT_NOT_NULL(gz); /* gzrewind should attempt to lseek and fail, returning -1 */ int rc = gzrewind(gz); TEST_ASSERT_EQUAL_INT(-1, rc); /* cleanup: gzclose will close rfd; close write end explicitly */ gzclose(gz); close(wfd); } void test_gzrewind_multiple_calls_idempotent(void) { char tmpl[256]; make_temp_path(tmpl, sizeof(tmpl)); int fd = mkstemp(tmpl); TEST_ASSERT_TRUE_MESSAGE(fd != -1, "mkstemp failed"); close(fd); create_gz_file(tmpl, kSampleText, strlen(kSampleText)); gzFile gz = gzopen(tmpl, "rb"); TEST_ASSERT_NOT_NULL(gz); /* First rewind before any read: should succeed */ TEST_ASSERT_EQUAL_INT(0, gzrewind(gz)); /* Read a few bytes, then rewind, twice */ char buf[6] = {0}; int n = gzread(gz, buf, 5); TEST_ASSERT_EQUAL_INT(5, n); TEST_ASSERT_EQUAL_STRING_LEN("Hello", buf, 5); TEST_ASSERT_EQUAL_INT(0, gzrewind(gz)); TEST_ASSERT_EQUAL_INT(0, gzrewind(gz)); /* Read again from start */ memset(buf, 0, sizeof(buf)); n = gzread(gz, buf, 5); TEST_ASSERT_EQUAL_INT(5, n); TEST_ASSERT_EQUAL_STRING_LEN("Hello", buf, 5); gzclose(gz); unlink(tmpl); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_gzrewind_null); RUN_TEST(test_gzrewind_write_mode_returns_error); RUN_TEST(test_gzrewind_seekable_basic_reset); RUN_TEST(test_gzrewind_clears_eof_and_restarts); RUN_TEST(test_gzrewind_nonseekable_fd_returns_error); RUN_TEST(test_gzrewind_multiple_calls_idempotent); return UNITY_END(); }