--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018 Intel Corporation
+ */
+#include <string.h>
+#include <zlib.h>
+#include <math.h>
+
+#include <rte_cycles.h>
+#include <rte_malloc.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include <rte_compressdev.h>
+
+#include "test_compressdev_test_buffer.h"
+#include "test.h"
+
+#define DEFAULT_WINDOW_SIZE 15
+#define DEFAULT_MEM_LEVEL 8
+#define MAX_DEQD_RETRIES 10
+#define DEQUEUE_WAIT_TIME 10000
+
+/*
+ * 30% extra size for compressed data compared to original data,
+ * in case data size cannot be reduced and it is actually bigger
+ * due to the compress block headers
+ */
+#define COMPRESS_BUF_SIZE_RATIO 1.3
+#define NUM_MBUFS 16
+#define NUM_OPS 16
+#define NUM_MAX_XFORMS 1
+#define NUM_MAX_INFLIGHT_OPS 128
+#define CACHE_SIZE 0
+
+const char *
+huffman_type_strings[] = {
+ [RTE_COMP_HUFFMAN_DEFAULT] = "PMD default",
+ [RTE_COMP_HUFFMAN_FIXED] = "Fixed",
+ [RTE_COMP_HUFFMAN_DYNAMIC] = "Dynamic"
+};
+
+enum zlib_direction {
+ ZLIB_NONE,
+ ZLIB_COMPRESS,
+ ZLIB_DECOMPRESS,
+ ZLIB_ALL
+};
+
+struct comp_testsuite_params {
+ struct rte_mempool *mbuf_pool;
+ struct rte_mempool *op_pool;
+ struct rte_comp_xform def_comp_xform;
+ struct rte_comp_xform def_decomp_xform;
+};
+
+static struct comp_testsuite_params testsuite_params = { 0 };
+
+static void
+testsuite_teardown(void)
+{
+ struct comp_testsuite_params *ts_params = &testsuite_params;
+
+ rte_mempool_free(ts_params->mbuf_pool);
+ rte_mempool_free(ts_params->op_pool);
+}
+
+static int
+testsuite_setup(void)
+{
+ struct comp_testsuite_params *ts_params = &testsuite_params;
+ unsigned int i;
+
+ if (rte_compressdev_count() == 0) {
+ RTE_LOG(ERR, USER1, "Need at least one compress device\n");
+ return TEST_FAILED;
+ }
+
+ uint32_t max_buf_size = 0;
+ for (i = 0; i < RTE_DIM(compress_test_bufs); i++)
+ max_buf_size = RTE_MAX(max_buf_size,
+ strlen(compress_test_bufs[i]) + 1);
+
+ max_buf_size *= COMPRESS_BUF_SIZE_RATIO;
+ /*
+ * Buffers to be used in compression and decompression.
+ * Since decompressed data might be larger than
+ * compressed data (due to block header),
+ * buffers should be big enough for both cases.
+ */
+ ts_params->mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool",
+ NUM_MBUFS,
+ CACHE_SIZE, 0,
+ max_buf_size + RTE_PKTMBUF_HEADROOM,
+ rte_socket_id());
+ if (ts_params->mbuf_pool == NULL) {
+ RTE_LOG(ERR, USER1, "Large mbuf pool could not be created\n");
+ return TEST_FAILED;
+ }
+
+ ts_params->op_pool = rte_comp_op_pool_create("op_pool", NUM_OPS,
+ 0, 0, rte_socket_id());
+ if (ts_params->op_pool == NULL) {
+ RTE_LOG(ERR, USER1, "Operation pool could not be created\n");
+ goto exit;
+ }
+
+ /* Initializes default values for compress/decompress xforms */
+ ts_params->def_comp_xform.type = RTE_COMP_COMPRESS;
+ ts_params->def_comp_xform.compress.algo = RTE_COMP_ALGO_DEFLATE,
+ ts_params->def_comp_xform.compress.deflate.huffman =
+ RTE_COMP_HUFFMAN_DEFAULT;
+ ts_params->def_comp_xform.compress.level = RTE_COMP_LEVEL_PMD_DEFAULT;
+ ts_params->def_comp_xform.compress.chksum = RTE_COMP_CHECKSUM_NONE;
+ ts_params->def_comp_xform.compress.window_size = DEFAULT_WINDOW_SIZE;
+
+ ts_params->def_decomp_xform.type = RTE_COMP_DECOMPRESS;
+ ts_params->def_decomp_xform.decompress.algo = RTE_COMP_ALGO_DEFLATE,
+ ts_params->def_decomp_xform.decompress.chksum = RTE_COMP_CHECKSUM_NONE;
+ ts_params->def_decomp_xform.decompress.window_size = DEFAULT_WINDOW_SIZE;
+
+ return TEST_SUCCESS;
+
+exit:
+ testsuite_teardown();
+
+ return TEST_FAILED;
+}
+
+static int
+generic_ut_setup(void)
+{
+ /* Configure compressdev (one device, one queue pair) */
+ struct rte_compressdev_config config = {
+ .socket_id = rte_socket_id(),
+ .nb_queue_pairs = 1,
+ .max_nb_priv_xforms = NUM_MAX_XFORMS,
+ .max_nb_streams = 0
+ };
+
+ if (rte_compressdev_configure(0, &config) < 0) {
+ RTE_LOG(ERR, USER1, "Device configuration failed\n");
+ return -1;
+ }
+
+ if (rte_compressdev_queue_pair_setup(0, 0, NUM_MAX_INFLIGHT_OPS,
+ rte_socket_id()) < 0) {
+ RTE_LOG(ERR, USER1, "Queue pair setup failed\n");
+ return -1;
+ }
+
+ if (rte_compressdev_start(0) < 0) {
+ RTE_LOG(ERR, USER1, "Device could not be started\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+generic_ut_teardown(void)
+{
+ rte_compressdev_stop(0);
+ if (rte_compressdev_close(0) < 0)
+ RTE_LOG(ERR, USER1, "Device could not be closed\n");
+}
+
+static int
+compare_buffers(const char *buffer1, uint32_t buffer1_len,
+ const char *buffer2, uint32_t buffer2_len)
+{
+ if (buffer1_len != buffer2_len) {
+ RTE_LOG(ERR, USER1, "Buffer lengths are different\n");
+ return -1;
+ }
+
+ if (memcmp(buffer1, buffer2, buffer1_len) != 0) {
+ RTE_LOG(ERR, USER1, "Buffers are different\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Maps compressdev and Zlib flush flags
+ */
+static int
+map_zlib_flush_flag(enum rte_comp_flush_flag flag)
+{
+ switch (flag) {
+ case RTE_COMP_FLUSH_NONE:
+ return Z_NO_FLUSH;
+ case RTE_COMP_FLUSH_SYNC:
+ return Z_SYNC_FLUSH;
+ case RTE_COMP_FLUSH_FULL:
+ return Z_FULL_FLUSH;
+ case RTE_COMP_FLUSH_FINAL:
+ return Z_FINISH;
+ /*
+ * There should be only the values above,
+ * so this should never happen
+ */
+ default:
+ return -1;
+ }
+}
+
+static int
+compress_zlib(struct rte_comp_op *op,
+ const struct rte_comp_xform *xform, int mem_level)
+{
+ z_stream stream;
+ int zlib_flush;
+ int strategy, window_bits, comp_level;
+ int ret = -1;
+
+ /* initialize zlib stream */
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ if (xform->compress.deflate.huffman == RTE_COMP_HUFFMAN_FIXED)
+ strategy = Z_FIXED;
+ else
+ strategy = Z_DEFAULT_STRATEGY;
+
+ /*
+ * Window bits is the base two logarithm of the window size (in bytes).
+ * When doing raw DEFLATE, this number will be negative.
+ */
+ window_bits = -(xform->compress.window_size);
+
+ comp_level = xform->compress.level;
+
+ if (comp_level != RTE_COMP_LEVEL_NONE)
+ ret = deflateInit2(&stream, comp_level, Z_DEFLATED,
+ window_bits, mem_level, strategy);
+ else
+ ret = deflateInit(&stream, Z_NO_COMPRESSION);
+
+ if (ret != Z_OK) {
+ printf("Zlib deflate could not be initialized\n");
+ goto exit;
+ }
+
+ /* Assuming stateless operation */
+ stream.avail_in = op->src.length;
+ stream.next_in = rte_pktmbuf_mtod(op->m_src, uint8_t *);
+ stream.avail_out = op->m_dst->data_len;
+ stream.next_out = rte_pktmbuf_mtod(op->m_dst, uint8_t *);
+
+ /* Stateless operation, all buffer will be compressed in one go */
+ zlib_flush = map_zlib_flush_flag(op->flush_flag);
+ ret = deflate(&stream, zlib_flush);
+
+ if (stream.avail_in != 0) {
+ RTE_LOG(ERR, USER1, "Buffer could not be read entirely\n");
+ goto exit;
+ }
+
+ if (ret != Z_STREAM_END)
+ goto exit;
+
+ op->consumed = op->src.length - stream.avail_in;
+ op->produced = op->m_dst->data_len - stream.avail_out;
+ op->status = RTE_COMP_OP_STATUS_SUCCESS;
+
+ deflateReset(&stream);
+
+ ret = 0;
+exit:
+ deflateEnd(&stream);
+
+ return ret;
+}
+
+static int
+decompress_zlib(struct rte_comp_op *op,
+ const struct rte_comp_xform *xform)
+{
+ z_stream stream;
+ int window_bits;
+ int zlib_flush;
+ int ret = TEST_FAILED;
+
+ /* initialize zlib stream */
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+
+ /*
+ * Window bits is the base two logarithm of the window size (in bytes).
+ * When doing raw DEFLATE, this number will be negative.
+ */
+ window_bits = -(xform->decompress.window_size);
+
+ ret = inflateInit2(&stream, window_bits);
+
+ if (ret != Z_OK) {
+ printf("Zlib deflate could not be initialized\n");
+ goto exit;
+ }
+
+ /* Assuming stateless operation */
+ stream.avail_in = op->src.length;
+ stream.next_in = rte_pktmbuf_mtod(op->m_src, uint8_t *);
+ stream.avail_out = op->m_dst->data_len;
+ stream.next_out = rte_pktmbuf_mtod(op->m_dst, uint8_t *);
+
+ /* Stateless operation, all buffer will be compressed in one go */
+ zlib_flush = map_zlib_flush_flag(op->flush_flag);
+ ret = inflate(&stream, zlib_flush);
+
+ if (stream.avail_in != 0) {
+ RTE_LOG(ERR, USER1, "Buffer could not be read entirely\n");
+ goto exit;
+ }
+
+ if (ret != Z_STREAM_END)
+ goto exit;
+
+ op->consumed = op->src.length - stream.avail_in;
+ op->produced = op->m_dst->data_len - stream.avail_out;
+ op->status = RTE_COMP_OP_STATUS_SUCCESS;
+
+ inflateReset(&stream);
+
+ ret = 0;
+exit:
+ inflateEnd(&stream);
+
+ return ret;
+}
+
+/*
+ * Compresses and decompresses buffer with compressdev API and Zlib API
+ */
+static int
+test_deflate_comp_decomp(const char *test_buffer,
+ struct rte_comp_xform *compress_xform,
+ struct rte_comp_xform *decompress_xform,
+ enum rte_comp_op_type state,
+ enum zlib_direction zlib_dir)
+{
+ struct comp_testsuite_params *ts_params = &testsuite_params;
+ int ret_status = -1;
+ int ret;
+ struct rte_mbuf *comp_buf = NULL;
+ struct rte_mbuf *uncomp_buf = NULL;
+ struct rte_comp_op *op = NULL;
+ struct rte_comp_op *op_processed = NULL;
+ void *priv_xform = NULL;
+ uint16_t num_deqd;
+ unsigned int deqd_retries = 0;
+ char *data_ptr;
+
+ /* Prepare the source mbuf with the data */
+ uncomp_buf = rte_pktmbuf_alloc(ts_params->mbuf_pool);
+ if (uncomp_buf == NULL) {
+ RTE_LOG(ERR, USER1,
+ "Source mbuf could not be allocated "
+ "from the mempool\n");
+ goto exit;
+ }
+
+ data_ptr = rte_pktmbuf_append(uncomp_buf, strlen(test_buffer) + 1);
+ snprintf(data_ptr, strlen(test_buffer) + 1, "%s", test_buffer);
+
+ /* Prepare the destination mbuf */
+ comp_buf = rte_pktmbuf_alloc(ts_params->mbuf_pool);
+ if (comp_buf == NULL) {
+ RTE_LOG(ERR, USER1,
+ "Destination mbuf could not be allocated "
+ "from the mempool\n");
+ goto exit;
+ }
+
+ rte_pktmbuf_append(comp_buf,
+ strlen(test_buffer) * COMPRESS_BUF_SIZE_RATIO);
+
+ /* Build the compression operations */
+ op = rte_comp_op_alloc(ts_params->op_pool);
+ if (op == NULL) {
+ RTE_LOG(ERR, USER1,
+ "Compress operation could not be allocated "
+ "from the mempool\n");
+ goto exit;
+ }
+
+ op->m_src = uncomp_buf;
+ op->m_dst = comp_buf;
+ op->src.offset = 0;
+ op->src.length = rte_pktmbuf_pkt_len(uncomp_buf);
+ op->dst.offset = 0;
+ if (state == RTE_COMP_OP_STATELESS) {
+ op->flush_flag = RTE_COMP_FLUSH_FINAL;
+ } else {
+ RTE_LOG(ERR, USER1,
+ "Stateful operations are not supported "
+ "in these tests yet\n");
+ goto exit;
+ }
+ op->input_chksum = 0;
+
+ /* Compress data (either with Zlib API or compressdev API */
+ if (zlib_dir == ZLIB_COMPRESS || zlib_dir == ZLIB_ALL) {
+ ret = compress_zlib(op,
+ (const struct rte_comp_xform *)&compress_xform,
+ DEFAULT_MEM_LEVEL);
+ if (ret < 0)
+ goto exit;
+
+ op_processed = op;
+ } else {
+ /* Create compress xform private data */
+ ret = rte_compressdev_private_xform_create(0,
+ (const struct rte_comp_xform *)compress_xform,
+ &priv_xform);
+ if (ret < 0) {
+ RTE_LOG(ERR, USER1,
+ "Compression private xform "
+ "could not be created\n");
+ goto exit;
+ }
+
+ /* Attach xform private data to operation */
+ op->private_xform = priv_xform;
+
+ /* Enqueue and dequeue all operations */
+ ret = rte_compressdev_enqueue_burst(0, 0, &op, 1);
+ if (ret == 0) {
+ RTE_LOG(ERR, USER1,
+ "The operation could not be enqueued\n");
+ goto exit;
+ }
+ do {
+ /*
+ * If retrying a dequeue call, wait for 10 ms to allow
+ * enough time to the driver to process the operations
+ */
+ if (deqd_retries != 0) {
+ /*
+ * Avoid infinite loop if not all the
+ * operations get out of the device
+ */
+ if (deqd_retries == MAX_DEQD_RETRIES) {
+ RTE_LOG(ERR, USER1,
+ "Not all operations could be "
+ "dequeued\n");
+ goto exit;
+ }
+ usleep(DEQUEUE_WAIT_TIME);
+ }
+ num_deqd = rte_compressdev_dequeue_burst(0, 0,
+ &op_processed, 1);
+
+ deqd_retries++;
+ } while (num_deqd < 1);
+
+ deqd_retries = 0;
+
+ /* Free compress private xform */
+ rte_compressdev_private_xform_free(0, priv_xform);
+ priv_xform = NULL;
+ }
+
+ enum rte_comp_huffman huffman_type =
+ compress_xform->compress.deflate.huffman;
+ RTE_LOG(DEBUG, USER1, "Buffer compressed from %u to %u bytes "
+ "(level = %u, huffman = %s)\n",
+ op_processed->consumed, op_processed->produced,
+ compress_xform->compress.level,
+ huffman_type_strings[huffman_type]);
+ RTE_LOG(DEBUG, USER1, "Compression ratio = %.2f",
+ (float)op_processed->produced /
+ op_processed->consumed * 100);
+ op = NULL;
+
+ /*
+ * Check operation status and free source mbuf (destination mbuf and
+ * compress operation information is needed for the decompression stage)
+ */
+ if (op_processed->status != RTE_COMP_OP_STATUS_SUCCESS) {
+ RTE_LOG(ERR, USER1,
+ "Some operations were not successful\n");
+ goto exit;
+ }
+ rte_pktmbuf_free(uncomp_buf);
+ uncomp_buf = NULL;
+
+ /* Allocate buffer for decompressed data */
+ uncomp_buf = rte_pktmbuf_alloc(ts_params->mbuf_pool);
+ if (uncomp_buf == NULL) {
+ RTE_LOG(ERR, USER1,
+ "Destination mbuf could not be allocated "
+ "from the mempool\n");
+ goto exit;
+ }
+
+ rte_pktmbuf_append(uncomp_buf, strlen(test_buffer) + 1);
+
+ /* Build the decompression operations */
+ op = rte_comp_op_alloc(ts_params->op_pool);
+ if (op == NULL) {
+ RTE_LOG(ERR, USER1,
+ "Decompress operation could not be allocated "
+ "from the mempool\n");
+ goto exit;
+ }
+
+ /* Source buffer is the compressed data from the previous operation */
+ op->m_src = op_processed->m_dst;
+ op->m_dst = uncomp_buf;
+ op->src.offset = 0;
+ /*
+ * Set the length of the compressed data to the
+ * number of bytes that were produced in the previous stage
+ */
+ op->src.length = op_processed->produced;
+ op->dst.offset = 0;
+ if (state == RTE_COMP_OP_STATELESS) {
+ op->flush_flag = RTE_COMP_FLUSH_FINAL;
+ } else {
+ RTE_LOG(ERR, USER1,
+ "Stateful operations are not supported "
+ "in these tests yet\n");
+ goto exit;
+ }
+ op->input_chksum = 0;
+
+ /*
+ * Free the previous compress operation,
+ * as it is not needed anymore
+ */
+ rte_comp_op_free(op_processed);
+ op_processed = NULL;
+
+ /* Decompress data (either with Zlib API or compressdev API */
+ if (zlib_dir == ZLIB_DECOMPRESS || zlib_dir == ZLIB_ALL) {
+ ret = decompress_zlib(op,
+ (const struct rte_comp_xform *)&decompress_xform);
+ if (ret < 0)
+ goto exit;
+
+ op_processed = op;
+ } else {
+ num_deqd = 0;
+ /* Create decompress xform private data */
+ ret = rte_compressdev_private_xform_create(0,
+ (const struct rte_comp_xform *)decompress_xform,
+ &priv_xform);
+ if (ret < 0) {
+ RTE_LOG(ERR, USER1,
+ "Decompression private xform "
+ "could not be created\n");
+ goto exit;
+ }
+
+ /* Attach xform private data to operation */
+ op->private_xform = priv_xform;
+
+ /* Enqueue and dequeue all operations */
+ ret = rte_compressdev_enqueue_burst(0, 0, &op, 1);
+ if (ret == 0) {
+ RTE_LOG(ERR, USER1,
+ "The operation could not be enqueued\n");
+ goto exit;
+ }
+ do {
+ /*
+ * If retrying a dequeue call, wait for 10 ms to allow
+ * enough time to the driver to process the operations
+ */
+ if (deqd_retries != 0) {
+ /*
+ * Avoid infinite loop if not all the
+ * operations get out of the device
+ */
+ if (deqd_retries == MAX_DEQD_RETRIES) {
+ RTE_LOG(ERR, USER1,
+ "Not all operations could be "
+ "dequeued\n");
+ goto exit;
+ }
+ usleep(DEQUEUE_WAIT_TIME);
+ }
+ num_deqd = rte_compressdev_dequeue_burst(0, 0,
+ &op_processed, 1);
+
+ deqd_retries++;
+ } while (num_deqd < 1);
+ }
+
+ RTE_LOG(DEBUG, USER1, "Buffer decompressed from %u to %u bytes\n",
+ op_processed->consumed, op_processed->produced);
+ op = NULL;
+ /*
+ * Check operation status and free source mbuf (destination mbuf and
+ * compress operation information is still needed)
+ */
+ if (op_processed->status != RTE_COMP_OP_STATUS_SUCCESS) {
+ RTE_LOG(ERR, USER1,
+ "Some operations were not successful\n");
+ goto exit;
+ }
+ rte_pktmbuf_free(comp_buf);
+ comp_buf = NULL;
+
+ /*
+ * Compare the original stream with the decompressed stream
+ * (in size and the data)
+ */
+ if (compare_buffers(test_buffer, strlen(test_buffer) + 1,
+ rte_pktmbuf_mtod(op_processed->m_dst, const char *),
+ op_processed->produced) < 0)
+ goto exit;
+
+ ret_status = 0;
+
+exit:
+ /* Free resources */
+ rte_pktmbuf_free(uncomp_buf);
+ rte_pktmbuf_free(comp_buf);
+ rte_comp_op_free(op);
+ rte_comp_op_free(op_processed);
+
+ if (priv_xform != NULL)
+ rte_compressdev_private_xform_free(0, priv_xform);
+
+ return ret_status;
+}
+
+static int
+test_compressdev_deflate_stateless_fixed(void)
+{
+ struct comp_testsuite_params *ts_params = &testsuite_params;
+ const char *test_buffer;
+ uint16_t i;
+ struct rte_comp_xform compress_xform;
+
+ memcpy(&compress_xform, &ts_params->def_comp_xform,
+ sizeof(struct rte_comp_xform));
+ compress_xform.compress.deflate.huffman = RTE_COMP_HUFFMAN_FIXED;
+
+ for (i = 0; i < RTE_DIM(compress_test_bufs); i++) {
+ test_buffer = compress_test_bufs[i];
+
+ /* Compress with compressdev, decompress with Zlib */
+ if (test_deflate_comp_decomp(test_buffer,
+ &compress_xform,
+ &ts_params->def_decomp_xform,
+ RTE_COMP_OP_STATELESS,
+ ZLIB_DECOMPRESS) < 0)
+ return TEST_FAILED;
+
+ /* Compress with Zlib, decompress with compressdev */
+ if (test_deflate_comp_decomp(test_buffer,
+ &compress_xform,
+ &ts_params->def_decomp_xform,
+ RTE_COMP_OP_STATELESS,
+ ZLIB_COMPRESS) < 0)
+ return TEST_FAILED;
+ }
+
+ return TEST_SUCCESS;
+}
+
+static int
+test_compressdev_deflate_stateless_dynamic(void)
+{
+ struct comp_testsuite_params *ts_params = &testsuite_params;
+ const char *test_buffer;
+ uint16_t i;
+ struct rte_comp_xform compress_xform;
+
+ memcpy(&compress_xform, &ts_params->def_comp_xform,
+ sizeof(struct rte_comp_xform));
+ compress_xform.compress.deflate.huffman = RTE_COMP_HUFFMAN_DYNAMIC;
+
+ for (i = 0; i < RTE_DIM(compress_test_bufs); i++) {
+ test_buffer = compress_test_bufs[i];
+
+ /* Compress with compressdev, decompress with Zlib */
+ if (test_deflate_comp_decomp(test_buffer,
+ &compress_xform,
+ &ts_params->def_decomp_xform,
+ RTE_COMP_OP_STATELESS,
+ ZLIB_DECOMPRESS) < 0)
+ return TEST_FAILED;
+
+ /* Compress with Zlib, decompress with compressdev */
+ if (test_deflate_comp_decomp(test_buffer,
+ &compress_xform,
+ &ts_params->def_decomp_xform,
+ RTE_COMP_OP_STATELESS,
+ ZLIB_COMPRESS) < 0)
+ return TEST_FAILED;
+ }
+
+ return TEST_SUCCESS;
+}
+
+static struct unit_test_suite compressdev_testsuite = {
+ .suite_name = "compressdev unit test suite",
+ .setup = testsuite_setup,
+ .teardown = testsuite_teardown,
+ .unit_test_cases = {
+ TEST_CASE_ST(generic_ut_setup, generic_ut_teardown,
+ test_compressdev_deflate_stateless_fixed),
+ TEST_CASE_ST(generic_ut_setup, generic_ut_teardown,
+ test_compressdev_deflate_stateless_dynamic),
+ TEST_CASES_END() /**< NULL terminate unit test array */
+ }
+};
+
+static int
+test_compressdev(void)
+{
+ return unit_test_suite_runner(&compressdev_testsuite);
+}
+
+REGISTER_TEST_COMMAND(compressdev_autotest, test_compressdev);
--- /dev/null
+#ifndef TEST_COMPRESSDEV_TEST_BUFFERS_H_
+#define TEST_COMPRESSDEV_TEST_BUFFERS_H_
+
+/*
+ * These test buffers are snippets obtained
+ * from the Canterbury and Calgary Corpus
+ * collection.
+ */
+
+/* Snippet of Alice's Adventures in Wonderland */
+static const char test_buf_alice[] =
+ " Alice was beginning to get very tired of sitting by her sister\n"
+ "on the bank, and of having nothing to do: once or twice she had\n"
+ "peeped into the book her sister was reading, but it had no\n"
+ "pictures or conversations in it, `and what is the use of a book,'\n"
+ "thought Alice `without pictures or conversation?'\n\n"
+ " So she was considering in her own mind (as well as she could,\n"
+ "for the hot day made her feel very sleepy and stupid), whether\n"
+ "the pleasure of making a daisy-chain would be worth the trouble\n"
+ "of getting up and picking the daisies, when suddenly a White\n"
+ "Rabbit with pink eyes ran close by her.\n\n"
+ " There was nothing so VERY remarkable in that; nor did Alice\n"
+ "think it so VERY much out of the way to hear the Rabbit say to\n"
+ "itself, `Oh dear! Oh dear! I shall be late!' (when she thought\n"
+ "it over afterwards, it occurred to her that she ought to have\n"
+ "wondered at this, but at the time it all seemed quite natural);\n"
+ "but when the Rabbit actually TOOK A WATCH OUT OF ITS WAISTCOAT-\n"
+ "POCKET, and looked at it, and then hurried on, Alice started to\n"
+ "her feet, for it flashed across her mind that she had never\n"
+ "before seen a rabbit with either a waistcoat-pocket, or a watch to\n"
+ "take out of it, and burning with curiosity, she ran across the\n"
+ "field after it, and fortunately was just in time to see it pop\n"
+ "down a large rabbit-hole under the hedge.\n\n"
+ " In another moment down went Alice after it, never once\n"
+ "considering how in the world she was to get out again.\n\n"
+ " The rabbit-hole went straight on like a tunnel for some way,\n"
+ "and then dipped suddenly down, so suddenly that Alice had not a\n"
+ "moment to think about stopping herself before she found herself\n"
+ "falling down a very deep well.\n\n"
+ " Either the well was very deep, or she fell very slowly, for she\n"
+ "had plenty of time as she went down to look about her and to\n"
+ "wonder what was going to happen next. First, she tried to look\n"
+ "down and make out what she was coming to, but it was too dark to\n"
+ "see anything; then she looked at the sides of the well, and\n"
+ "noticed that they were filled with cupboards and book-shelves;\n"
+ "here and there she saw maps and pictures hung upon pegs. She\n"
+ "took down a jar from one of the shelves as she passed; it was\n"
+ "labelled `ORANGE MARMALADE', but to her great disappointment it\n"
+ "was empty: she did not like to drop the jar for fear of killing\n"
+ "somebody, so managed to put it into one of the cupboards as she\n"
+ "fell past it.\n\n"
+ " `Well!' thought Alice to herself, `after such a fall as this, I\n"
+ "shall think nothing of tumbling down stairs! How brave they'll\n"
+ "all think me at home! Why, I wouldn't say anything about it,\n"
+ "even if I fell off the top of the house!' (Which was very likely\n"
+ "true.)\n\n"
+ " Down, down, down. Would the fall NEVER come to an end! `I\n"
+ "wonder how many miles I've fallen by this time?' she said aloud.\n"
+ "`I must be getting somewhere near the centre of the earth. Let\n"
+ "me see: that would be four thousand miles down, I think--' (for,\n"
+ "you see, Alice had learnt several things of this sort in her\n"
+ "lessons in the schoolroom, and though this was not a VERY good\n"
+ "opportunity for showing off her knowledge, as there was no one to\n"
+ "listen to her, still it was good practice to say it over) `--yes,\n"
+ "that's about the right distance--but then I wonder what Latitude\n"
+ "or Longitude I've got to?' (Alice had no idea what Latitude was,\n"
+ "or Longitude either, but thought they were nice grand words to\n"
+ "say.)\n\n"
+ " Presently she began again. `I wonder if I shall fall right\n"
+ "THROUGH the earth! How funny it'll seem to come out among the\n"
+ "people that walk with their heads downward! The Antipathies, I\n"
+ "think--' (she was rather glad there WAS no one listening, this\n"
+ "time, as it didn't sound at all the right word) `--but I shall\n"
+ "have to ask them what the name of the country is, you know.\n"
+ "Please, Ma'am, is this New Zealand or Australia?' (and she tried\n"
+ "to curtsey as she spoke--fancy CURTSEYING as you're falling\n"
+ "through the air! Do you think you could manage it?) `And what\n"
+ "an ignorant little girl she'll think me for asking! No, it'll\n"
+ "never do to ask: perhaps I shall see it written up somewhere.'\n"
+ " Down, down, down. There was nothing else to do, so Alice soon\n"
+ "began talking again. `Dinah'll miss me very much to-night, I\n"
+ "should think!' (Dinah was the cat.) `I hope they'll remember\n"
+ "her saucer of milk at tea-time. Dinah my dear! I wish you were\n"
+ "down here with me! There are no mice in the air, I'm afraid, but\n"
+ "you might catch a bat, and that's very like a mouse, you know.\n"
+ "But do cats eat bats, I wonder?' And here Alice began to get\n"
+ "rather sleepy, and went on saying to herself, in a dreamy sort of\n"
+ "way, `Do cats eat bats? Do cats eat bats?' and sometimes, `Do\n"
+ "bats eat cats?' for, you see, as she couldn't answer either\n"
+ "question, it didn't much matter which way she put it. She felt\n"
+ "that she was dozing off, and had just begun to dream that she\n"
+ "was walking hand in hand with Dinah, and saying to her very\n"
+ "earnestly, `Now, Dinah, tell me the truth: did you ever eat a\n"
+ "bat?' when suddenly, thump! thump! down she came upon a heap of\n"
+ "sticks and dry leaves, and the fall was over.\n\n";
+
+/* Snippet of Shakespeare play */
+static const char test_buf_shakespeare[] =
+ "CHARLES wrestler to Frederick.\n"
+ "\n"
+ "\n"
+ "OLIVER |\n"
+ " |\n"
+ "JAQUES (JAQUES DE BOYS:) | sons of Sir Rowland de Boys.\n"
+ " |\n"
+ "ORLANDO |\n"
+ "\n"
+ "\n"
+ "ADAM |\n"
+ " | servants to Oliver.\n"
+ "DENNIS |\n"
+ "\n"
+ "\n"
+ "TOUCHSTONE a clown.\n"
+ "\n"
+ "SIR OLIVER MARTEXT a vicar.\n"
+ "\n"
+ "\n"
+ "CORIN |\n"
+ " | shepherds.\n"
+ "SILVIUS |\n"
+ "\n"
+ "\n"
+ "WILLIAM a country fellow in love with Audrey.\n"
+ "\n"
+ " A person representing HYMEN. (HYMEN:)\n"
+ "\n"
+ "ROSALIND daughter to the banished duke.\n"
+ "\n"
+ "CELIA daughter to Frederick.\n"
+ "\n"
+ "PHEBE a shepherdess.\n"
+ "\n"
+ "AUDREY a country wench.\n"
+ "\n"
+ " Lords, pages, and attendants, &c.\n"
+ " (Forester:)\n"
+ " (A Lord:)\n"
+ " (First Lord:)\n"
+ " (Second Lord:)\n"
+ " (First Page:)\n"
+ " (Second Page:)\n"
+ "\n"
+ "\n"
+ "SCENE Oliver's house; Duke Frederick's court; and the\n"
+ " Forest of Arden.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " AS YOU LIKE IT\n"
+ "\n"
+ "\n"
+ "ACT I\n"
+ "\n"
+ "\n"
+ "\n"
+ "SCENE I Orchard of Oliver's house.\n"
+ "\n"
+ "\n"
+ " [Enter ORLANDO and ADAM]\n"
+ "\n"
+ "ORLANDO As I remember, Adam, it was upon this fashion\n"
+ " bequeathed me by will but poor a thousand crowns,\n"
+ " and, as thou sayest, charged my brother, on his\n"
+ " blessing, to breed me well: and there begins my\n"
+ " sadness. My brother Jaques he keeps at school, and\n"
+ " report speaks goldenly of his profit: for my part,\n"
+ " he keeps me rustically at home, or, to speak more\n"
+ " properly, stays me here at home unkept; for call you\n"
+ " that keeping for a gentleman of my birth, that\n"
+ " differs not from the stalling of an ox? His horses\n"
+ " are bred better; for, besides that they are fair\n"
+ " with their feeding, they are taught their manage,\n"
+ " and to that end riders dearly hired: but I, his\n"
+ " brother, gain nothing under him but growth; for the\n"
+ " which his animals on his dunghills are as much\n"
+ " bound to him as I. Besides this nothing that he so\n"
+ " plentifully gives me, the something that nature gave\n"
+ " me his countenance seems to take from me: he lets\n"
+ " me feed with his hinds, bars me the place of a\n"
+ " brother, and, as much as in him lies, mines my\n"
+ " gentility with my education. This is it, Adam, that\n"
+ " grieves me; and the spirit of my father, which I\n"
+ " think is within me, begins to mutiny against this\n"
+ " servitude: I will no longer endure it, though yet I\n"
+ " know no wise remedy how to avoid it.\n"
+ "\n"
+ "ADAM Yonder comes my master, your brother.\n"
+ "\n"
+ "ORLANDO Go apart, Adam, and thou shalt hear how he will\n";
+
+/* Snippet of source code in Pascal */
+static const char test_buf_pascal[] =
+ " Ptr = 1..DMem;\n"
+ " Loc = 1..IMem;\n"
+ " Loc0 = 0..IMem;\n"
+ " EdgeT = (hout,lin,hin,lout); {Warning this order is important in}\n"
+ " {predicates such as gtS,geS}\n"
+ " CardT = (finite,infinite);\n"
+ " ExpT = Minexp..Maxexp;\n"
+ " ManT = Mininf..Maxinf; \n"
+ " Pflag = (PNull,PSoln,PTrace,PPrint);\n"
+ " Sreal = record\n"
+ " edge:EdgeT;\n"
+ " cardinality:CardT;\n"
+ " exp:ExpT; {exponent}\n"
+ " mantissa:ManT;\n"
+ " end;\n"
+ " Int = record\n"
+ " hi:Sreal;\n"
+ " lo:Sreal;\n"
+ " end;\n"
+ " Instr = record\n"
+ " Code:OpType;\n"
+ " Pars: array[0..Par] of 0..DMem;\n"
+ " end;\n"
+ " DataMem= record\n"
+ " D :array [Ptr] of Int;\n"
+ " S :array [Loc] of State;\n"
+ " LastHalve:Loc;\n"
+ " RHalve :array [Loc] of real;\n"
+ " end;\n"
+ " DataFlags=record\n"
+ " PF :array [Ptr] of Pflag;\n"
+ " end;\n"
+ "var\n"
+ " Debug : (none,activity,post,trace,dump);\n"
+ " Cut : (once,all);\n"
+ " GlobalEnd,Verifiable:boolean;\n"
+ " HalveThreshold:real;\n"
+ " I : array [Loc] of Instr; {Memory holding instructions}\n"
+ " End : Loc; {last instruction in I}\n"
+ " ParN : array [OpType] of -1..Par; {number of parameters for each \n"
+ " opcode. -1 means no result}\n"
+ " ParIntersect : array [OpType] of boolean ;\n"
+ " DInit : DataMem; {initial memory which is cleared and \n"
+ " used in first call}\n"
+ " DF : DataFlags; {hold flags for variables, e.g. print/trace}\n"
+ " MaxDMem:0..DMem;\n"
+ " Shift : array[0..Digits] of 1..maxint;{array of constant multipliers}\n"
+ " {used for alignment etc.}\n"
+ " Dummy :Positive;\n"
+ " {constant intervals and Sreals}\n"
+ " PlusInfS,MinusInfS,PlusSmallS,MinusSmallS,ZeroS,\n"
+ " PlusFiniteS,MinusFiniteS:Sreal;\n"
+ " Zero,All,AllFinite:Int;\n"
+ "\n"
+ "procedure deblank;\n"
+ "var Ch:char;\n"
+ "begin\n"
+ " while (not eof) and (input^ in [' ',' ']) do read(Ch);\n"
+ "end;\n"
+ "\n"
+ "procedure InitialOptions;\n"
+ "\n"
+ "#include '/user/profs/cleary/bin/options.i';\n"
+ "\n"
+ " procedure Option;\n"
+ " begin\n"
+ " case Opt of\n"
+ " 'a','A':Debug:=activity;\n"
+ " 'd','D':Debug:=dump;\n"
+ " 'h','H':HalveThreshold:=StringNum/100;\n"
+ " 'n','N':Debug:=none;\n"
+ " 'p','P':Debug:=post;\n"
+ " 't','T':Debug:=trace;\n"
+ " 'v','V':Verifiable:=true;\n"
+ " end;\n"
+ " end;\n"
+ "\n"
+ "begin\n"
+ " Debug:=trace;\n"
+ " Verifiable:=false;\n"
+ " HalveThreshold:=67/100;\n"
+ " Options;\n"
+ " writeln(Debug);\n"
+ " writeln('Verifiable:',Verifiable);\n"
+ " writeln('Halve threshold',HalveThreshold);\n"
+ "end;{InitialOptions}\n"
+ "\n"
+ "procedure NormalizeUp(E,M:integer;var S:Sreal;var Closed:boolean);\n"
+ "begin\n"
+ "with S do\n"
+ "begin\n"
+ " if M=0 then S:=ZeroS else\n"
+ " if M>0 then\n";
+
+static const char * const compress_test_bufs[] = {
+ test_buf_alice,
+ test_buf_shakespeare,
+ test_buf_pascal
+};
+
+#endif /* TEST_COMPRESSDEV_TEST_BUFFERS_H_ */