#include "unity/unity.h" #include "zlib.h" #include #include #include /* Declare the target function with a compatible prototype. We use void* for the state parameter to avoid depending on internal headers. */ extern void gz_error(void *state, int err, const char *msg); static void remove_if_exists(const char *path) { if (path) { remove(path); } } void setUp(void) { /* Setup code here, or leave empty */ } void tearDown(void) { /* Cleanup code here, or leave empty */ } static gzFile open_temp_gz(const char *path, const char *mode) { remove_if_exists(path); gzFile f = gzopen(path, mode); TEST_ASSERT_MESSAGE(f != NULL, "gzopen failed to create/open temporary gzip file"); return f; } static void close_and_cleanup(gzFile f, const char *path) { if (f) { int rc = gzclose(f); TEST_ASSERT_MESSAGE(rc == Z_OK, "gzclose failed"); } remove_if_exists(path); } /* Test: gz_error with (Z_OK, NULL) clears error and leaves empty message. */ void test_gz_error_clears_with_null_message(void) { const char *path = "tmp_gzlib_gz_error_clear.gz"; gzFile f = open_temp_gz(path, "wb"); /* Ensure we start clean */ int errnum = 777; const char *msg = gzerror(f, &errnum); TEST_ASSERT_EQUAL_INT(Z_OK, errnum); TEST_ASSERT_NOT_NULL(msg); /* Explicitly clear with Z_OK and NULL message */ gz_error((void*)f, Z_OK, NULL); errnum = 777; msg = gzerror(f, &errnum); TEST_ASSERT_EQUAL_INT(Z_OK, errnum); /* When no message is set, gzerror returns an empty string */ TEST_ASSERT_NOT_NULL(msg); TEST_ASSERT_EQUAL_UINT32(0, (unsigned)strlen(msg)); close_and_cleanup(f, path); } /* Test: gz_error sets error code and allocates message "path: msg". */ void test_gz_error_sets_message_with_path_prefix(void) { const char *path = "tmp_gzlib_gz_error_basic.gz"; gzFile f = open_temp_gz(path, "wb"); const char *user_msg = "whoops"; gz_error((void*)f, Z_STREAM_ERROR, user_msg); int errnum = 0; const char *ret = gzerror(f, &errnum); TEST_ASSERT_EQUAL_INT(Z_STREAM_ERROR, errnum); TEST_ASSERT_NOT_NULL(ret); char expected[512]; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) (void)snprintf(expected, sizeof(expected), "%s: %s", path, user_msg); #else strcpy(expected, path); strcat(expected, ": "); strcat(expected, user_msg); #endif TEST_ASSERT_EQUAL_STRING(expected, ret); close_and_cleanup(f, path); } /* Test: Calling gz_error repeatedly replaces the previous message and updates the code. */ void test_gz_error_replaces_previous_message(void) { const char *path = "tmp_gzlib_gz_error_replace.gz"; gzFile f = open_temp_gz(path, "wb"); gz_error((void*)f, Z_DATA_ERROR, "first"); int err1 = 0; const char *msg1 = gzerror(f, &err1); TEST_ASSERT_EQUAL_INT(Z_DATA_ERROR, err1); TEST_ASSERT_NOT_NULL(msg1); gz_error((void*)f, Z_BUF_ERROR, "second"); int err2 = 0; const char *msg2 = gzerror(f, &err2); TEST_ASSERT_EQUAL_INT(Z_BUF_ERROR, err2); TEST_ASSERT_NOT_NULL(msg2); /* Pointer should change because a new allocation occurs for the new message */ TEST_ASSERT_NOT_EQUAL(msg1, msg2); char expected[512]; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) (void)snprintf(expected, sizeof(expected), "%s: %s", path, "second"); #else strcpy(expected, path); strcat(expected, ": "); strcat(expected, "second"); #endif TEST_ASSERT_EQUAL_STRING(expected, msg2); close_and_cleanup(f, path); } /* Test: Z_MEM_ERROR returns the literal "out of memory" and does not allocate a message. */ void test_gz_error_mem_error_returns_literal(void) { const char *path = "tmp_gzlib_gz_error_mem.gz"; gzFile f = open_temp_gz(path, "wb"); /* Trigger a memory error; the message argument should be ignored for allocation. */ gz_error((void*)f, Z_MEM_ERROR, "ignored"); int err = 0; const char *ret = gzerror(f, &err); TEST_ASSERT_EQUAL_INT(Z_MEM_ERROR, err); TEST_ASSERT_NOT_NULL(ret); TEST_ASSERT_EQUAL_STRING("out of memory", ret); /* After a mem error, subsequent errors should still work and set a message */ gz_error((void*)f, Z_STREAM_ERROR, "later"); err = 0; ret = gzerror(f, &err); TEST_ASSERT_EQUAL_INT(Z_STREAM_ERROR, err); char expected[512]; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) (void)snprintf(expected, sizeof(expected), "%s: %s", path, "later"); #else strcpy(expected, path); strcat(expected, ": "); strcat(expected, "later"); #endif TEST_ASSERT_EQUAL_STRING(expected, ret); close_and_cleanup(f, path); } /* Test: Z_OK with a non-NULL message stores a message while err stays Z_OK. */ void test_gz_error_ok_with_message_stores_message(void) { const char *path = "tmp_gzlib_gz_error_okmsg.gz"; gzFile f = open_temp_gz(path, "wb"); gz_error((void*)f, Z_OK, "note"); int err = 0; const char *ret = gzerror(f, &err); TEST_ASSERT_EQUAL_INT(Z_OK, err); TEST_ASSERT_NOT_NULL(ret); char expected[512]; #if !defined(NO_snprintf) && !defined(NO_vsnprintf) (void)snprintf(expected, sizeof(expected), "%s: %s", path, "note"); #else strcpy(expected, path); strcat(expected, ": "); strcat(expected, "note"); #endif TEST_ASSERT_EQUAL_STRING(expected, ret); close_and_cleanup(f, path); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_gz_error_clears_with_null_message); RUN_TEST(test_gz_error_sets_message_with_path_prefix); RUN_TEST(test_gz_error_replaces_previous_message); RUN_TEST(test_gz_error_mem_error_returns_literal); RUN_TEST(test_gz_error_ok_with_message_stores_message); return UNITY_END(); }