ores.cpp.wire-codec.roundtrip_tests

Table of Contents

The test file for one type's wire codec. It fills a value with ores::utility::codec::sample, writes it, reads it back, and requires the value it read to equal the value it wrote. It does this in JSON and in MessagePack, for the sample and for the default value.

The sample sets every member away from its default, so a codec that drops a member, writes it under the wrong name or reads it back as another value fails here. The default value catches the opposite fault: a codec that cannot read back an empty string, a nil id or the epoch.

See the Template variable reference for the complete list of available variables and their semantics.

1. Mustache incantations

An entity and a junction go through wire_traits, which is the generated translation unit the facet's codec_impl writes. A field group has no traits of its own, because it crosses the wire only inside the entity that holds it, so its test calls the struct codec directly on the group's wire_fields.

The sample fills only the members wire_fields names, so a roundtrip alone cannot see a member the list leaves out: the sample leaves it at its default and the codec reads the default back. A static_assert closes that gap. It compares the struct's member count with the list's length, less {{wire_left_out}}, the members the model marks :no_wire: on purpose.

Every case name and helper starts with the type's name. A component's test files link into one binary and build as one unity unit, so without the prefix two types in the same component declare each name twice.

2. Template

The full template source. Edit here and re-tangle with compass build --direct tangle_codegen_templates to regenerate library/templates/cpp_domain_type_codec_roundtrip_tests.cpp.mustache.

