Files
scylladb/tests/idl_test.cc
Paweł Dziepak 38ee69dee0 idl: allow writers to use any output stream
Original IDL generated code was hardcoded to always use bytes_ostream.
This patch makes the output stream a template parameter so that any
valid output stream can be used.
Unfortunately, making IDL writers generic requires updates in the code
that uses them, this is fixed in C++17 which would be able to deduce the
parameter in most cases.
2016-12-22 13:35:04 +01:00

352 lines
12 KiB
C++

/*
* Copyright 2016 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#define BOOST_TEST_MODULE core
#include <boost/test/unit_test.hpp>
#include "tests/test-utils.hh"
#include <map>
#include <vector>
#include <experimental/optional>
#include "bytes.hh"
#include "bytes_ostream.hh"
#include "serializer.hh"
#include "disk-error-handler.hh"
thread_local disk_error_signal_type commit_error;
thread_local disk_error_signal_type general_disk_error;
namespace stdx = std::experimental;
struct simple_compound {
// TODO: change this to test for #905
uint32_t foo;
uint32_t bar;
bool operator==(const simple_compound& other) const {
return foo == other.foo && bar == other.bar;
}
};
class non_final_composite_test_object {
simple_compound _x;
public:
static thread_local int construction_count;
non_final_composite_test_object(simple_compound x) : _x(x) {
++construction_count;
}
simple_compound x() const { return _x; }
};
class final_composite_test_object {
simple_compound _x;
public:
static thread_local int construction_count;
final_composite_test_object(simple_compound x) : _x(x) {
++construction_count;
}
simple_compound x() const { return _x; }
};
thread_local int non_final_composite_test_object::construction_count = 0;
thread_local int final_composite_test_object::construction_count = 0;
std::ostream& operator<<(std::ostream& os, const simple_compound& sc)
{
return os << " { foo: " << sc.foo << ", bar: " << sc.bar << " }";
}
struct compound_with_optional {
stdx::optional<simple_compound> first;
simple_compound second;
bool operator==(const compound_with_optional& other) const {
return first == other.first && second == other.second;
}
};
std::ostream& operator<<(std::ostream& os, const compound_with_optional& v)
{
os << " { first: ";
if (v.first) {
os << *v.first;
} else {
os << "<disengaged>";
}
os << ", second: " << v.second << " }";
return os;
}
struct wrapped_vector {
std::vector<simple_compound> vector;
bool operator==(const wrapped_vector& v) const { // = default;
return vector == v.vector;
}
};
std::ostream& operator<<(std::ostream& os, const wrapped_vector& v)
{
return os << v.vector;
}
struct vectors_of_compounds {
std::vector<simple_compound> first;
wrapped_vector second;
};
#include "serialization_visitors.hh"
#include "idl/idl_test.dist.hh"
#include "serializer_impl.hh"
#include "idl/idl_test.dist.impl.hh"
BOOST_AUTO_TEST_CASE(test_simple_compound)
{
simple_compound sc = { 0xdeadbeef, 0xbadc0ffe };
bytes_ostream buf1;
ser::serialize(buf1, sc);
BOOST_REQUIRE_EQUAL(buf1.size(), 12);
bytes_ostream buf2;
ser::writer_of_writable_simple_compound<bytes_ostream> wowsc(buf2);
std::move(wowsc).write_foo(sc.foo).write_bar(sc.bar).end_writable_simple_compound();
BOOST_REQUIRE_EQUAL(buf1.linearize(), buf2.linearize());
auto bv1 = buf1.linearize();
auto in1 = ser::as_input_stream(bv1);
auto deser_sc = ser::deserialize(in1, boost::type<simple_compound>());
BOOST_REQUIRE_EQUAL(sc, deser_sc);
auto bv2 = buf2.linearize();
auto in2 = ser::as_input_stream(bv2);
auto sc_view = ser::deserialize(in2, boost::type<ser::writable_simple_compound_view>());
BOOST_REQUIRE_EQUAL(sc.foo, sc_view.foo());
BOOST_REQUIRE_EQUAL(sc.bar, sc_view.bar());
}
BOOST_AUTO_TEST_CASE(test_vector)
{
std::vector<simple_compound> vec1 = {
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 },
{ 9, 10 },
};
std::vector<simple_compound> vec2 = {
{ 11, 12 },
{ 13, 14 },
{ 15, 16 },
{ 17, 18 },
{ 19, 20 },
};
vectors_of_compounds voc = { vec1, wrapped_vector { vec2 } };
bytes_ostream buf1;
ser::serialize(buf1, voc);
BOOST_REQUIRE_EQUAL(buf1.size(), 136);
bytes_ostream buf2;
ser::writer_of_writable_vectors_of_compounds<bytes_ostream> wowvoc(buf2);
auto first_writer = std::move(wowvoc).start_first();
for (auto& c : vec1) {
first_writer.add().write_foo(c.foo).write_bar(c.bar).end_writable_simple_compound();
}
auto second_writer = std::move(first_writer).end_first().start_second().start_vector();
for (auto& c : vec2) {
second_writer.add_vector(c);
}
std::move(second_writer).end_vector().end_second().end_writable_vectors_of_compounds();
BOOST_REQUIRE_EQUAL(buf1.linearize(), buf2.linearize());
auto bv1 = buf1.linearize();
auto in1 = ser::as_input_stream(bv1);
auto deser_voc = ser::deserialize(in1, boost::type<vectors_of_compounds>());
BOOST_REQUIRE_EQUAL(voc.first, deser_voc.first);
BOOST_REQUIRE_EQUAL(voc.second, deser_voc.second);
auto bv2 = buf2.linearize();
auto in2 = ser::as_input_stream(bv2);
auto voc_view = ser::deserialize(in2, boost::type<ser::writable_vectors_of_compounds_view>());
auto&& first_view = voc_view.first();
BOOST_REQUIRE_EQUAL(vec1.size(), first_view.size());
for (size_t i = 0; i < first_view.size(); i++) {
auto fv = first_view[i];
assert(vec1[i].foo == fv.foo());
BOOST_REQUIRE_EQUAL(vec1[i].foo, first_view[i].foo());
BOOST_REQUIRE_EQUAL(vec1[i].bar, first_view[i].bar());
}
auto&& second_view = voc_view.second().vector();
BOOST_REQUIRE_EQUAL(vec2.size(), second_view.size());
for (size_t i = 0; i < second_view.size(); i++) {
BOOST_REQUIRE_EQUAL(vec2[i], second_view[i]);
}
}
BOOST_AUTO_TEST_CASE(test_variant)
{
std::vector<simple_compound> vec = {
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
{ 7, 8 },
{ 9, 10 },
};
simple_compound sc = { 0xdeadbeef, 0xbadc0ffe };
simple_compound sc2 = { 0x12344321, 0x56788765 };
bytes_ostream buf;
ser::writer_of_writable_variants<bytes_ostream> wowv(buf);
auto second_writer = std::move(wowv).write_id(17).write_first_simple_compound(sc).start_second_writable_vector().start_vector();
for (auto&& v : vec) {
second_writer.add_vector(v);
}
auto third_writer = std::move(second_writer).end_vector().end_writable_vector().start_third_writable_final_simple_compound();
std::move(third_writer).write_foo(sc2.foo).write_bar(sc2.bar).end_writable_final_simple_compound().end_writable_variants();
BOOST_REQUIRE_EQUAL(buf.size(), 120);
auto bv = buf.linearize();
auto in = ser::as_input_stream(bv);
auto wv_view = ser::deserialize(in, boost::type<ser::writable_variants_view>());
BOOST_REQUIRE_EQUAL(wv_view.id(), 17);
struct expect_compound : boost::static_visitor<simple_compound> {
simple_compound operator()(ser::writable_vector_view&) const {
throw std::runtime_error("got writable_vector, expected simple_compound");
}
simple_compound operator()(simple_compound& sc) const {
return sc;
}
simple_compound operator()(ser::writable_final_simple_compound_view&) const {
throw std::runtime_error("got writable_final_simple_compound, expected simple_compound");
}
simple_compound operator()(ser::unknown_variant_type&) const {
throw std::runtime_error("unknown type, expected simple_compound");
}
};
auto v1 = wv_view.first();
auto&& compound = boost::apply_visitor(expect_compound(), v1);
BOOST_REQUIRE_EQUAL(compound, sc);
struct expect_vector : boost::static_visitor<std::vector<simple_compound>> {
std::vector<simple_compound> operator()(ser::writable_vector_view& wvv) const {
return wvv.vector();
}
std::vector<simple_compound> operator()(simple_compound&) const {
throw std::runtime_error("got simple_compound, expected writable_vector");
}
std::vector<simple_compound> operator()(ser::writable_final_simple_compound_view&) const {
throw std::runtime_error("got writable_final_simple_compound, expected writable_vector");
}
std::vector<simple_compound> operator()(ser::unknown_variant_type&) const {
throw std::runtime_error("unknown type, expected writable_vector");
}
};
auto v2 = wv_view.second();
auto&& vector = boost::apply_visitor(expect_vector(), v2);
BOOST_REQUIRE_EQUAL(vector, vec);
struct expect_writable_compound : boost::static_visitor<simple_compound> {
simple_compound operator()(ser::writable_vector_view&) const {
throw std::runtime_error("got writable_vector, expected writable_final_simple_compound");
}
simple_compound operator()(simple_compound&) const {
throw std::runtime_error("got simple_compound, expected writable_final_simple_compound");
}
simple_compound operator()(ser::writable_final_simple_compound_view& scv) const {
return simple_compound { scv.foo(), scv.bar() };
}
simple_compound operator()(ser::unknown_variant_type&) const {
throw std::runtime_error("unknown type, expected writable_final_simple_compound");
}
};
auto v3 = wv_view.third();
auto&& compound2 = boost::apply_visitor(expect_writable_compound(), v3);
BOOST_REQUIRE_EQUAL(compound2, sc2);
}
BOOST_AUTO_TEST_CASE(test_compound_with_optional)
{
simple_compound foo = { 0xdeadbeef, 0xbadc0ffe };
simple_compound bar = { 0x12345678, 0x87654321 };
compound_with_optional one = { foo, bar };
bytes_ostream buf1;
ser::serialize(buf1, one);
BOOST_REQUIRE_EQUAL(buf1.size(), 29);
auto bv1 = buf1.linearize();
seastar::simple_input_stream in1(reinterpret_cast<const char*>(bv1.data()), bv1.size());
auto deser_one = ser::deserialize(in1, boost::type<compound_with_optional>());
BOOST_REQUIRE_EQUAL(one, deser_one);
compound_with_optional two = { {}, foo };
bytes_ostream buf2;
ser::serialize(buf2, two);
BOOST_REQUIRE_EQUAL(buf2.size(), 17);
auto bv2 = buf2.linearize();
seastar::simple_input_stream in2(reinterpret_cast<const char*>(bv2.data()), bv2.size());
auto deser_two = ser::deserialize(in2, boost::type<compound_with_optional>());
BOOST_REQUIRE_EQUAL(two, deser_two);
}
BOOST_AUTO_TEST_CASE(test_skip_does_not_deserialize)
{
{
non_final_composite_test_object x({1, 2});
bytes_ostream buf;
ser::serialize(buf, x);
auto in = ser::as_input_stream(buf.linearize());
auto prev = non_final_composite_test_object::construction_count;
ser::skip(in, boost::type<non_final_composite_test_object>());
BOOST_REQUIRE(prev == non_final_composite_test_object::construction_count);
}
{
final_composite_test_object x({1, 2});
bytes_ostream buf;
ser::serialize(buf, x);
auto in = ser::as_input_stream(buf.linearize());
auto prev = final_composite_test_object::construction_count;
ser::skip(in, boost::type<final_composite_test_object>());
BOOST_REQUIRE(prev == final_composite_test_object::construction_count);
}
}