184 lines
7.4 KiB
C++
184 lines
7.4 KiB
C++
/* @file toppstr.test.cpp
|
|
*
|
|
* author: Roland Conybeare, Aug 2026
|
|
*
|
|
* xo::pp::toppstr -- render through a PrettySink, i.e. WITH line breaking.
|
|
* Counterpart to xo::pp::tostr, which uses a FlatSink and never breaks.
|
|
*
|
|
* Expectations here were read off observed output, not predicted.
|
|
*/
|
|
|
|
#include "print/toppstr.hpp"
|
|
#include "print/tostr.hpp"
|
|
#include <xo/ppsink/PpStyle.hpp>
|
|
#include <xo/ppsink/pretty_struct.hpp>
|
|
#include <xo/ppsink/pretty_array.hpp>
|
|
#include <catch2/catch.hpp>
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace xo {
|
|
namespace ut {
|
|
using xo::pp::PpConfig;
|
|
using xo::pp::PpStyle;
|
|
using xo::pp::toppstr;
|
|
using xo::pp::tostr;
|
|
using xo::pp::color_spec_type;
|
|
|
|
namespace {
|
|
/** an array is enough structure to break: Prettifier<std::array>
|
|
* puts a split() between elements.
|
|
**/
|
|
std::array<int, 8> s_value = { 111, 222, 333, 444, 555, 666, 777, 888 };
|
|
|
|
std::string
|
|
render_at(std::uint32_t margin) {
|
|
return toppstr(PpConfig::scratch_plain(margin), s_value);
|
|
}
|
|
|
|
struct Testcase_Toppstr {
|
|
Testcase_Toppstr(std::uint32_t margin, bool expect_break)
|
|
: margin_{margin}, expect_break_{expect_break} {}
|
|
|
|
/** soft right margin to render at **/
|
|
std::uint32_t margin_;
|
|
/** whether the rendering is expected to span >1 line **/
|
|
bool expect_break_;
|
|
};
|
|
|
|
std::vector<Testcase_Toppstr>
|
|
s_testcase_v = {
|
|
Testcase_Toppstr(200, false),
|
|
Testcase_Toppstr(100, false),
|
|
Testcase_Toppstr(20, true),
|
|
Testcase_Toppstr(8, true),
|
|
};
|
|
} /*namespace*/
|
|
|
|
TEST_CASE("toppstr-breaks-at-margin", "[toppstr]") {
|
|
/* the whole point of toppstr: unlike tostr (FlatSink), the margin
|
|
* governs whether the rendering breaks.
|
|
*/
|
|
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
|
|
const Testcase_Toppstr & tc = s_testcase_v[i_tc];
|
|
|
|
std::string s = render_at(tc.margin_);
|
|
|
|
INFO("i_tc=" << i_tc << " margin=" << tc.margin_ << " rendered=[" << s << "]");
|
|
|
|
REQUIRE((s.find('\n') != std::string::npos) == tc.expect_break_);
|
|
|
|
/* breaking must not lose content */
|
|
for (int elt : s_value) {
|
|
INFO("element " << elt);
|
|
REQUIRE(s.find(std::to_string(elt)) != std::string::npos);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("toppstr-wide-margin-matches-tostr", "[toppstr]") {
|
|
/* at a margin wide enough that nothing breaks, toppstr and tostr
|
|
* must agree -- they differ only in whether breaking is possible.
|
|
*/
|
|
REQUIRE(render_at(200) == tostr(s_value));
|
|
}
|
|
|
|
TEST_CASE("toppstr-is-repeatable", "[toppstr]") {
|
|
/* The hazard toppstr exists to make unreachable: two PrettySinks
|
|
* sharing an ArenaConfig name interfere, and the symptom is WRONG
|
|
* INDENTATION in whichever renders second -- a silent wrong answer,
|
|
* not an error. Six files carried a comment warning about it.
|
|
* Rendering the same input repeatedly must give the same result.
|
|
*/
|
|
constexpr int c_n = 8;
|
|
|
|
std::string first = render_at(20);
|
|
|
|
for (int i = 0; i < c_n; ++i) {
|
|
INFO("iteration " << i << " first=[" << first << "]");
|
|
REQUIRE(render_at(20) == first);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("toppstr-carries-style", "[toppstr][PpStyle]") {
|
|
/* The seam PpStyle exists to close: the style is set on a PpConfig
|
|
* (xo-indentlog2) and consumed by a Prettifier that sees only a
|
|
* PpSink (xo-ppsink, one subsystem BELOW). PrettySink installs it
|
|
* on itself at construction; nothing else connects the two, so a
|
|
* dropped with_style() would silently render in the default.
|
|
*
|
|
* tag() and field() are rendered directly, since they ARE the two
|
|
* consumers -- and they take the two different colors, which is the
|
|
* distinction being checked.
|
|
*
|
|
* Styles are set explicitly rather than read from the ambient
|
|
* default, which the utest main has replaced with plain().
|
|
*/
|
|
PpStyle style;
|
|
style.tag_color = color_spec_type::ansi(31); /* red */
|
|
style.struct_tag_color = color_spec_type::ansi(32); /* green */
|
|
|
|
int x = 1; /* named: field() captures by reference */
|
|
|
|
{
|
|
auto ppc = PpConfig::plain().with_style(style);
|
|
|
|
REQUIRE(toppstr(ppc, xo::pp::tag("k", x))
|
|
== "\033[31m:k\033[0m 1");
|
|
REQUIRE(toppstr(ppc, xo::pp::field("k", x))
|
|
== "\033[32m:k\033[0m 1");
|
|
}
|
|
|
|
{
|
|
auto ppc = PpConfig::plain();
|
|
|
|
/* ... and both render bare under plain() */
|
|
REQUIRE(toppstr(ppc, xo::pp::tag("k", x))
|
|
== ":k 1");
|
|
REQUIRE(toppstr(ppc, xo::pp::field("k", x))
|
|
== ":k 1");
|
|
}
|
|
}
|
|
|
|
TEST_CASE("toppstr-no-config-is-colored", "[toppstr][PpStyle]") {
|
|
/* The no-config overload picks its OWN config, so nothing else in
|
|
* this file pins what style that is -- every other test passes one
|
|
* explicitly, in order to pin text. Without this case the overload
|
|
* can be switched between PpConfig::plain() and PpConfig::colored()
|
|
* and the suite stays green: exactly what happened in 522799d7,
|
|
* where it silently became plain(). See
|
|
* .xo-backlog/xo-indentlog2/issues/05.
|
|
*
|
|
* RC's call: a bare toppstr(x) COLOURS, matching toppstr2 and the
|
|
* PpConfig-taking overload, so migrating a call site off legacy
|
|
* never loses colour by itself.
|
|
*
|
|
* Values OBSERVED, not predicted. Note this is deliberately
|
|
* independent of PpStyle::default_style(), which the utest main has
|
|
* replaced with plain() -- PpConfig::colored() names its colours.
|
|
*/
|
|
int x = 1; /* named: field() captures by reference */
|
|
|
|
REQUIRE(toppstr(xo::pp::tag("k", x)) == "\033[38;5;245m:k\033[0m 1");
|
|
REQUIRE(toppstr(xo::pp::field("k", x)) == "\033[33m:k\033[0m 1");
|
|
|
|
/* ... and it is PpConfig::colored() specifically that it picks */
|
|
REQUIRE(toppstr(xo::pp::tag("k", x))
|
|
== toppstr(PpConfig::colored(), xo::pp::tag("k", x)));
|
|
REQUIRE(toppstr(xo::pp::field("k", x))
|
|
== toppstr(PpConfig::colored(), xo::pp::field("k", x)));
|
|
}
|
|
|
|
TEST_CASE("toppstr-overloads", "[toppstr]") {
|
|
/* the no-config overload must not be shadowed by the
|
|
* PpConfig-taking one, nor vice versa
|
|
*/
|
|
REQUIRE(toppstr(123) == "123");
|
|
REQUIRE(toppstr(PpConfig::plain(), 123) == "123");
|
|
}
|
|
} /*namespace ut*/
|
|
} /*namespace xo*/
|
|
|
|
/* end toppstr.test.cpp */
|