eldritch horrors
5eec6418de
generators are a better basis for serializers than streaming into sinks
as we do currently for many reasons, such as being usable as sources if
one wishes to (without requiring an intermediate sink to serialize full
data sets into memory, or boost coroutines to turn sinks into sources),
composing more naturally (as one can just yield a sub-generator instead
of being forced to wrap entire substreams into clunky functions or even
more clunky custom types to implement operator<< on), allowing wrappers
to transform data with clear ownership semantics (removing the need for
explicit memory allocations and Source wrappers), and many other things
Change-Id: I361d89ff556354f6930d9204f55117565f2f7f20
218 lines
5.1 KiB
C++
218 lines
5.1 KiB
C++
#include "serialise.hh"
|
|
#include "error.hh"
|
|
#include "fmt.hh"
|
|
#include "pos-table.hh"
|
|
#include "generator.hh"
|
|
#include "ref.hh"
|
|
#include "types.hh"
|
|
|
|
#include <concepts>
|
|
#include <cstdint>
|
|
#include <initializer_list>
|
|
#include <limits.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <numeric>
|
|
#include <stdexcept>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
|
|
namespace nix {
|
|
|
|
// don't deduce the type of `val` for added insurance.
|
|
template<typename T>
|
|
static std::string toWire(const std::type_identity_t<T> & val)
|
|
{
|
|
std::string result;
|
|
auto g = [] (const auto & val) -> WireFormatGenerator { co_yield val; }(val);
|
|
while (auto buffer = g.next()) {
|
|
result.append(buffer->data(), buffer->size());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
TEST(WireFormatGenerator, uint64_t)
|
|
{
|
|
auto s = toWire<uint64_t>(42);
|
|
ASSERT_EQ(s, std::string({42, 0, 0, 0, 0, 0, 0, 0}));
|
|
}
|
|
|
|
TEST(WireFormatGenerator, string_view)
|
|
{
|
|
auto s = toWire<std::string_view>("");
|
|
// clang-format off
|
|
ASSERT_EQ(
|
|
s,
|
|
std::string({
|
|
// length
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
// data (omitted)
|
|
})
|
|
);
|
|
// clang-format on
|
|
|
|
s = toWire<std::string_view>("test");
|
|
// clang-format off
|
|
ASSERT_EQ(
|
|
s,
|
|
std::string({
|
|
// length
|
|
4, 0, 0, 0, 0, 0, 0, 0,
|
|
// data
|
|
't', 'e', 's', 't',
|
|
// padding
|
|
0, 0, 0, 0,
|
|
})
|
|
);
|
|
// clang-format on
|
|
|
|
s = toWire<std::string_view>("longer string");
|
|
// clang-format off
|
|
ASSERT_EQ(
|
|
s,
|
|
std::string({
|
|
// length
|
|
13, 0, 0, 0, 0, 0, 0, 0,
|
|
// data
|
|
'l', 'o', 'n', 'g', 'e', 'r', ' ', 's', 't', 'r', 'i', 'n', 'g',
|
|
// padding
|
|
0, 0, 0,
|
|
})
|
|
);
|
|
// clang-format on
|
|
}
|
|
|
|
TEST(WireFormatGenerator, StringSet)
|
|
{
|
|
auto s = toWire<StringSet>({});
|
|
// clang-format off
|
|
ASSERT_EQ(
|
|
s,
|
|
std::string({
|
|
// length
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
// data (omitted)
|
|
})
|
|
);
|
|
// clang-format on
|
|
|
|
s = toWire<StringSet>({"a", ""});
|
|
// clang-format off
|
|
ASSERT_EQ(
|
|
s,
|
|
std::string({
|
|
// length
|
|
2, 0, 0, 0, 0, 0, 0, 0,
|
|
// data ""
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
// data "a"
|
|
1, 0, 0, 0, 0, 0, 0, 0, 'a', 0, 0, 0, 0, 0, 0, 0,
|
|
})
|
|
);
|
|
// clang-format on
|
|
}
|
|
|
|
TEST(WireFormatGenerator, Strings)
|
|
{
|
|
auto s = toWire<Strings>({});
|
|
// clang-format off
|
|
ASSERT_EQ(
|
|
s,
|
|
std::string({
|
|
// length
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
// data (omitted)
|
|
})
|
|
);
|
|
// clang-format on
|
|
|
|
s = toWire<Strings>({"a", ""});
|
|
// clang-format off
|
|
ASSERT_EQ(
|
|
s,
|
|
std::string({
|
|
// length
|
|
2, 0, 0, 0, 0, 0, 0, 0,
|
|
// data "a"
|
|
1, 0, 0, 0, 0, 0, 0, 0, 'a', 0, 0, 0, 0, 0, 0, 0,
|
|
// data ""
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
})
|
|
);
|
|
// clang-format on
|
|
}
|
|
|
|
TEST(WireFormatGenerator, Error)
|
|
{
|
|
PosTable pt;
|
|
auto o = pt.addOrigin(Pos::String{make_ref<std::string>("test")}, 4);
|
|
|
|
auto s = toWire<Error>(Error{ErrorInfo{
|
|
.level = lvlInfo,
|
|
.msg = HintFmt("foo"),
|
|
.pos = pt[pt.add(o, 1)],
|
|
.traces = {{.pos = pt[pt.add(o, 2)], .hint = HintFmt("b %1%", "foo")}},
|
|
}});
|
|
// NOTE position of the error and all traces are ignored
|
|
// by the wire format
|
|
// clang-format off
|
|
ASSERT_EQ(
|
|
s,
|
|
std::string({
|
|
5, 0, 0, 0, 0, 0, 0, 0, 'E', 'r', 'r', 'o', 'r', 0, 0, 0,
|
|
3, 0, 0, 0, 0, 0, 0, 0,
|
|
5, 0, 0, 0, 0, 0, 0, 0, 'E', 'r', 'r', 'o', 'r', 0, 0, 0,
|
|
3, 0, 0, 0, 0, 0, 0, 0, 'f', 'o', 'o', 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
16, 0, 0, 0, 0, 0, 0, 0,
|
|
'b', ' ', '\x1b', '[', '3', '5', ';', '1', 'm', 'f', 'o', 'o', '\x1b', '[', '0', 'm',
|
|
})
|
|
);
|
|
// clang-format on
|
|
}
|
|
|
|
TEST(WireFormatGenerator, exampleMessage)
|
|
{
|
|
auto gen = []() -> WireFormatGenerator {
|
|
std::set<std::string> foo{"a", "longer string", ""};
|
|
co_yield 42;
|
|
co_yield foo;
|
|
co_yield std::string_view("test");
|
|
co_yield true;
|
|
}();
|
|
|
|
std::vector<char> full;
|
|
while (auto s = gen.next()) {
|
|
full.insert(full.end(), s->begin(), s->end());
|
|
}
|
|
|
|
ASSERT_EQ(
|
|
full,
|
|
(std::vector<char>{
|
|
// clang-format off
|
|
// 42
|
|
42, 0, 0, 0, 0, 0, 0, 0,
|
|
// foo
|
|
3, 0, 0, 0, 0, 0, 0, 0,
|
|
/// ""
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
/// a
|
|
1, 0, 0, 0, 0, 0, 0, 0,
|
|
'a', 0, 0, 0, 0, 0, 0, 0,
|
|
/// longer string
|
|
13, 0, 0, 0, 0, 0, 0, 0,
|
|
'l', 'o', 'n', 'g', 'e', 'r', ' ', 's', 't', 'r', 'i', 'n', 'g', 0, 0, 0,
|
|
// foo done
|
|
// test
|
|
4, 0, 0, 0, 0, 0, 0, 0,
|
|
't', 'e', 's', 't', 0, 0, 0, 0,
|
|
// true
|
|
1, 0, 0, 0, 0, 0, 0, 0,
|
|
//clang-format on
|
|
}));
|
|
}
|
|
|
|
}
|