diff options
Diffstat (limited to 'src/test/encoding.cc')
-rw-r--r-- | src/test/encoding.cc | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/src/test/encoding.cc b/src/test/encoding.cc new file mode 100644 index 000000000..6bc89491f --- /dev/null +++ b/src/test/encoding.cc @@ -0,0 +1,543 @@ +#include "include/buffer.h" +#include "include/encoding.h" + +#include <fmt/format.h> +#include "gtest/gtest.h" + +using namespace std; + +template < typename T > +static void test_encode_and_decode(const T& src) +{ + bufferlist bl(1000000); + encode(src, bl); + T dst; + auto i = bl.cbegin(); + decode(dst, i); + ASSERT_EQ(src, dst) << "Encoding roundtrip changed the string: orig=" << src << ", but new=" << dst; +} + +TEST(EncodingRoundTrip, StringSimple) { + string my_str("I am the very model of a modern major general"); + test_encode_and_decode < std::string >(my_str); +} + +TEST(EncodingRoundTrip, StringEmpty) { + string my_str(""); + test_encode_and_decode < std::string >(my_str); +} + +TEST(EncodingRoundTrip, StringNewline) { + string my_str("foo bar baz\n"); + test_encode_and_decode < std::string >(my_str); +} + +template <typename Size, typename T> +static void test_encode_and_nohead_nohead(Size len, const T& src) +{ + bufferlist bl(1000000); + encode(len, bl); + encode_nohead(src, bl); + T dst; + auto i = bl.cbegin(); + decode(len, i); + decode_nohead(len, dst, i); + ASSERT_EQ(src, dst) << "Encoding roundtrip changed the string: orig=" << src << ", but new=" << dst; +} + +TEST(EncodingRoundTrip, StringNoHead) { + const string str("The quick brown fox jumps over the lazy dog"); + auto size = str.size(); + test_encode_and_nohead_nohead(static_cast<int>(size), str); + test_encode_and_nohead_nohead(static_cast<unsigned>(size), str); + test_encode_and_nohead_nohead(static_cast<uint32_t>(size), str); + test_encode_and_nohead_nohead(static_cast<__u32>(size), str); + test_encode_and_nohead_nohead(static_cast<size_t>(size), str); +} + +TEST(EncodingRoundTrip, BufferListNoHead) { + bufferlist bl; + bl.append("is this a dagger which i see before me?"); + auto size = bl.length(); + test_encode_and_nohead_nohead(static_cast<int>(size), bl); + test_encode_and_nohead_nohead(static_cast<unsigned>(size), bl); + test_encode_and_nohead_nohead(static_cast<uint32_t>(size), bl); + test_encode_and_nohead_nohead(static_cast<__u32>(size), bl); + test_encode_and_nohead_nohead(static_cast<size_t>(size), bl); +} + +typedef std::multimap < int, std::string > multimap_t; +typedef multimap_t::value_type my_val_ty; + +namespace std { +static std::ostream& operator<<(std::ostream& oss, const multimap_t &multimap) +{ + for (multimap_t::const_iterator m = multimap.begin(); + m != multimap.end(); + ++m) + { + oss << m->first << "->" << m->second << " "; + } + return oss; +} +} + +TEST(EncodingRoundTrip, Multimap) { + multimap_t multimap; + multimap.insert( my_val_ty(1, "foo") ); + multimap.insert( my_val_ty(2, "bar") ); + multimap.insert( my_val_ty(2, "baz") ); + multimap.insert( my_val_ty(3, "lucky number 3") ); + multimap.insert( my_val_ty(10000, "large number") ); + + test_encode_and_decode < multimap_t >(multimap); +} + + + +/////////////////////////////////////////////////////// +// ConstructorCounter +/////////////////////////////////////////////////////// +template <typename T> +class ConstructorCounter +{ +public: + ConstructorCounter() : data(0) + { + default_ctor++; + } + + explicit ConstructorCounter(const T& data_) + : data(data_) + { + one_arg_ctor++; + } + + ConstructorCounter(const ConstructorCounter &rhs) + : data(rhs.data) + { + copy_ctor++; + } + + ConstructorCounter &operator=(const ConstructorCounter &rhs) + { + data = rhs.data; + assigns++; + return *this; + } + + static void init(void) + { + default_ctor = 0; + one_arg_ctor = 0; + copy_ctor = 0; + assigns = 0; + } + + static int get_default_ctor(void) + { + return default_ctor; + } + + static int get_one_arg_ctor(void) + { + return one_arg_ctor; + } + + static int get_copy_ctor(void) + { + return copy_ctor; + } + + static int get_assigns(void) + { + return assigns; + } + + bool operator<(const ConstructorCounter &rhs) const + { + return data < rhs.data; + } + + bool operator==(const ConstructorCounter &rhs) const + { + return data == rhs.data; + } + + friend void decode(ConstructorCounter &s, bufferlist::const_iterator& p) + { + decode(s.data, p); + } + + friend void encode(const ConstructorCounter &s, bufferlist& p) + { + encode(s.data, p); + } + + friend ostream& operator<<(ostream &oss, const ConstructorCounter &cc) + { + oss << cc.data; + return oss; + } + + T data; +private: + static int default_ctor; + static int one_arg_ctor; + static int copy_ctor; + static int assigns; +}; + +template class ConstructorCounter <int32_t>; +template class ConstructorCounter <int16_t>; + +typedef ConstructorCounter <int32_t> my_key_t; +typedef ConstructorCounter <int16_t> my_val_t; +typedef std::multimap < my_key_t, my_val_t > multimap2_t; +typedef multimap2_t::value_type val2_ty; + +template <class T> int ConstructorCounter<T>::default_ctor = 0; +template <class T> int ConstructorCounter<T>::one_arg_ctor = 0; +template <class T> int ConstructorCounter<T>::copy_ctor = 0; +template <class T> int ConstructorCounter<T>::assigns = 0; + +static std::ostream& operator<<(std::ostream& oss, const multimap2_t &multimap) +{ + for (multimap2_t::const_iterator m = multimap.begin(); + m != multimap.end(); + ++m) + { + oss << m->first << "->" << m->second << " "; + } + return oss; +} + +TEST(EncodingRoundTrip, MultimapConstructorCounter) { + multimap2_t multimap2; + multimap2.insert( val2_ty(my_key_t(1), my_val_t(10)) ); + multimap2.insert( val2_ty(my_key_t(2), my_val_t(20)) ); + multimap2.insert( val2_ty(my_key_t(2), my_val_t(30)) ); + multimap2.insert( val2_ty(my_key_t(3), my_val_t(40)) ); + multimap2.insert( val2_ty(my_key_t(10000), my_val_t(1)) ); + + my_key_t::init(); + my_val_t::init(); + test_encode_and_decode < multimap2_t >(multimap2); + + EXPECT_EQ(my_key_t::get_default_ctor(), 5); + EXPECT_EQ(my_key_t::get_one_arg_ctor(), 0); + EXPECT_EQ(my_key_t::get_copy_ctor(), 5); + EXPECT_EQ(my_key_t::get_assigns(), 0); + + EXPECT_EQ(my_val_t::get_default_ctor(), 5); + EXPECT_EQ(my_val_t::get_one_arg_ctor(), 0); + EXPECT_EQ(my_val_t::get_copy_ctor(), 5); + EXPECT_EQ(my_val_t::get_assigns(), 0); +} + +namespace ceph { +// make sure that the legacy encode/decode methods are selected +// over the ones defined using templates. the later is likely to +// be slower, see also the definition of "WRITE_INT_DENC" in +// include/denc.h +template<> +void encode<uint64_t, denc_traits<uint64_t>>(const uint64_t&, + bufferlist&, + uint64_t f) { + static_assert(denc_traits<uint64_t>::supported, + "should support new encoder"); + static_assert(!denc_traits<uint64_t>::featured, + "should not be featured"); + ASSERT_EQ(0UL, f); + // make sure the test fails if i get called + ASSERT_TRUE(false); +} + +template<> +void encode<ceph_le64, denc_traits<ceph_le64>>(const ceph_le64&, + bufferlist&, + uint64_t f) { + static_assert(denc_traits<ceph_le64>::supported, + "should support new encoder"); + static_assert(!denc_traits<ceph_le64>::featured, + "should not be featured"); + ASSERT_EQ(0UL, f); + // make sure the test fails if i get called + ASSERT_TRUE(false); +} +} + +namespace { + // search `underlying_type` in denc.h for supported underlying types + enum class Colour : int8_t { R,G,B }; + ostream& operator<<(ostream& os, Colour c) { + switch (c) { + case Colour::R: + return os << "Colour::R"; + case Colour::G: + return os << "Colour::G"; + case Colour::B: + return os << "Colour::B"; + default: + return os << "Colour::???"; + } + } +} + +TEST(EncodingRoundTrip, Integers) { + // int types + { + uint64_t i = 42; + test_encode_and_decode(i); + } + { + int16_t i = 42; + test_encode_and_decode(i); + } + { + bool b = true; + test_encode_and_decode(b); + } + { + bool b = false; + test_encode_and_decode(b); + } + // raw encoder + { + ceph_le64 i; + i = 42; + test_encode_and_decode(i); + } + // enum + { + test_encode_and_decode(Colour::R); + // this should not build, as the size of unsigned is not the same on + // different archs, that's why denc_traits<> intentionally leaves + // `int` and `unsigned int` out of supported types. + // + // enum E { R, G, B }; + // test_encode_and_decode(R); + } +} + +TEST(EncodingException, Macros) { + const struct { + buffer::malformed_input exc; + std::string expected_what; + } tests[] = { + { + DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, 100, 200), + fmt::format("{} no longer understand old encoding version 100 < 200: Malformed input", + __PRETTY_FUNCTION__) + }, + { + DECODE_ERR_PAST(__PRETTY_FUNCTION__), + fmt::format("{} decode past end of struct encoding: Malformed input", + __PRETTY_FUNCTION__) + } + }; + for (auto& [exec, expected_what] : tests) { + try { + throw exec; + } catch (const exception& e) { + ASSERT_NE(string(e.what()).find(expected_what), string::npos); + } + } +} + + +TEST(small_encoding, varint) { + uint32_t v[][4] = { + /* value, varint bytes, signed varint bytes, signed varint bytes (neg) */ + {0, 1, 1, 1}, + {1, 1, 1, 1}, + {2, 1, 1, 1}, + {31, 1, 1, 1}, + {32, 1, 1, 1}, + {0xff, 2, 2, 2}, + {0x100, 2, 2, 2}, + {0xfff, 2, 2, 2}, + {0x1000, 2, 2, 2}, + {0x2000, 2, 3, 3}, + {0x3fff, 2, 3, 3}, + {0x4000, 3, 3, 3}, + {0x4001, 3, 3, 3}, + {0x10001, 3, 3, 3}, + {0x20001, 3, 3, 3}, + {0x40001, 3, 3, 3}, + {0x80001, 3, 3, 3}, + {0x7f0001, 4, 4, 4}, + {0xff00001, 4, 5, 5}, + {0x1ff00001, 5, 5, 5}, + {0xffff0001, 5, 5, 5}, + {0xffffffff, 5, 5, 5}, + {1074790401, 5, 5, 5}, + {0, 0, 0, 0} + }; + for (unsigned i=0; v[i][1]; ++i) { + { + bufferlist bl; + { + auto app = bl.get_contiguous_appender(16, true); + denc_varint(v[i][0], app); + } + cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t"; + bl.hexdump(cout, false); + cout << std::endl; + ASSERT_EQ(bl.length(), v[i][1]); + uint32_t u; + auto p = bl.begin().get_current_ptr().cbegin(); + denc_varint(u, p); + ASSERT_EQ(v[i][0], u); + } + { + bufferlist bl; + { + auto app = bl.get_contiguous_appender(16, true); + denc_signed_varint(v[i][0], app); + } + cout << std::hex << v[i][0] << "\t" << v[i][2] << "\t"; + bl.hexdump(cout, false); + cout << std::endl; + ASSERT_EQ(bl.length(), v[i][2]); + int32_t u; + auto p = bl.begin().get_current_ptr().cbegin(); + denc_signed_varint(u, p); + ASSERT_EQ((int32_t)v[i][0], u); + } + { + bufferlist bl; + int64_t x = -(int64_t)v[i][0]; + { + auto app = bl.get_contiguous_appender(16, true); + denc_signed_varint(x, app); + } + cout << std::dec << x << std::hex << "\t" << v[i][3] << "\t"; + bl.hexdump(cout, false); + cout << std::endl; + ASSERT_EQ(bl.length(), v[i][3]); + int64_t u; + auto p = bl.begin().get_current_ptr().cbegin(); + denc_signed_varint(u, p); + ASSERT_EQ(x, u); + } + } +} + +TEST(small_encoding, varint_lowz) { + uint32_t v[][4] = { + /* value, bytes encoded */ + {0, 1, 1, 1}, + {1, 1, 1, 1}, + {2, 1, 1, 1}, + {15, 1, 1, 1}, + {16, 1, 1, 1}, + {31, 1, 2, 2}, + {63, 2, 2, 2}, + {64, 1, 1, 1}, + {0xff, 2, 2, 2}, + {0x100, 1, 1, 1}, + {0x7ff, 2, 2, 2}, + {0xfff, 2, 3, 3}, + {0x1000, 1, 1, 1}, + {0x4000, 1, 1, 1}, + {0x8000, 1, 1, 1}, + {0x10000, 1, 2, 2}, + {0x20000, 2, 2, 2}, + {0x40000, 2, 2, 2}, + {0x80000, 2, 2, 2}, + {0x7f0000, 2, 2, 2}, + {0xffff0000, 4, 4, 4}, + {0xffffffff, 5, 5, 5}, + {0x41000000, 3, 4, 4}, + {0, 0, 0, 0} + }; + for (unsigned i=0; v[i][1]; ++i) { + { + bufferlist bl; + { + auto app = bl.get_contiguous_appender(16, true); + denc_varint_lowz(v[i][0], app); + } + cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t"; + bl.hexdump(cout, false); + cout << std::endl; + ASSERT_EQ(bl.length(), v[i][1]); + uint32_t u; + auto p = bl.begin().get_current_ptr().cbegin(); + denc_varint_lowz(u, p); + ASSERT_EQ(v[i][0], u); + } + { + bufferlist bl; + int64_t x = v[i][0]; + { + auto app = bl.get_contiguous_appender(16, true); + denc_signed_varint_lowz(x, app); + } + cout << std::hex << x << "\t" << v[i][1] << "\t"; + bl.hexdump(cout, false); + cout << std::endl; + ASSERT_EQ(bl.length(), v[i][2]); + int64_t u; + auto p = bl.begin().get_current_ptr().cbegin(); + denc_signed_varint_lowz(u, p); + ASSERT_EQ(x, u); + } + { + bufferlist bl; + int64_t x = -(int64_t)v[i][0]; + { + auto app = bl.get_contiguous_appender(16, true); + denc_signed_varint_lowz(x, app); + } + cout << std::dec << x << "\t" << v[i][1] << "\t"; + bl.hexdump(cout, false); + cout << std::endl; + ASSERT_EQ(bl.length(), v[i][3]); + int64_t u; + auto p = bl.begin().get_current_ptr().cbegin(); + denc_signed_varint_lowz(u, p); + ASSERT_EQ(x, u); + } + } +} + +TEST(small_encoding, lba) { + uint64_t v[][2] = { + /* value, bytes encoded */ + {0, 4}, + {1, 4}, + {0xff, 4}, + {0x10000, 4}, + {0x7f0000, 4}, + {0xffff0000, 4}, + {0x0fffffff, 4}, + {0x1fffffff, 5}, + {0xffffffff, 5}, + {0x3fffffff000, 4}, + {0x7fffffff000, 5}, + {0x1fffffff0000, 4}, + {0x3fffffff0000, 5}, + {0xfffffff00000, 4}, + {0x1fffffff00000, 5}, + {0x41000000, 4}, + {0, 0} + }; + for (unsigned i=0; v[i][1]; ++i) { + bufferlist bl; + { + auto app = bl.get_contiguous_appender(16, true); + denc_lba(v[i][0], app); + } + cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t"; + bl.hexdump(cout, false); + cout << std::endl; + ASSERT_EQ(bl.length(), v[i][1]); + uint64_t u; + auto p = bl.begin().get_current_ptr().cbegin(); + denc_lba(u, p); + ASSERT_EQ(v[i][0], u); + } + +} |