#include #include #include #include "heatshrink_encoder.h" #include "heatshrink_decoder.h" #include "greatest.h" #if !HEATSHRINK_DYNAMIC_ALLOC #error Must set HEATSHRINK_DYNAMIC_ALLOC to 1 for dynamic allocation test suite. #endif SUITE(encoding); SUITE(decoding); SUITE(integration); #ifdef HEATSHRINK_HAS_THEFT SUITE(properties); #endif static void dump_buf(char *name, uint8_t *buf, uint16_t count) { for (int i=0; iinput_size, 6); ASSERT_EQ(hsd->input_index, 0); heatshrink_decoder_free(hsd); PASS(); } TEST decoder_poll_should_return_empty_if_empty(void) { uint8_t output[256]; size_t out_sz = 0; heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, HEATSHRINK_MIN_WINDOW_BITS, 4); HSD_poll_res res = heatshrink_decoder_poll(hsd, output, 256, &out_sz); ASSERT_EQ(HSDR_POLL_EMPTY, res); heatshrink_decoder_free(hsd); PASS(); } TEST decoder_poll_should_reject_null_hsd(void) { uint8_t output[256]; size_t out_sz = 0; HSD_poll_res res = heatshrink_decoder_poll(NULL, output, 256, &out_sz); ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); PASS(); } TEST decoder_poll_should_reject_null_output_buffer(void) { size_t out_sz = 0; heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, HEATSHRINK_MIN_WINDOW_BITS, 4); HSD_poll_res res = heatshrink_decoder_poll(hsd, NULL, 256, &out_sz); ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); heatshrink_decoder_free(hsd); PASS(); } TEST decoder_poll_should_reject_null_output_size_pointer(void) { uint8_t output[256]; heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, HEATSHRINK_MIN_WINDOW_BITS, 4); HSD_poll_res res = heatshrink_decoder_poll(hsd, output, 256, NULL); ASSERT_EQ(HSDR_POLL_ERROR_NULL, res); heatshrink_decoder_free(hsd); PASS(); } TEST decoder_poll_should_expand_short_literal(void) { uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0 }; //"foo" uint8_t output[4]; heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 3); size_t count = 0; HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); ASSERT_EQ(HSDR_SINK_OK, sres); size_t out_sz = 0; HSD_poll_res pres = heatshrink_decoder_poll(hsd, output, 4, &out_sz); ASSERT_EQ(HSDR_POLL_EMPTY, pres); ASSERT_EQ(3, out_sz); ASSERT_EQ('f', output[0]); ASSERT_EQ('o', output[1]); ASSERT_EQ('o', output[2]); heatshrink_decoder_free(hsd); PASS(); } TEST decoder_poll_should_expand_short_literal_and_backref(void) { uint8_t input[] = {0xb3, 0x5b, 0xed, 0xe0, 0x40, 0x80}; //"foofoo" uint8_t output[6]; heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 7, 7); memset(output, 0, sizeof(*output)); size_t count = 0; HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); ASSERT_EQ(HSDR_SINK_OK, sres); size_t out_sz = 0; (void)heatshrink_decoder_poll(hsd, output, 6, &out_sz); if (0) dump_buf("output", output, out_sz); ASSERT_EQ(6, out_sz); ASSERT_EQ('f', output[0]); ASSERT_EQ('o', output[1]); ASSERT_EQ('o', output[2]); ASSERT_EQ('f', output[3]); ASSERT_EQ('o', output[4]); ASSERT_EQ('o', output[5]); heatshrink_decoder_free(hsd); PASS(); } TEST decoder_poll_should_expand_short_self_overlapping_backref(void) { /* "aaaaa" == (literal, 1), ('a'), (backref, 1 back, 4 bytes) */ uint8_t input[] = {0xb0, 0x80, 0x01, 0x80}; uint8_t output[6]; uint8_t expected[] = {'a', 'a', 'a', 'a', 'a'}; heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 7); size_t count = 0; HSD_sink_res sres = heatshrink_decoder_sink(hsd, input, sizeof(input), &count); ASSERT_EQ(HSDR_SINK_OK, sres); size_t out_sz = 0; (void)heatshrink_decoder_poll(hsd, output, sizeof(output), &out_sz); if (0) dump_buf("output", output, out_sz); ASSERT_EQ(sizeof(expected), out_sz); for (size_t i=0; iwindow_sz2, cfg->lookahead_sz2); heatshrink_decoder *hsd = heatshrink_decoder_alloc(cfg->decoder_input_buffer_size, cfg->window_sz2, cfg->lookahead_sz2); size_t comp_sz = input_size + (input_size/2) + 4; size_t decomp_sz = input_size + (input_size/2) + 4; uint8_t *comp = malloc(comp_sz); uint8_t *decomp = malloc(decomp_sz); if (comp == NULL) FAILm("malloc fail"); if (decomp == NULL) FAILm("malloc fail"); memset(comp, 0, comp_sz); memset(decomp, 0, decomp_sz); size_t count = 0; if (cfg->log_lvl > 1) { printf("\n^^ COMPRESSING\n"); dump_buf("input", input, input_size); } size_t sunk = 0; size_t polled = 0; while (sunk < input_size) { ASSERT(heatshrink_encoder_sink(hse, &input[sunk], input_size - sunk, &count) >= 0); sunk += count; if (cfg->log_lvl > 1) printf("^^ sunk %zd\n", count); if (sunk == input_size) { ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); } HSE_poll_res pres; do { /* "turn the crank" */ pres = heatshrink_encoder_poll(hse, &comp[polled], comp_sz - polled, &count); ASSERT(pres >= 0); polled += count; if (cfg->log_lvl > 1) printf("^^ polled %zd\n", count); } while (pres == HSER_POLL_MORE); ASSERT_EQ(HSER_POLL_EMPTY, pres); if (polled >= comp_sz) FAILm("compression should never expand that much"); if (sunk == input_size) { ASSERT_EQ(HSER_FINISH_DONE, heatshrink_encoder_finish(hse)); } } if (cfg->log_lvl > 0) printf("in: %u compressed: %zu ", input_size, polled); size_t compressed_size = polled; sunk = 0; polled = 0; if (cfg->log_lvl > 1) { printf("\n^^ DECOMPRESSING\n"); dump_buf("comp", comp, compressed_size); } while (sunk < compressed_size) { ASSERT(heatshrink_decoder_sink(hsd, &comp[sunk], compressed_size - sunk, &count) >= 0); sunk += count; if (cfg->log_lvl > 1) printf("^^ sunk %zd\n", count); if (sunk == compressed_size) { ASSERT_EQ(HSDR_FINISH_MORE, heatshrink_decoder_finish(hsd)); } HSD_poll_res pres; do { pres = heatshrink_decoder_poll(hsd, &decomp[polled], decomp_sz - polled, &count); ASSERT(pres >= 0); ASSERT(count > 0); polled += count; if (cfg->log_lvl > 1) printf("^^ polled %zd\n", count); } while (pres == HSDR_POLL_MORE); ASSERT_EQ(HSDR_POLL_EMPTY, pres); if (sunk == compressed_size) { HSD_finish_res fres = heatshrink_decoder_finish(hsd); ASSERT_EQ(HSDR_FINISH_DONE, fres); } if (polled > input_size) { printf("\nExpected %zd, got %zu\n", (size_t)input_size, polled); FAILm("Decompressed data is larger than original input"); } } if (cfg->log_lvl > 0) printf("decompressed: %zu\n", polled); if (polled != input_size) { FAILm("Decompressed length does not match original input length"); } if (cfg->log_lvl > 1) dump_buf("decomp", decomp, polled); for (uint32_t i=0; i out[%d] == 0x%02x ('%c') %c\n", j, input[j], isprint(input[j]) ? input[j] : '.', j, decomp[j], isprint(decomp[j]) ? decomp[j] : '.', input[j] == decomp[j] ? ' ' : 'X'); } } } ASSERT_EQ(input[i], decomp[i]); } free(comp); free(decomp); heatshrink_encoder_free(hse); heatshrink_decoder_free(hsd); PASS(); } TEST data_without_duplication_should_match(void) { uint8_t input[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; cfg_info cfg; cfg.log_lvl = 0; cfg.window_sz2 = 8; cfg.lookahead_sz2 = 3; cfg.decoder_input_buffer_size = 256; return compress_and_expand_and_check(input, sizeof(input), &cfg); } TEST data_with_simple_repetition_should_compress_and_decompress_properly(void) { uint8_t input[] = {'a', 'b', 'c', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e', 'f', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; cfg_info cfg; cfg.log_lvl = 0; cfg.window_sz2 = 8; cfg.lookahead_sz2 = 3; cfg.decoder_input_buffer_size = 256; return compress_and_expand_and_check(input, sizeof(input), &cfg); } TEST data_without_duplication_should_match_with_absurdly_tiny_buffers(void) { heatshrink_encoder *hse = heatshrink_encoder_alloc(8, 3); heatshrink_decoder *hsd = heatshrink_decoder_alloc(256, 8, 3); uint8_t input[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; uint8_t comp[60]; uint8_t decomp[60]; size_t count = 0; int log = 0; if (log) dump_buf("input", input, sizeof(input)); for (uint32_t i=0; i= 0); } ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); size_t packed_count = 0; do { ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0); packed_count += count; } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE); if (log) dump_buf("comp", comp, packed_count); for (uint32_t i=0; i= 0); } for (uint32_t i=0; i= 0); } if (log) dump_buf("decomp", decomp, sizeof(input)); for (uint32_t i=0; i= 0); } ASSERT_EQ(HSER_FINISH_MORE, heatshrink_encoder_finish(hse)); size_t packed_count = 0; do { ASSERT(heatshrink_encoder_poll(hse, &comp[packed_count], 1, &count) >= 0); packed_count += count; } while (heatshrink_encoder_finish(hse) == HSER_FINISH_MORE); if (log) dump_buf("comp", comp, packed_count); for (uint32_t i=0; i= 0); } for (uint32_t i=0; i= 0); } if (log) dump_buf("decomp", decomp, sizeof(input)); for (uint32_t i=0; ilog_lvl > 0) { printf("\n-- size %u, seed %u, input buf %zu\n", size, seed, cfg->decoder_input_buffer_size); } fill_with_pseudorandom_letters(input, size, seed); return compress_and_expand_and_check(input, size, cfg); } TEST small_input_buffer_should_not_impact_decoder_correctness(void) { int size = 5; uint8_t input[size]; cfg_info cfg; cfg.log_lvl = 0; cfg.window_sz2 = 8; cfg.lookahead_sz2 = 3; cfg.decoder_input_buffer_size = 5; for (uint16_t i=0; i= 19901L printf("\n\nFuzzing (single-byte sizes):\n"); for (uint8_t lsize=3; lsize < 8; lsize++) { for (uint32_t size=1; size < 128*1024L; size <<= 1) { if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); for (uint16_t ibs=32; ibs<=8192; ibs <<= 1) { /* input buffer size */ if (GREATEST_IS_VERBOSE()) printf(" -- input buffer %u\n", ibs); for (uint32_t seed=1; seed<=10; seed++) { if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); cfg_info cfg; cfg.log_lvl = 0; cfg.window_sz2 = 8; cfg.lookahead_sz2 = lsize; cfg.decoder_input_buffer_size = ibs; RUN_TESTp(pseudorandom_data_should_match, size, seed, &cfg); } } } } printf("\nFuzzing (multi-byte sizes):\n"); for (uint8_t lsize=6; lsize < 9; lsize++) { for (uint32_t size=1; size < 128*1024L; size <<= 1) { if (GREATEST_IS_VERBOSE()) printf(" -- size %u\n", size); for (uint16_t ibs=32; ibs<=8192; ibs <<= 1) { /* input buffer size */ if (GREATEST_IS_VERBOSE()) printf(" -- input buffer %u\n", ibs); for (uint32_t seed=1; seed<=10; seed++) { if (GREATEST_IS_VERBOSE()) printf(" -- seed %u\n", seed); cfg_info cfg; cfg.log_lvl = 0; cfg.window_sz2 = 11; cfg.lookahead_sz2 = lsize; cfg.decoder_input_buffer_size = ibs; RUN_TESTp(pseudorandom_data_should_match, size, seed, &cfg); } } } } #endif } /* Add all the definitions that need to be in the test runner's main file. */ GREATEST_MAIN_DEFS(); int main(int argc, char **argv) { GREATEST_MAIN_BEGIN(); /* command-line arguments, initialization. */ RUN_SUITE(encoding); RUN_SUITE(decoding); RUN_SUITE(integration); #ifdef HEATSHRINK_HAS_THEFT RUN_SUITE(properties); #endif GREATEST_MAIN_END(); /* display results */ }