xo-umbrella2/xo-indentlog2/utest/pretty_struct.test.cpp

132 lines
4.9 KiB
C++

/** @file pretty_struct.test.cpp
*
* exercise PpSink::pretty_struct (xo/ppsink/pretty_struct.hpp) through the
* real line-breaking sink.
*
* xo-ppsink's own pretty_struct tests can only pin the flat text and the
* token stream -- FlatSink's begin/split/end are no-ops, and PrettySink lives
* here. So this is the only place the *rendered wrapped form* can be
* asserted, and in particular the indent it wraps to.
*
* That indent is the point. begin(offset) and split(spaces,offset) COMPOUND
* in PpState (begin adds to the running indent; a split's break target is
* running-indent + its own offset). A printer that passes the indent to both
* wraps to twice the intended column, and nothing in the flat case reveals
* it. These tests fail if pretty_struct regresses that way.
**/
#include <xo/ppsink/pretty_struct.hpp>
#include "print/PrettySink.hpp"
#include <xo/arena/ArenaConfig.hpp>
#include <catch2/catch.hpp>
#include <string>
namespace ut {
using xo::pp::PrettySink;
using xo::pp::PpConfig;
using xo::pp::PpStyle;
using xo::pp::field;
using xo::mm::ArenaConfig;
namespace {
/** render fields as a struct named @p name, at a given right margin.
*
* NB the arena name must be unique per call: two PrettySinks sharing
* an ArenaConfig name interfere, and the symptom is wrong indentation
* in whichever case runs second -- which looks exactly like a bug in
* the printer under test. Hence the counter.
**/
template <typename... Fields>
std::string
render(std::uint32_t margin, std::string_view name, const Fields &... fields)
{
PrettySink pp(PpConfig::scratch_plain(margin), nullptr);
pp.pretty_struct(name, fields...);
return std::string(pp.output());
}
/** a struct-shaped value that itself prints via pretty_struct,
* to exercise nesting
**/
struct Inner { int x_ = 1; };
} /*namespace*/
} /*namespace ut*/
namespace xo::pp {
template <>
struct Prettifier<ut::Inner> {
static void print(PpSink & sink, const ut::Inner & v) {
sink.pretty_struct("In", field("x", v.x_));
}
};
} /*namespace xo::pp*/
namespace ut {
TEST_CASE("pretty_struct-fits-one-line", "[pretty_struct]") {
int alpha = 1, beta = 2;
REQUIRE(render(135, "Point", field("alpha", alpha), field("beta", beta))
== "<Point :alpha 1 :beta 2>");
}
TEST_CASE("pretty_struct-wraps-to-indent-2", "[pretty_struct]") {
/* THE regression test. indent_width defaults to 2, and pretty_struct
* calls the no-arg begin() + split(1) -- so a broken field lands at
* column 2. If it ever lands at column 4, someone has passed the
* indent to begin() AND split() and they have compounded.
*
* margin 20: wide enough that each field still fits on its own line
* (so the only breaks are the ones between fields), narrow enough that
* the 24-char flat form does not.
*/
int alpha = 1, beta = 2;
std::string out = render(20, "Point", field("alpha", alpha), field("beta", beta));
REQUIRE(out == "<Point\n :alpha 1\n :beta 2>");
/* stated separately so a regression reads unambiguously */
REQUIRE(out.find("\n :alpha") != std::string::npos);
REQUIRE(out.find("\n :alpha") == std::string::npos);
}
TEST_CASE("pretty_struct-nested-accumulates-indent", "[pretty_struct]") {
/* begin() takes its offset from the sink's PpConfig and accumulates,
* so an inner struct's fields sit one level deeper than the outer's:
* outer fields at column 2, the inner struct's own field at 4.
*/
Inner inner;
int alpha = 1;
std::string out = render(12, "Out", field("alpha", alpha), field("in", inner));
INFO(out);
REQUIRE(out == "<Out\n :alpha 1\n :in\n <In\n :x 1>>");
/* the accumulation itself, stated directly */
REQUIRE(out.find("\n :alpha") != std::string::npos); /* outer: 2 */
REQUIRE(out.find("\n :x") != std::string::npos); /* inner: 4 */
}
TEST_CASE("pretty_struct-nested-fits-flat", "[pretty_struct]") {
Inner inner;
int alpha = 1;
REQUIRE(render(135, "Out", field("alpha", alpha), field("in", inner))
== "<Out :alpha 1 :in <In :x 1>>");
}
TEST_CASE("pretty_struct-absent-field-wrapped", "[pretty_struct]") {
/* an omitted field must not leave a break opportunity behind: at a
* margin where "<Point :alpha 1>" fits, the result stays flat.
*/
int alpha = 1, beta = 2;
REQUIRE(render(20, "Point", field("alpha", alpha), field("beta", beta, false))
== "<Point :alpha 1>");
}
} /*namespace ut*/
/* end pretty_struct.test.cpp */