From 5eec6418de35daaa7b14b5412e39d85ce80a37cb Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Tue, 19 Mar 2024 22:22:18 +0100 Subject: libutil: begin porting serialization to generators 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 --- tests/unit/libutil/serialise.cc | 117 ++++++++++++++++++++++++++++------------ 1 file changed, 84 insertions(+), 33 deletions(-) (limited to 'tests') diff --git a/tests/unit/libutil/serialise.cc b/tests/unit/libutil/serialise.cc index 95ae43115..78882ad2c 100644 --- a/tests/unit/libutil/serialise.cc +++ b/tests/unit/libutil/serialise.cc @@ -2,30 +2,47 @@ #include "error.hh" #include "fmt.hh" #include "pos-table.hh" +#include "generator.hh" #include "ref.hh" #include "types.hh" +#include +#include +#include #include #include #include +#include +#include +#include namespace nix { -TEST(Sink, uint64_t) +// don't deduce the type of `val` for added insurance. +template +static std::string toWire(const std::type_identity_t & val) { - StringSink s; - s << 42; - ASSERT_EQ(s.s, std::string({42, 0, 0, 0, 0, 0, 0, 0})); + 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(Sink, string_view) +TEST(WireFormatGenerator, uint64_t) { - StringSink s; - s << ""; + auto s = toWire(42); + ASSERT_EQ(s, std::string({42, 0, 0, 0, 0, 0, 0, 0})); +} + +TEST(WireFormatGenerator, string_view) +{ + auto s = toWire(""); // clang-format off ASSERT_EQ( - s.s, + s, std::string({ // length 0, 0, 0, 0, 0, 0, 0, 0, @@ -34,11 +51,10 @@ TEST(Sink, string_view) ); // clang-format on - s = {}; - s << "test"; + s = toWire("test"); // clang-format off ASSERT_EQ( - s.s, + s, std::string({ // length 4, 0, 0, 0, 0, 0, 0, 0, @@ -50,11 +66,10 @@ TEST(Sink, string_view) ); // clang-format on - s = {}; - s << "longer string"; + s = toWire("longer string"); // clang-format off ASSERT_EQ( - s.s, + s, std::string({ // length 13, 0, 0, 0, 0, 0, 0, 0, @@ -67,13 +82,12 @@ TEST(Sink, string_view) // clang-format on } -TEST(Sink, StringSet) +TEST(WireFormatGenerator, StringSet) { - StringSink s; - s << StringSet{}; + auto s = toWire({}); // clang-format off ASSERT_EQ( - s.s, + s, std::string({ // length 0, 0, 0, 0, 0, 0, 0, 0, @@ -82,11 +96,10 @@ TEST(Sink, StringSet) ); // clang-format on - s = {}; - s << StringSet{"a", ""}; + s = toWire({"a", ""}); // clang-format off ASSERT_EQ( - s.s, + s, std::string({ // length 2, 0, 0, 0, 0, 0, 0, 0, @@ -99,13 +112,12 @@ TEST(Sink, StringSet) // clang-format on } -TEST(Sink, Strings) +TEST(WireFormatGenerator, Strings) { - StringSink s; - s << Strings{}; + auto s = toWire({}); // clang-format off ASSERT_EQ( - s.s, + s, std::string({ // length 0, 0, 0, 0, 0, 0, 0, 0, @@ -114,11 +126,10 @@ TEST(Sink, Strings) ); // clang-format on - s = {}; - s << Strings{"a", ""}; + s = toWire({"a", ""}); // clang-format off ASSERT_EQ( - s.s, + s, std::string({ // length 2, 0, 0, 0, 0, 0, 0, 0, @@ -131,23 +142,22 @@ TEST(Sink, Strings) // clang-format on } -TEST(Sink, Error) +TEST(WireFormatGenerator, Error) { PosTable pt; auto o = pt.addOrigin(Pos::String{make_ref("test")}, 4); - StringSink s; - s << Error{ErrorInfo{ + auto s = toWire(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.s, + 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, @@ -163,4 +173,45 @@ TEST(Sink, Error) // clang-format on } +TEST(WireFormatGenerator, exampleMessage) +{ + auto gen = []() -> WireFormatGenerator { + std::set foo{"a", "longer string", ""}; + co_yield 42; + co_yield foo; + co_yield std::string_view("test"); + co_yield true; + }(); + + std::vector full; + while (auto s = gen.next()) { + full.insert(full.end(), s->begin(), s->end()); + } + + ASSERT_EQ( + full, + (std::vector{ + // 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 + })); +} + } -- cgit v1.2.3