{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/ores.cpp.wire-codec.roundtrip_tests.org. Edit the org source. }}
{{{cpp_license}}}
{{#domain_entity}}
#include "ores.{{component_include}}/domain/{{entity_singular}}_codec.hpp"
{{#wire_codec_includes}}
#include "{{{.}}}"
{{/wire_codec_includes}}
#include "ores.utility/codec/member_count.hpp"
#include "ores.utility/codec/sample.hpp"
#include <catch2/catch_test_macros.hpp>
#include <cstddef>
#include <span>
#include <string>

namespace {

using {{entity_singular}}_roundtrip_type = ores::{{component}}::domain::{{entity_singular}};
using {{entity_singular}}_roundtrip_traits =
    ores::utility::codec::wire_traits<{{entity_singular}}_roundtrip_type>;

const std::string {{entity_singular}}_roundtrip_tags("[codec][roundtrip]");

std::span<const std::byte> {{entity_singular}}_roundtrip_bytes(const std::string& s) {
    return {reinterpret_cast<const std::byte*>(s.data()), s.size()};
}

}

static_assert(ores::utility::codec::names_every_member<{{entity_singular}}_roundtrip_type>({{wire_left_out}}),
              "{{entity_singular}} has a member its wire shape does not name");

TEST_CASE("{{entity_singular}}_codec_roundtrip_json_sample", {{entity_singular}}_roundtrip_tags) {
    const auto v = ores::utility::codec::sample<{{entity_singular}}_roundtrip_type>(0);
    CHECK({{entity_singular}}_roundtrip_traits::read_json({{entity_singular}}_roundtrip_traits::write_json(v)) == v);
}

TEST_CASE("{{entity_singular}}_codec_roundtrip_msgpack_sample", {{entity_singular}}_roundtrip_tags) {
    const auto v = ores::utility::codec::sample<{{entity_singular}}_roundtrip_type>(0);
    const auto packed = {{entity_singular}}_roundtrip_traits::write_msgpack(v);
    CHECK({{entity_singular}}_roundtrip_traits::read_msgpack({{entity_singular}}_roundtrip_bytes(packed)) == v);
}

TEST_CASE("{{entity_singular}}_codec_roundtrip_json_default", {{entity_singular}}_roundtrip_tags) {
    const {{entity_singular}}_roundtrip_type v{};
    CHECK({{entity_singular}}_roundtrip_traits::read_json({{entity_singular}}_roundtrip_traits::write_json(v)) == v);
}

TEST_CASE("{{entity_singular}}_codec_roundtrip_msgpack_default", {{entity_singular}}_roundtrip_tags) {
    const {{entity_singular}}_roundtrip_type v{};
    const auto packed = {{entity_singular}}_roundtrip_traits::write_msgpack(v);
    CHECK({{entity_singular}}_roundtrip_traits::read_msgpack({{entity_singular}}_roundtrip_bytes(packed)) == v);
}
{{/domain_entity}}
{{#junction}}
#include "ores.{{component_include}}/domain/{{name_singular}}_codec.hpp"
{{#wire_codec_includes}}
#include "{{{.}}}"
{{/wire_codec_includes}}
#include "ores.utility/codec/member_count.hpp"
#include "ores.utility/codec/sample.hpp"
#include <catch2/catch_test_macros.hpp>
#include <cstddef>
#include <span>
#include <string>

namespace {

using {{name_singular}}_roundtrip_type = ores::{{component}}::domain::{{name_singular}};
using {{name_singular}}_roundtrip_traits =
    ores::utility::codec::wire_traits<{{name_singular}}_roundtrip_type>;

const std::string {{name_singular}}_roundtrip_tags("[codec][roundtrip]");

std::span<const std::byte> {{name_singular}}_roundtrip_bytes(const std::string& s) {
    return {reinterpret_cast<const std::byte*>(s.data()), s.size()};
}

}

static_assert(ores::utility::codec::names_every_member<{{name_singular}}_roundtrip_type>({{wire_left_out}}),
              "{{name_singular}} has a member its wire shape does not name");

TEST_CASE("{{name_singular}}_codec_roundtrip_json_sample", {{name_singular}}_roundtrip_tags) {
    const auto v = ores::utility::codec::sample<{{name_singular}}_roundtrip_type>(0);
    CHECK({{name_singular}}_roundtrip_traits::read_json({{name_singular}}_roundtrip_traits::write_json(v)) == v);
}

TEST_CASE("{{name_singular}}_codec_roundtrip_msgpack_sample", {{name_singular}}_roundtrip_tags) {
    const auto v = ores::utility::codec::sample<{{name_singular}}_roundtrip_type>(0);
    const auto packed = {{name_singular}}_roundtrip_traits::write_msgpack(v);
    CHECK({{name_singular}}_roundtrip_traits::read_msgpack({{name_singular}}_roundtrip_bytes(packed)) == v);
}

TEST_CASE("{{name_singular}}_codec_roundtrip_json_default", {{name_singular}}_roundtrip_tags) {
    const {{name_singular}}_roundtrip_type v{};
    CHECK({{name_singular}}_roundtrip_traits::read_json({{name_singular}}_roundtrip_traits::write_json(v)) == v);
}

TEST_CASE("{{name_singular}}_codec_roundtrip_msgpack_default", {{name_singular}}_roundtrip_tags) {
    const {{name_singular}}_roundtrip_type v{};
    const auto packed = {{name_singular}}_roundtrip_traits::write_msgpack(v);
    CHECK({{name_singular}}_roundtrip_traits::read_msgpack({{name_singular}}_roundtrip_bytes(packed)) == v);
}
{{/junction}}
{{#field_group}}
#include "ores.{{component_include}}/domain/{{entity_singular}}.hpp"
{{#wire_codec_includes}}
#include "{{{.}}}"
{{/wire_codec_includes}}
#include "ores.utility/codec/member_count.hpp"
#include "ores.utility/codec/sample.hpp"
#include "ores.utility/codec/struct_codec.hpp"
#include <catch2/catch_test_macros.hpp>
#include <cstddef>
#include <span>
#include <string>

namespace {

using {{entity_singular}}_roundtrip_type = {{cpp.namespace}}::{{entity_singular}};

const std::string {{entity_singular}}_roundtrip_tags("[codec][roundtrip]");

std::span<const std::byte> {{entity_singular}}_roundtrip_bytes(const std::string& s) {
    return {reinterpret_cast<const std::byte*>(s.data()), s.size()};
}

}

static_assert(ores::utility::codec::names_every_member<{{entity_singular}}_roundtrip_type>({{wire_left_out}}),
              "{{entity_singular}} has a member its wire shape does not name");

TEST_CASE("{{entity_singular}}_codec_roundtrip_json_sample", {{entity_singular}}_roundtrip_tags) {
    using namespace ores::utility::codec;
    const auto v = sample<{{entity_singular}}_roundtrip_type>(0);
    CHECK(object::read_json<{{entity_singular}}_roundtrip_type>(object::write_json(v)) == v);
}

TEST_CASE("{{entity_singular}}_codec_roundtrip_msgpack_sample", {{entity_singular}}_roundtrip_tags) {
    using namespace ores::utility::codec;
    const auto v = sample<{{entity_singular}}_roundtrip_type>(0);
    const auto packed = object::write_msgpack(v);
    CHECK(object::read_msgpack<{{entity_singular}}_roundtrip_type>({{entity_singular}}_roundtrip_bytes(packed)) == v);
}

TEST_CASE("{{entity_singular}}_codec_roundtrip_json_default", {{entity_singular}}_roundtrip_tags) {
    using namespace ores::utility::codec;
    const {{entity_singular}}_roundtrip_type v{};
    CHECK(object::read_json<{{entity_singular}}_roundtrip_type>(object::write_json(v)) == v);
}

TEST_CASE("{{entity_singular}}_codec_roundtrip_msgpack_default", {{entity_singular}}_roundtrip_tags) {
    using namespace ores::utility::codec;
    const {{entity_singular}}_roundtrip_type v{};
    const auto packed = object::write_msgpack(v);
    CHECK(object::read_msgpack<{{entity_singular}}_roundtrip_type>({{entity_singular}}_roundtrip_bytes(packed)) == v);
}
{{/field_group}}

3. See also

Emacs 29.3 (Org mode 9.6.15)