--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2015 Mirantis, Inc.
+ *
+ * Author: Alyona Kiseleva <akiselyova@mirantis.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include "gtest/gtest.h"
+#include "common/config.h"
+#include "compressor/Compressor.h"
+#include "compressor/CompressionPlugin.h"
+#include "global/global_context.h"
+
+class CompressorTest : public ::testing::Test,
+ public ::testing::WithParamInterface<const char*> {
+public:
+ std::string plugin;
+ CompressorRef compressor;
+ bool old_zlib_isal;
+
+ CompressorTest() {
+ // note for later
+ old_zlib_isal = g_conf->compressor_zlib_isal;
+
+ plugin = GetParam();
+ size_t pos = plugin.find('/');
+ if (pos != std::string::npos) {
+ string isal = plugin.substr(pos + 1);
+ plugin = plugin.substr(0, pos);
+ if (isal == "isal") {
+ g_conf->set_val("compressor_zlib_isal", "true");
+ g_ceph_context->_conf->apply_changes(NULL);
+ } else if (isal == "noisal") {
+ g_conf->set_val("compressor_zlib_isal", "false");
+ g_ceph_context->_conf->apply_changes(NULL);
+ } else {
+ assert(0 == "bad option");
+ }
+ }
+ cout << "[plugin " << plugin << " (" << GetParam() << ")]" << std::endl;
+ }
+ ~CompressorTest() override {
+ g_conf->set_val("compressor_zlib_isal", old_zlib_isal ? "true" : "false");
+ g_ceph_context->_conf->apply_changes(NULL);
+ }
+
+ void SetUp() override {
+ compressor = Compressor::create(g_ceph_context, plugin);
+ ASSERT_TRUE(compressor);
+ }
+ void TearDown() override {
+ compressor.reset();
+ }
+};
+
+TEST_P(CompressorTest, load_plugin)
+{
+}
+
+TEST_P(CompressorTest, small_round_trip)
+{
+ bufferlist orig;
+ orig.append("This is a short string. There are many strings like it but this one is mine.");
+ bufferlist compressed;
+ int r = compressor->compress(orig, compressed);
+ ASSERT_EQ(0, r);
+ bufferlist decompressed;
+ r = compressor->decompress(compressed, decompressed);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(decompressed.length(), orig.length());
+ ASSERT_TRUE(decompressed.contents_equal(orig));
+ cout << "orig " << orig.length() << " compressed " << compressed.length()
+ << " with " << GetParam() << std::endl;
+}
+
+TEST_P(CompressorTest, big_round_trip_repeated)
+{
+ unsigned len = 1048576 * 4;
+ bufferlist orig;
+ while (orig.length() < len) {
+ orig.append("This is a short string. There are many strings like it but this one is mine.");
+ }
+ bufferlist compressed;
+ int r = compressor->compress(orig, compressed);
+ ASSERT_EQ(0, r);
+ bufferlist decompressed;
+ r = compressor->decompress(compressed, decompressed);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(decompressed.length(), orig.length());
+ ASSERT_TRUE(decompressed.contents_equal(orig));
+ cout << "orig " << orig.length() << " compressed " << compressed.length()
+ << " with " << GetParam() << std::endl;
+}
+
+TEST_P(CompressorTest, big_round_trip_randomish)
+{
+ unsigned len = 1048576 * 10;//269;
+ bufferlist orig;
+ const char *alphabet = "abcdefghijklmnopqrstuvwxyz";
+ if (false) {
+ while (orig.length() < len) {
+ orig.append(alphabet[rand() % 10]);
+ }
+ } else {
+ bufferptr bp(len);
+ char *p = bp.c_str();
+ for (unsigned i=0; i<len; ++i) {
+ p[i] = alphabet[rand() % 10];
+ }
+ orig.append(bp);
+ }
+ bufferlist compressed;
+ int r = compressor->compress(orig, compressed);
+ ASSERT_EQ(0, r);
+ bufferlist decompressed;
+ r = compressor->decompress(compressed, decompressed);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(decompressed.length(), orig.length());
+ ASSERT_TRUE(decompressed.contents_equal(orig));
+ cout << "orig " << orig.length() << " compressed " << compressed.length()
+ << " with " << GetParam() << std::endl;
+}
+
+#if 0
+TEST_P(CompressorTest, big_round_trip_file)
+{
+ bufferlist orig;
+ int fd = ::open("bin/ceph-osd", O_RDONLY);
+ struct stat st;
+ ::fstat(fd, &st);
+ orig.read_fd(fd, st.st_size);
+
+ bufferlist compressed;
+ int r = compressor->compress(orig, compressed);
+ ASSERT_EQ(0, r);
+ bufferlist decompressed;
+ r = compressor->decompress(compressed, decompressed);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(decompressed.length(), orig.length());
+ ASSERT_TRUE(decompressed.contents_equal(orig));
+ cout << "orig " << orig.length() << " compressed " << compressed.length()
+ << " with " << GetParam() << std::endl;
+}
+#endif
+
+
+TEST_P(CompressorTest, compress_decompress)
+{
+ const char* test = "This is test text";
+ int res;
+ int len = strlen(test);
+ bufferlist in, out;
+ bufferlist after;
+ bufferlist exp;
+ in.append(test, len);
+ res = compressor->compress(in, out);
+ EXPECT_EQ(res, 0);
+ res = compressor->decompress(out, after);
+ EXPECT_EQ(res, 0);
+ exp.append(test);
+ EXPECT_TRUE(exp.contents_equal(after));
+ after.clear();
+ size_t compressed_len = out.length();
+ out.append_zero(12);
+ auto it = out.begin();
+ res = compressor->decompress(it, compressed_len, after);
+ EXPECT_EQ(res, 0);
+ EXPECT_TRUE(exp.contents_equal(after));
+
+ //large block and non-begin iterator for continuous block
+ std::string data;
+ data.resize(0x10000 * 1);
+ for(size_t i = 0; i < data.size(); i++)
+ data[i] = i / 256;
+ in.clear();
+ out.clear();
+ in.append(data);
+ exp = in;
+ res = compressor->compress(in, out);
+ EXPECT_EQ(res, 0);
+ compressed_len = out.length();
+ out.append_zero(0x10000 - out.length());
+ after.clear();
+ out.c_str();
+ bufferlist prefix;
+ prefix.append(string("some prefix"));
+ size_t prefix_len = prefix.length();
+ out.claim_prepend(prefix);
+ it = out.begin();
+ it.advance(prefix_len);
+ res = compressor->decompress(it, compressed_len, after);
+ EXPECT_EQ(res, 0);
+ EXPECT_TRUE(exp.contents_equal(after));
+}
+
+TEST_P(CompressorTest, sharded_input_decompress)
+{
+ const size_t small_prefix_size=3;
+
+ string test(128*1024,0);
+ int len = test.size();
+ bufferlist in, out;
+ in.append(test.c_str(), len);
+ int res = compressor->compress(in, out);
+ EXPECT_EQ(res, 0);
+ EXPECT_GT(out.length(), small_prefix_size);
+
+ bufferlist out2, tmp;
+ tmp.substr_of(out, 0, small_prefix_size );
+ out2.append( tmp );
+ size_t left = out.length()-small_prefix_size;
+ size_t offs = small_prefix_size;
+ while( left > 0 ){
+ size_t shard_size = MIN( 2048, left );
+ tmp.substr_of(out, offs, shard_size );
+ out2.append( tmp );
+ left -= shard_size;
+ offs += shard_size;
+ }
+
+ bufferlist after;
+ res = compressor->decompress(out2, after);
+ EXPECT_EQ(res, 0);
+}
+
+void test_compress(CompressorRef compressor, size_t size)
+{
+ char* data = (char*) malloc(size);
+ for (size_t t = 0; t < size; t++) {
+ data[t] = (t & 0xff) | (t >> 8);
+ }
+ bufferlist in;
+ in.append(data, size);
+ for (size_t t = 0; t < 10000; t++) {
+ bufferlist out;
+ int res = compressor->compress(in, out);
+ EXPECT_EQ(res, 0);
+ }
+}
+
+void test_decompress(CompressorRef compressor, size_t size)
+{
+ char* data = (char*) malloc(size);
+ for (size_t t = 0; t < size; t++) {
+ data[t] = (t & 0xff) | (t >> 8);
+ }
+ bufferlist in, out;
+ in.append(data, size);
+ int res = compressor->compress(in, out);
+ EXPECT_EQ(res, 0);
+ for (size_t t = 0; t < 10000; t++) {
+ bufferlist out_dec;
+ int res = compressor->decompress(out, out_dec);
+ EXPECT_EQ(res, 0);
+ }
+}
+
+TEST_P(CompressorTest, compress_1024)
+{
+ test_compress(compressor, 1024);
+}
+
+TEST_P(CompressorTest, compress_2048)
+{
+ test_compress(compressor, 2048);
+}
+
+TEST_P(CompressorTest, compress_4096)
+{
+ test_compress(compressor, 4096);
+}
+
+TEST_P(CompressorTest, compress_8192)
+{
+ test_compress(compressor, 8192);
+}
+
+TEST_P(CompressorTest, compress_16384)
+{
+ test_compress(compressor, 16384);
+}
+
+TEST_P(CompressorTest, decompress_1024)
+{
+ test_decompress(compressor, 1024);
+}
+
+TEST_P(CompressorTest, decompress_2048)
+{
+ test_decompress(compressor, 2048);
+}
+
+TEST_P(CompressorTest, decompress_4096)
+{
+ test_decompress(compressor, 4096);
+}
+
+TEST_P(CompressorTest, decompress_8192)
+{
+ test_decompress(compressor, 8192);
+}
+
+TEST_P(CompressorTest, decompress_16384)
+{
+ test_decompress(compressor, 16384);
+}
+
+
+INSTANTIATE_TEST_CASE_P(
+ Compressor,
+ CompressorTest,
+ ::testing::Values(
+#ifdef HAVE_LZ4
+ "lz4",
+#endif
+#ifdef __x86_64__
+ "zlib/isal",
+#endif
+ "zlib/noisal",
+ "snappy",
+ "zstd"));
+
+#ifdef __x86_64__
+
+TEST(ZlibCompressor, zlib_isal_compatibility)
+{
+ g_conf->set_val("compressor_zlib_isal", "true");
+ g_ceph_context->_conf->apply_changes(NULL);
+ CompressorRef isal = Compressor::create(g_ceph_context, "zlib");
+ if (!isal) {
+ // skip the test if the plugin is not ready
+ return;
+ }
+ g_conf->set_val("compressor_zlib_isal", "false");
+ g_ceph_context->_conf->apply_changes(NULL);
+ CompressorRef zlib = Compressor::create(g_ceph_context, "zlib");
+ char test[101];
+ srand(time(0));
+ for (int i=0; i<100; ++i)
+ test[i] = 'a' + rand()%26;
+ test[100] = '\0';
+ int len = strlen(test);
+ bufferlist in, out;
+ in.append(test, len);
+ // isal -> zlib
+ int res = isal->compress(in, out);
+ EXPECT_EQ(res, 0);
+ bufferlist after;
+ res = zlib->decompress(out, after);
+ EXPECT_EQ(res, 0);
+ bufferlist exp;
+ exp.append(test);
+ EXPECT_TRUE(exp.contents_equal(after));
+ after.clear();
+ out.clear();
+ exp.clear();
+ // zlib -> isal
+ res = zlib->compress(in, out);
+ EXPECT_EQ(res, 0);
+ res = isal->decompress(out, after);
+ EXPECT_EQ(res, 0);
+ exp.append(test);
+ EXPECT_TRUE(exp.contents_equal(after));
+}
+#endif
+
+TEST(CompressionPlugin, all)
+{
+ const char* env = getenv("CEPH_LIB");
+ std::string directory(env ? env : ".libs");
+ CompressorRef compressor;
+ PluginRegistry *reg = g_ceph_context->get_plugin_registry();
+ EXPECT_TRUE(reg);
+ CompressionPlugin *factory = dynamic_cast<CompressionPlugin*>(reg->get_with_load("compressor", "invalid"));
+ EXPECT_FALSE(factory);
+ factory = dynamic_cast<CompressionPlugin*>(reg->get_with_load("compressor", "example"));
+ EXPECT_TRUE(factory);
+ stringstream ss;
+ EXPECT_EQ(0, factory->factory(&compressor, &ss));
+ EXPECT_TRUE(compressor.get());
+ {
+ Mutex::Locker l(reg->lock);
+ EXPECT_EQ(-ENOENT, reg->remove("compressor", "does not exist"));
+ EXPECT_EQ(0, reg->remove("compressor", "example"));
+ EXPECT_EQ(0, reg->load("compressor", "example"));
+ }
+}
+
+#ifdef __x86_64__
+
+TEST(ZlibCompressor, isal_compress_zlib_decompress_random)
+{
+ g_conf->set_val("compressor_zlib_isal", "true");
+ g_ceph_context->_conf->apply_changes(NULL);
+ CompressorRef isal = Compressor::create(g_ceph_context, "zlib");
+ if (!isal) {
+ // skip the test if the plugin is not ready
+ return;
+ }
+ g_conf->set_val("compressor_zlib_isal", "false");
+ g_ceph_context->_conf->apply_changes(NULL);
+ CompressorRef zlib = Compressor::create(g_ceph_context, "zlib");
+
+ for (int cnt=0; cnt<100; cnt++)
+ {
+ srand(cnt + 1000);
+ int log2 = (rand()%18) + 1;
+ int size = (rand() % (1 << log2)) + 1;
+
+ char test[size];
+ for (int i=0; i<size; ++i)
+ test[i] = rand()%256;
+ bufferlist in, out;
+ in.append(test, size);
+
+ int res = isal->compress(in, out);
+ EXPECT_EQ(res, 0);
+ bufferlist after;
+ res = zlib->decompress(out, after);
+ EXPECT_EQ(res, 0);
+ bufferlist exp;
+ exp.append(test, size);
+ EXPECT_TRUE(exp.contents_equal(after));
+ }
+}
+
+TEST(ZlibCompressor, isal_compress_zlib_decompress_walk)
+{
+ g_conf->set_val("compressor_zlib_isal", "true");
+ g_ceph_context->_conf->apply_changes(NULL);
+ CompressorRef isal = Compressor::create(g_ceph_context, "zlib");
+ if (!isal) {
+ // skip the test if the plugin is not ready
+ return;
+ }
+ g_conf->set_val("compressor_zlib_isal", "false");
+ g_ceph_context->_conf->apply_changes(NULL);
+ CompressorRef zlib = Compressor::create(g_ceph_context, "zlib");
+
+ for (int cnt=0; cnt<100; cnt++)
+ {
+ srand(cnt + 1000);
+ int log2 = (rand()%18) + 1;
+ int size = (rand() % (1 << log2)) + 1;
+
+ int range = 1;
+
+ char test[size];
+ test[0] = rand()%256;
+ for (int i=1; i<size; ++i)
+ test[i] = test[i-1] + rand()%(range*2+1) - range;
+ bufferlist in, out;
+ in.append(test, size);
+
+ int res = isal->compress(in, out);
+ EXPECT_EQ(res, 0);
+ bufferlist after;
+ res = zlib->decompress(out, after);
+ EXPECT_EQ(res, 0);
+ bufferlist exp;
+ exp.append(test, size);
+ EXPECT_TRUE(exp.contents_equal(after));
+ }
+}
+
+#endif // __x86_64__