#include "unity/unity.h" #include "zlib.h" #include "deflate.h" #include #include #include /* Wrapper provided in the module under test */ void test_fill_window(deflate_state *s); static void init_deflater(z_stream *strm) { memset(strm, 0, sizeof(*strm)); int ret = deflateInit(strm, Z_DEFAULT_COMPRESSION); TEST_ASSERT_EQUAL_INT(Z_OK, ret); } static void cleanup_deflater(z_stream *strm) { int ret = deflateEnd(strm); TEST_ASSERT(ret == Z_OK || ret == Z_DATA_ERROR); } void setUp(void) { /* Setup code here, or leave empty */ } void tearDown(void) { /* Cleanup code here, or leave empty */ } /* * Test that fill_window reads available input into the sliding window, * updates lookahead, total_in, next_in, and adler (for zlib wrapper). */ void test_fill_window_reads_data_and_updates_adler_and_window(void) { z_stream strm; init_deflater(&strm); deflate_state *s = (deflate_state *)strm.state; TEST_ASSERT_NOT_NULL(s); /* Prepare input */ const size_t in_len = 100; unsigned char inbuf[in_len]; for (size_t i = 0; i < in_len; i++) inbuf[i] = (unsigned char)(i ^ 0x5A); strm.next_in = inbuf; strm.avail_in = (uInt)in_len; /* Preconditions per fill_window assertion */ TEST_ASSERT_TRUE(s->lookahead < MIN_LOOKAHEAD); /* Expected adler after read_buf with wrap==1 */ uLong expected_adler = adler32(strm.adler, inbuf, (uInt)in_len); test_fill_window(s); /* All input consumed */ TEST_ASSERT_EQUAL_UINT32_MESSAGE(in_len, strm.total_in, "total_in should equal input length"); TEST_ASSERT_EQUAL_UINT_MESSAGE(0, strm.avail_in, "avail_in should be zero after read"); /* Data copied into window at current position */ TEST_ASSERT_TRUE_MESSAGE(s->lookahead == in_len, "lookahead should equal input length when input is exhausted"); TEST_ASSERT_EQUAL_INT(0, memcmp(s->window + s->strstart, inbuf, in_len)); /* Adler updated (zlib wrapper default) */ TEST_ASSERT_EQUAL_UINT32(expected_adler, strm.adler); /* High water advanced to at least current end */ TEST_ASSERT_TRUE(s->high_water >= (ulg)(s->strstart + s->lookahead)); cleanup_deflater(&strm); } /* * Test that when there is no input available, fill_window doesn't change lookahead * and doesn't consume anything. */ void test_fill_window_no_input_no_change(void) { z_stream strm; init_deflater(&strm); deflate_state *s = (deflate_state *)strm.state; TEST_ASSERT_NOT_NULL(s); /* Ensure no input */ strm.next_in = Z_NULL; strm.avail_in = 0; /* track initial values */ uInt initial_lookahead = s->lookahead; uLong initial_total_in = strm.total_in; /* Precondition */ TEST_ASSERT_TRUE(s->lookahead < MIN_LOOKAHEAD); test_fill_window(s); TEST_ASSERT_EQUAL_UINT(initial_lookahead, s->lookahead); TEST_ASSERT_EQUAL_UINT32(initial_total_in, strm.total_in); cleanup_deflater(&strm); } /* * Test that the high-water zeroing logic clears the window bytes after the * current data position, up to WIN_INIT or window end, whichever is smaller. */ void test_fill_window_zeroes_high_water_region(void) { z_stream strm; init_deflater(&strm); deflate_state *s = (deflate_state *)strm.state; TEST_ASSERT_NOT_NULL(s); /* Prefill entire window with non-zero so we can detect zeroing */ memset(s->window, 0xA5, (size_t)s->w_size * 2); s->high_water = 0; /* ensure zeroing will be performed */ /* Small input to set curr just beyond start */ const uInt in_len = 16; unsigned char inbuf[in_len]; for (uInt i = 0; i < in_len; i++) inbuf[i] = (unsigned char)(0xC3 ^ i); strm.next_in = inbuf; strm.avail_in = in_len; /* Precondition */ TEST_ASSERT_TRUE(s->lookahead < MIN_LOOKAHEAD); test_fill_window(s); /* curr = s->strstart + s->lookahead (strstart is 0 initially) */ ulg curr = s->strstart + (ulg)s->lookahead; TEST_ASSERT_EQUAL_UINT(in_len, curr); /* Expected zero length */ ulg expected_zero = s->window_size - curr; if (expected_zero > WIN_INIT) expected_zero = WIN_INIT; /* Verify zeroing */ for (ulg i = 0; i < expected_zero; i++) { TEST_ASSERT_EQUAL_UINT8_MESSAGE(0, s->window[curr + i], "high-water region should be zeroed"); } /* High water should have advanced */ TEST_ASSERT_TRUE(s->high_water >= curr); cleanup_deflater(&strm); } /* * Test that when strstart is far enough, fill_window triggers a slide of the * window, moving the upper half down, and adjusts match_start accordingly. */ void test_fill_window_triggers_slide_and_adjusts_pointers(void) { z_stream strm; init_deflater(&strm); deflate_state *s = (deflate_state *)strm.state; TEST_ASSERT_NOT_NULL(s); uInt wsize = s->w_size; /* 32K by default */ ulg win_size = s->window_size; /* 2 * wsize */ /* Prepare window upper half with a known pattern */ uInt more = MIN_LOOKAHEAD; /* we will force 'more' to this */ uInt copy_len = wsize - more; /* amount copied during slide */ memset(s->window, 0x00, (size_t)wsize * 2); memset(s->window + wsize, 0x5B, copy_len); /* pattern in upper half */ /* Force conditions to trigger the slide: strstart >= wsize + MAX_DIST(s) which equals 2*wsize - MIN_LOOKAHEAD, with lookahead == 0 */ s->strstart = (uInt)(win_size - MIN_LOOKAHEAD); s->lookahead = 0; s->block_start = 0; s->insert = 0; s->match_start = wsize + 5; /* will be reduced by wsize */ /* Provide a byte of input so fill_window proceeds after sliding */ unsigned char inbyte = 0xAA; strm.next_in = &inbyte; strm.avail_in = 1; test_fill_window(s); /* After sliding, the lower part should contain the copied pattern */ for (uInt i = 0; i < copy_len; i++) { TEST_ASSERT_EQUAL_UINT8(0x5B, s->window[i]); } /* match_start should be adjusted down by wsize */ TEST_ASSERT_EQUAL_UINT((uInt)5, s->match_start); cleanup_deflater(&strm); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_fill_window_reads_data_and_updates_adler_and_window); RUN_TEST(test_fill_window_no_input_no_change); RUN_TEST(test_fill_window_zeroes_high_water_region); RUN_TEST(test_fill_window_triggers_slide_and_adjusts_pointers); return UNITY_END(); }