| #include "unity/unity.h" |
| #include "zlib.h" |
| #include "gzguts.h" |
|
|
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
|
|
| |
| int test_gz_decomp(gz_statep state); |
|
|
| |
|
|
| static int compress_to_gzip(const unsigned char *in, size_t inlen, |
| unsigned char **out, size_t *outlen) { |
| z_stream strm; |
| memset(&strm, 0, sizeof(strm)); |
| int ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, |
| 15 + 16, 8, Z_DEFAULT_STRATEGY); |
| if (ret != Z_OK) return ret; |
|
|
| uLong bound = deflateBound(&strm, (uLong)inlen); |
| unsigned char *buf = (unsigned char *)malloc(bound); |
| if (!buf) { |
| deflateEnd(&strm); |
| return Z_MEM_ERROR; |
| } |
|
|
| strm.next_in = (Bytef *)in; |
| strm.avail_in = (uInt)inlen; |
| strm.next_out = buf; |
| strm.avail_out = (uInt)bound; |
|
|
| ret = deflate(&strm, Z_FINISH); |
| if (ret != Z_STREAM_END) { |
| free(buf); |
| deflateEnd(&strm); |
| return ret == Z_OK ? Z_BUF_ERROR : ret; |
| } |
|
|
| *outlen = (size_t)((Bytef *)strm.next_out - buf); |
| *out = buf; |
|
|
| deflateEnd(&strm); |
| return Z_OK; |
| } |
|
|
| static gz_statep make_state_with_input(const unsigned char *comp, unsigned comp_len) { |
| struct gz_state *state = (struct gz_state *)calloc(1, sizeof(struct gz_state)); |
| if (!state) return NULL; |
|
|
| state->how = GZIP; |
| state->direct = 0; |
| state->err = Z_OK; |
| state->msg = NULL; |
| state->eof = 0; |
| state->x.have = 0; |
| state->x.next = NULL; |
|
|
| z_streamp strm = &state->strm; |
| memset(strm, 0, sizeof(*strm)); |
| strm->zalloc = Z_NULL; |
| strm->zfree = Z_NULL; |
| strm->opaque = Z_NULL; |
| if (inflateInit2(strm, 15 + 16) != Z_OK) { |
| free(state); |
| return NULL; |
| } |
|
|
| strm->next_in = (Bytef *)comp; |
| strm->avail_in = comp_len; |
|
|
| return state; |
| } |
|
|
| static void destroy_state(gz_statep state) { |
| if (!state) return; |
| inflateEnd(&(state->strm)); |
| free(state); |
| } |
|
|
| |
|
|
| void setUp(void) { |
| |
| } |
|
|
| void tearDown(void) { |
| |
| } |
|
|
| |
|
|
| static void test_gz_decomp_success_full_stream_end_sets_LOOK(void) { |
| const char *msg = "Hello, gz_decomp via zlib! 0123456789.\n"; |
| const size_t msg_len = strlen(msg); |
|
|
| unsigned char *gzbuf = NULL; |
| size_t gzlen = 0; |
| int rc = compress_to_gzip((const unsigned char *)msg, msg_len, &gzbuf, &gzlen); |
| TEST_ASSERT_EQUAL_INT(Z_OK, rc); |
| TEST_ASSERT_NOT_NULL(gzbuf); |
| TEST_ASSERT_TRUE(gzlen > 0); |
|
|
| gz_statep state = make_state_with_input(gzbuf, (unsigned)gzlen); |
| TEST_ASSERT_NOT_NULL(state); |
|
|
| |
| unsigned char outbuf[1024]; |
| memset(outbuf, 0xA5, sizeof(outbuf)); |
| state->strm.next_out = outbuf; |
| state->strm.avail_out = (uInt)sizeof(outbuf); |
|
|
| rc = test_gz_decomp(state); |
| TEST_ASSERT_EQUAL_INT(0, rc); |
|
|
| |
| TEST_ASSERT_EQUAL_UINT(msg_len, state->x.have); |
| TEST_ASSERT_EQUAL_INT(LOOK, state->how); |
| TEST_ASSERT_NOT_NULL(state->x.next); |
| TEST_ASSERT_EQUAL_UINT8_ARRAY((const uint8_t *)msg, state->x.next, state->x.have); |
|
|
| destroy_state(state); |
| free(gzbuf); |
| } |
|
|
| static void test_gz_decomp_partial_output_no_end(void) { |
| |
| char msg[256]; |
| for (int i = 0; i < (int)sizeof(msg); i++) { |
| msg[i] = (char)('A' + (i % 26)); |
| } |
| const size_t msg_len = sizeof(msg); |
|
|
| unsigned char *gzbuf = NULL; |
| size_t gzlen = 0; |
| int rc = compress_to_gzip((const unsigned char *)msg, msg_len, &gzbuf, &gzlen); |
| TEST_ASSERT_EQUAL_INT(Z_OK, rc); |
| TEST_ASSERT_TRUE(gzlen > 0); |
|
|
| gz_statep state = make_state_with_input(gzbuf, (unsigned)gzlen); |
| TEST_ASSERT_NOT_NULL(state); |
|
|
| |
| enum { OUT_LIMIT = 37 }; |
| unsigned char outbuf[OUT_LIMIT]; |
| memset(outbuf, 0xCD, sizeof(outbuf)); |
| state->strm.next_out = outbuf; |
| state->strm.avail_out = (uInt)sizeof(outbuf); |
|
|
| rc = test_gz_decomp(state); |
| TEST_ASSERT_EQUAL_INT(0, rc); |
|
|
| |
| TEST_ASSERT_EQUAL_UINT(OUT_LIMIT, state->x.have); |
| TEST_ASSERT_EQUAL_INT(GZIP, state->how); |
| TEST_ASSERT_NOT_NULL(state->x.next); |
| TEST_ASSERT_EQUAL_UINT8_ARRAY((const uint8_t *)msg, state->x.next, state->x.have); |
|
|
| destroy_state(state); |
| free(gzbuf); |
| } |
|
|
| static void test_gz_decomp_unexpected_eof_sets_buf_error(void) { |
| |
| gz_statep state = make_state_with_input(NULL, 0); |
| TEST_ASSERT_NOT_NULL(state); |
|
|
| state->eof = 1; |
|
|
| unsigned char outbuf[64]; |
| memset(outbuf, 0xEE, sizeof(outbuf)); |
| state->strm.next_out = outbuf; |
| state->strm.avail_out = (uInt)sizeof(outbuf); |
|
|
| int rc = test_gz_decomp(state); |
| TEST_ASSERT_EQUAL_INT(0, rc); |
| TEST_ASSERT_EQUAL_INT(Z_BUF_ERROR, state->err); |
| TEST_ASSERT_EQUAL_UINT(0u, state->x.have); |
|
|
| destroy_state(state); |
| } |
|
|
| static void test_gz_decomp_invalid_header_returns_data_error(void) { |
| |
| unsigned char badhdr[2] = { 0x00, 0xFF }; |
|
|
| gz_statep state = make_state_with_input(badhdr, sizeof(badhdr)); |
| TEST_ASSERT_NOT_NULL(state); |
|
|
| unsigned char outbuf[32]; |
| state->strm.next_out = outbuf; |
| state->strm.avail_out = (uInt)sizeof(outbuf); |
|
|
| int rc = test_gz_decomp(state); |
| TEST_ASSERT_EQUAL_INT(-1, rc); |
| TEST_ASSERT_EQUAL_INT(Z_DATA_ERROR, state->err); |
|
|
| destroy_state(state); |
| } |
|
|
| |
|
|
| int main(void) { |
| UNITY_BEGIN(); |
| RUN_TEST(test_gz_decomp_success_full_stream_end_sets_LOOK); |
| RUN_TEST(test_gz_decomp_partial_output_no_end); |
| RUN_TEST(test_gz_decomp_unexpected_eof_sets_buf_error); |
| RUN_TEST(test_gz_decomp_invalid_header_returns_data_error); |
| return UNITY_END(); |
| } |