xo-umbrella2/xo-ppsink/utest/PpStyle.test.cpp

174 lines
5.6 KiB
C++

/** @file PpStyle.test.cpp
*
* @author Roland Conybeare, Aug 2026
*
* PpStyle: presentation choices carried by a sink (xo/ppsink/PpStyle.hpp).
*
* THIS FILE IS THE ONE PLACE THE DEFAULTS ARE ASSERTED. Every utest main in
* the tree installs PpStyle::plain() so that expectations can pin readable
* text, which means the real defaults are exercised nowhere else -- they could
* be changed to none() and the entire suite would stay green. That is exactly
* how ppsink's color gate came to be false unnoticed.
*
* So these cases construct the styles they assert on, rather than reading the
* ambient default.
**/
#include <xo/ppsink/FlatSink.hpp>
#include <xo/ppsink/PpStyle.hpp>
#include <xo/ppsink/pretty_struct.hpp>
#include <catch2/catch.hpp>
#include <sstream>
#include <string>
namespace ut {
using xo::pp::FlatSink;
using xo::pp::PpStyle;
using xo::pp::color_encoding;
using xo::pp::color_spec_type;
using xo::pp::default_style_guard;
using xo::pp::field;
using xo::pp::tag;
using std::stringstream;
TEST_CASE("PpStyle-defaults", "[PpStyle]")
{
/* a freshly constructed PpStyle, NOT PpStyle::default_style() -- the
* utest main has replaced that one
*/
PpStyle dflt;
/* grey, matching legacy xo::tag_config::tag_color */
REQUIRE(dflt.tag_color.encoding() == color_encoding::xterm);
REQUIRE(dflt.tag_color.code() == 245);
/* yellow, matching legacy pretty_struct -- and DISTINCT from tag_color,
* which is the point of having two
*/
REQUIRE(dflt.struct_tag_color.encoding() == color_encoding::ansi);
REQUIRE(dflt.struct_tag_color.code() == 33);
REQUIRE(dflt.tag_value_offset == 1);
}
TEST_CASE("PpStyle-plain-is-colorless", "[PpStyle]")
{
PpStyle plain = PpStyle::plain();
REQUIRE(plain.tag_color.encoding() == color_encoding::none);
REQUIRE(plain.struct_tag_color.encoding() == color_encoding::none);
/* layout is NOT a color decision: plain() must not disturb it */
REQUIRE(plain.tag_value_offset == PpStyle().tag_value_offset);
}
TEST_CASE("PpStyle-tag-vs-struct-field", "[PpStyle]")
{
/* the distinction the two colors exist for: the same ":name value"
* shape renders in different colors depending on whether it is a log
* tag or a struct field.
*/
PpStyle style;
style.tag_color = color_spec_type::ansi(31); /* red */
style.struct_tag_color = color_spec_type::ansi(32); /* green */
int x = 1;
stringstream tag_ss;
{
FlatSink sink(style, tag_ss.rdbuf());
sink.pp(tag("k", x));
}
stringstream field_ss;
{
FlatSink sink(style, field_ss.rdbuf());
sink.pretty_struct("P", field("k", x));
}
REQUIRE(tag_ss.str() == "\033[31m:k\033[0m 1");
REQUIRE(field_ss.str() == "<P \033[32m:k\033[0m 1>");
}
TEST_CASE("PpStyle-per-sink-not-global", "[PpStyle]")
{
/* two sinks alive at once, styled differently. This is what the old
* process-wide tag_config could not do, and the reason PpStyle is held
* by value on the sink.
*/
PpStyle red;
red.tag_color = color_spec_type::ansi(31);
stringstream styled_ss, plain_ss;
FlatSink styled(red, styled_ss.rdbuf());
FlatSink plain(PpStyle::plain(), plain_ss.rdbuf());
int x = 1;
styled.pp(tag("k", x));
plain.pp(tag("k", x));
REQUIRE(styled_ss.str() == "\033[31m:k\033[0m 1");
REQUIRE(plain_ss.str() == ":k 1");
}
TEST_CASE("PpStyle-sink-copies-defaults-at-construction", "[PpStyle]")
{
/* a sink takes the defaults WHEN IT IS BUILT. Someone who sets the
* defaults late, after building a sink, gets nothing -- worth pinning,
* because the failure is silent.
*/
PpStyle red;
red.tag_color = color_spec_type::ansi(31);
stringstream before_ss, after_ss;
FlatSink before(before_ss.rdbuf()); /* built under plain (the utest main) */
default_style_guard g(red);
FlatSink after(after_ss.rdbuf()); /* built under red */
int x = 1;
before.pp(tag("k", x));
after.pp(tag("k", x));
REQUIRE(before_ss.str() == ":k 1");
REQUIRE(after_ss.str() == "\033[31m:k\033[0m 1");
}
TEST_CASE("PpStyle-default-style-guard-restores", "[PpStyle]")
{
PpStyle orig = PpStyle::default_style();
{
PpStyle red;
red.tag_color = color_spec_type::ansi(31);
default_style_guard g(red);
REQUIRE(PpStyle::default_style().tag_color.code() == 31);
}
REQUIRE(PpStyle::default_style().tag_color.encoding() == orig.tag_color.encoding());
REQUIRE(PpStyle::default_style().tag_color.code() == orig.tag_color.code());
}
TEST_CASE("PpStyle-tag-value-offset", "[PpStyle]")
{
/* tag_value_offset is layout, not color: it decides how far a broken
* value hangs below its ":name". Only visible through a sink that
* breaks, so the rendered check lives in xo-indentlog2; here we pin
* that the field is reached from the sink at all.
*/
PpStyle style;
style.tag_value_offset = 4;
stringstream ss;
FlatSink sink(style, ss.rdbuf());
REQUIRE(sink.style().tag_value_offset == 4);
REQUIRE(FlatSink(ss.rdbuf()).style().tag_value_offset == 1); /* untouched sink */
}
} /*namespace ut*/
/* end PpStyle.test.cpp */