| #include "unity/unity.h" |
| #include "zlib.h" |
|
|
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <errno.h> |
|
|
| |
| static char *make_temp_path(void) { |
| char tmpl[] = "/tmp/zlib_gzoffset64_XXXXXX"; |
| int fd = mkstemp(tmpl); |
| if (fd == -1) { |
| return NULL; |
| } |
| close(fd); |
| char *path = (char *)malloc(strlen(tmpl) + 1); |
| if (!path) return NULL; |
| strcpy(path, tmpl); |
| return path; |
| } |
|
|
| static size_t file_size(const char *path) { |
| struct stat st; |
| if (stat(path, &st) != 0) return 0; |
| return (size_t)st.st_size; |
| } |
|
|
| |
| static int write_gz_file(const char *path, const unsigned char *data, size_t len) { |
| gzFile gz = gzopen(path, "wb"); |
| if (!gz) return -1; |
| size_t total = 0; |
| while (total < len) { |
| unsigned chunk = (unsigned)((len - total) > 4096 ? 4096 : (len - total)); |
| int wrote = gzwrite(gz, data + total, chunk); |
| if (wrote <= 0) { |
| gzclose(gz); |
| return -1; |
| } |
| total += (size_t)wrote; |
| } |
| int rc = gzclose(gz); |
| return rc == Z_OK ? 0 : -1; |
| } |
|
|
| |
| static unsigned char *make_data(size_t len) { |
| unsigned char *buf = (unsigned char *)malloc(len); |
| if (!buf) return NULL; |
| for (size_t i = 0; i < len; i++) { |
| buf[i] = (unsigned char)(i * 1315423911u + 0x5bd1e995u); |
| } |
| return buf; |
| } |
|
|
| void setUp(void) { |
| |
| } |
|
|
| void tearDown(void) { |
| |
| } |
|
|
| |
|
|
| void test_gzoffset64_null_returns_minus1(void) { |
| z_off64_t off = gzoffset64(NULL); |
| TEST_ASSERT_EQUAL_INT64(-1, off); |
| } |
|
|
| void test_gzoffset64_write_mode_zero_and_increases_after_flush(void) { |
| char *path = make_temp_path(); |
| TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp path"); |
|
|
| gzFile gz = gzopen(path, "wb"); |
| TEST_ASSERT_NOT_NULL_MESSAGE(gz, "gzopen failed for write"); |
|
|
| |
| z_off64_t off0 = gzoffset64(gz); |
| TEST_ASSERT_EQUAL_INT64(0, off0); |
|
|
| |
| const char *msg1 = "The quick brown fox jumps over the lazy dog.\n"; |
| int w1 = gzwrite(gz, msg1, (unsigned)strlen(msg1)); |
| TEST_ASSERT_TRUE(w1 > 0); |
|
|
| |
| z_off64_t off_pre = gzoffset64(gz); |
| TEST_ASSERT_TRUE(off_pre >= 0); |
|
|
| |
| int fr = gzflush(gz, Z_SYNC_FLUSH); |
| TEST_ASSERT_TRUE(fr == Z_OK || fr == Z_BUF_ERROR); |
|
|
| z_off64_t off1 = gzoffset64(gz); |
| TEST_ASSERT_TRUE_MESSAGE(off1 >= 0, "gzoffset64 should be non-negative after flush"); |
| |
| TEST_ASSERT_TRUE_MESSAGE(off1 > 0, "gzoffset64 should be > 0 after writing and flushing"); |
|
|
| |
| const char *msg2 = "Pack my box with five dozen liquor jugs.\n"; |
| int w2 = gzwrite(gz, msg2, (unsigned)strlen(msg2)); |
| TEST_ASSERT_TRUE(w2 > 0); |
|
|
| fr = gzflush(gz, Z_SYNC_FLUSH); |
| TEST_ASSERT_TRUE(fr == Z_OK || fr == Z_BUF_ERROR); |
|
|
| z_off64_t off2 = gzoffset64(gz); |
| TEST_ASSERT_TRUE_MESSAGE(off2 >= off1, "Offset should be non-decreasing after additional writes"); |
|
|
| |
| int rc = gzclose(gz); |
| TEST_ASSERT_EQUAL_INT(Z_OK, rc); |
|
|
| size_t final_size = file_size(path); |
| TEST_ASSERT_TRUE_MESSAGE((z_off64_t)final_size >= off2, |
| "Final file size should be >= offset observed before close"); |
|
|
| |
| remove(path); |
| free(path); |
| } |
|
|
| void test_gzoffset64_read_mode_initial_fd_position_via_gzdopen(void) { |
| char *path = make_temp_path(); |
| TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp path"); |
|
|
| |
| int fd_create = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600); |
| TEST_ASSERT_TRUE_MESSAGE(fd_create >= 0, "open for create failed"); |
| unsigned char zeros[1024]; |
| memset(zeros, 0, sizeof(zeros)); |
| ssize_t wr = write(fd_create, zeros, sizeof(zeros)); |
| TEST_ASSERT_TRUE_MESSAGE(wr == (ssize_t)sizeof(zeros), "write failed"); |
| close(fd_create); |
|
|
| |
| int fd = open(path, O_RDONLY); |
| TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "open for read failed"); |
| off_t want = 123; |
| off_t pos = lseek(fd, want, SEEK_SET); |
| TEST_ASSERT_EQUAL_INT64((long long)want, (long long)pos); |
|
|
| |
| gzFile gz = gzdopen(fd, "rb"); |
| TEST_ASSERT_NOT_NULL_MESSAGE(gz, "gzdopen failed"); |
|
|
| |
| z_off64_t off = gzoffset64(gz); |
| TEST_ASSERT_EQUAL_INT64((long long)want, (long long)off); |
|
|
| int rc = gzclose(gz); |
| TEST_ASSERT_EQUAL_INT(Z_OK, rc); |
|
|
| remove(path); |
| free(path); |
| } |
|
|
| void test_gzoffset64_read_mode_not_greater_than_raw_fd_position_after_read(void) { |
| char *path = make_temp_path(); |
| TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp path"); |
|
|
| |
| size_t data_len = 20000; |
| unsigned char *data = make_data(data_len); |
| TEST_ASSERT_NOT_NULL_MESSAGE(data, "Failed to allocate data"); |
| TEST_ASSERT_EQUAL_INT(0, write_gz_file(path, data, data_len)); |
| free(data); |
|
|
| |
| int fd = open(path, O_RDONLY); |
| TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "open gzip for read failed"); |
| gzFile gz = gzdopen(fd, "rb"); |
| TEST_ASSERT_NOT_NULL_MESSAGE(gz, "gzdopen failed"); |
|
|
| |
| off_t raw0 = lseek(fd, 0, SEEK_CUR); |
| TEST_ASSERT_EQUAL_INT64(0, (long long)raw0); |
| z_off64_t off0 = gzoffset64(gz); |
| TEST_ASSERT_EQUAL_INT64(0, (long long)off0); |
|
|
| unsigned char buf[128]; |
| int rd1 = gzread(gz, buf, sizeof(buf)); |
| TEST_ASSERT_TRUE_MESSAGE(rd1 > 0, "gzread should read some bytes"); |
|
|
| off_t raw1 = lseek(fd, 0, SEEK_CUR); |
| z_off64_t off1 = gzoffset64(gz); |
| TEST_ASSERT_TRUE_MESSAGE(off1 >= 0, "gzoffset64 should be non-negative"); |
| TEST_ASSERT_TRUE_MESSAGE(off1 <= (z_off64_t)raw1, "gzoffset64 should be <= raw fd position after read"); |
|
|
| |
| int rd2 = gzread(gz, buf, sizeof(buf)); |
| TEST_ASSERT_TRUE_MESSAGE(rd2 >= 0, "gzread should not error"); |
| off_t raw2 = lseek(fd, 0, SEEK_CUR); |
| z_off64_t off2 = gzoffset64(gz); |
| TEST_ASSERT_TRUE_MESSAGE(off2 >= off1, "gzoffset64 should be non-decreasing after additional reads"); |
| TEST_ASSERT_TRUE_MESSAGE(off2 <= (z_off64_t)raw2, "gzoffset64 should be <= raw fd position after more reads"); |
|
|
| int rc = gzclose(gz); |
| TEST_ASSERT_EQUAL_INT(Z_OK, rc); |
|
|
| remove(path); |
| free(path); |
| } |
|
|
| void test_gzoffset64_unchanged_by_pending_seek_request(void) { |
| |
| char *path = make_temp_path(); |
| TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp path"); |
|
|
| size_t data_len = 10000; |
| unsigned char *data = make_data(data_len); |
| TEST_ASSERT_NOT_NULL_MESSAGE(data, "Failed to allocate data"); |
| TEST_ASSERT_EQUAL_INT(0, write_gz_file(path, data, data_len)); |
| free(data); |
|
|
| |
| int fd = open(path, O_RDONLY); |
| TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "open for read failed"); |
| gzFile gz = gzdopen(fd, "rb"); |
| TEST_ASSERT_NOT_NULL_MESSAGE(gz, "gzdopen failed"); |
|
|
| |
| unsigned char tmp[64]; |
| int rd = gzread(gz, tmp, sizeof(tmp)); |
| TEST_ASSERT_TRUE_MESSAGE(rd >= 0, "gzread should not fail"); |
|
|
| z_off64_t before = gzoffset64(gz); |
|
|
| |
| z_off64_t newpos = gzseek64(gz, 10, SEEK_CUR); |
| TEST_ASSERT_TRUE_MESSAGE(newpos >= 0, "gzseek64 should succeed"); |
|
|
| z_off64_t after = gzoffset64(gz); |
|
|
| |
| TEST_ASSERT_EQUAL_INT64((long long)before, (long long)after); |
|
|
| int rc = gzclose(gz); |
| TEST_ASSERT_EQUAL_INT(Z_OK, rc); |
|
|
| remove(path); |
| free(path); |
| } |
|
|
| int main(void) { |
| UNITY_BEGIN(); |
| RUN_TEST(test_gzoffset64_null_returns_minus1); |
| RUN_TEST(test_gzoffset64_write_mode_zero_and_increases_after_flush); |
| RUN_TEST(test_gzoffset64_read_mode_initial_fd_position_via_gzdopen); |
| RUN_TEST(test_gzoffset64_read_mode_not_greater_than_raw_fd_position_after_read); |
| RUN_TEST(test_gzoffset64_unchanged_by_pending_seek_request); |
| return UNITY_END(); |
| } |