|
|
#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(); |
|
|
} |