245 lines
8.7 KiB
C++
245 lines
8.7 KiB
C++
/** @file PpState.test.cpp
|
|
*
|
|
* @author Roland Conybeare, Jul 2026
|
|
**/
|
|
|
|
#include "print/PpState.hpp"
|
|
#include "LogBuffer.hpp"
|
|
#include "print/PpTokenType.hpp"
|
|
#include <xo/testutil/try_test_array.hpp>
|
|
#include <xo/arena/ArenaConfig.hpp>
|
|
#include <catch2/catch.hpp>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <iostream> /* std::cout -- was arriving via xo/reflectutil/typeseq.hpp */
|
|
|
|
namespace ut {
|
|
using xo::pp::PpState;
|
|
using xo::pp::PpConfig;
|
|
using xo::pp::PpLayoutConfig;
|
|
using xo::pp::PpTokenFlags;
|
|
using xo::pp::k_nominal;
|
|
using xo::pp::k_string;
|
|
using xo::pp::k_begin;
|
|
using xo::pp::k_split;
|
|
using xo::pp::k_end;
|
|
using xo::mm::MemorySizeInfo;
|
|
using xo::mm::ArenaConfig;
|
|
using xo::LogBuffer;
|
|
using xo::flatstring;
|
|
using xo::UtestRehearser;
|
|
using std::cout;
|
|
using std::endl;
|
|
using std::vector;
|
|
using std::size_t;
|
|
|
|
struct PpTokenInput {
|
|
PpTokenInput(PpTokenFlags tk_type, const char * tk_str)
|
|
: tk_type_{tk_type}, tk_str_{tk_str}
|
|
{}
|
|
|
|
static PpTokenInput string(const char * tk_str) {
|
|
return PpTokenInput(k_string, tk_str);
|
|
}
|
|
static PpTokenInput begin() { return PpTokenInput(k_begin, nullptr); }
|
|
static PpTokenInput split() { return PpTokenInput(k_split, nullptr); }
|
|
static PpTokenInput end() { return PpTokenInput(k_end, nullptr); }
|
|
|
|
PpTokenFlags tk_type_ = k_nominal;
|
|
const char * tk_str_ = nullptr;
|
|
};
|
|
|
|
/** Unit test setup.
|
|
* Test sequence:
|
|
* emit each string in token_v_ to a PpState via put_cstr(),
|
|
* with output connected to a fresh LogBuffer;
|
|
* then expect the LogBuffer to hold exactly exp_output_.
|
|
**/
|
|
struct TestCase_PpState {
|
|
TestCase_PpState(std::vector<PpTokenInput> token_v,
|
|
const char * exp_output,
|
|
std::uint32_t soft_right_margin = 0)
|
|
: token_v_{token_v},
|
|
exp_output_{exp_output},
|
|
soft_right_margin_{soft_right_margin}
|
|
{}
|
|
|
|
/** tokens emitted to the pretty printer via put_cstr **/
|
|
std::vector<PpTokenInput> token_v_;
|
|
/** expected pretty-printer output (LogBuffer contents) **/
|
|
const char * exp_output_ = nullptr;
|
|
/** soft right margin for PpConfig; 0 => use PpConfig default **/
|
|
std::uint32_t soft_right_margin_ = 0;
|
|
};
|
|
|
|
vector<TestCase_PpState>
|
|
s_ppstate_testcase_v = {
|
|
/* test case: single top-level token flushes verbatim */
|
|
TestCase_PpState({PpTokenInput::string("foo")},
|
|
"foo"),
|
|
|
|
/* test case: empty group -- establishes size 0, must not block printing */
|
|
TestCase_PpState({PpTokenInput::begin(),
|
|
PpTokenInput::end()},
|
|
""),
|
|
|
|
/* test case: group that fits within margin -- split collapses to no-op */
|
|
TestCase_PpState({PpTokenInput::begin(),
|
|
PpTokenInput::string("foo,"),
|
|
PpTokenInput::split(),
|
|
PpTokenInput::string("bar"),
|
|
PpTokenInput::end()},
|
|
"foo,bar"),
|
|
|
|
/* test case: group too wide for soft_right_margin (=4) -- split becomes
|
|
* newline + indent (indent_width default 2, nesting depth 1 => 2 spaces).
|
|
*/
|
|
TestCase_PpState({PpTokenInput::begin(),
|
|
PpTokenInput::string("foo,"),
|
|
PpTokenInput::split(),
|
|
PpTokenInput::string("bar"),
|
|
PpTokenInput::end()},
|
|
"foo,\n bar",
|
|
4 /*soft_right_margin*/),
|
|
|
|
/* test case: function call
|
|
* foo(apple: 1,
|
|
* banana: 2,
|
|
* carrot: 3);
|
|
*/
|
|
TestCase_PpState(
|
|
{PpTokenInput::string("foo"),
|
|
PpTokenInput::begin(),
|
|
PpTokenInput::string("(apple: 1,"),
|
|
PpTokenInput::split(),
|
|
PpTokenInput::string(" banana: 2,"),
|
|
PpTokenInput::split(),
|
|
PpTokenInput::string(" carrot: 3)"),
|
|
PpTokenInput::end()},
|
|
"foo(apple: 1,\n banana: 2,\n carrot: 3)",
|
|
16 /*soft_right_margin*/),
|
|
|
|
/* test case: function call + color escapes
|
|
* foo(apple: 1,
|
|
* banana: 2,
|
|
* carrot: 3);
|
|
*/
|
|
TestCase_PpState(
|
|
{PpTokenInput::string("foo"),
|
|
PpTokenInput::begin(),
|
|
PpTokenInput::string("("),
|
|
PpTokenInput::string("\033[38;2;180;180;180m"),
|
|
PpTokenInput::string("apple:"),
|
|
PpTokenInput::string("\033[0m"),
|
|
PpTokenInput::string(" 1,"),
|
|
PpTokenInput::split(),
|
|
PpTokenInput::string(" "),
|
|
PpTokenInput::string("\033[38;2;180;180;180m"),
|
|
PpTokenInput::string("banana:"),
|
|
PpTokenInput::string("\033[0m"),
|
|
PpTokenInput::string(" 2,"),
|
|
PpTokenInput::split(),
|
|
PpTokenInput::string(" "),
|
|
PpTokenInput::string("\033[38;2;180;180;180m"),
|
|
PpTokenInput::string("carrot:"),
|
|
PpTokenInput::string("\033[0m"),
|
|
PpTokenInput::string(" 3)"),
|
|
PpTokenInput::end()},
|
|
"foo(\033[38;2;180;180;180mapple:\033[0m 1,\n \033[38;2;180;180;180mbanana:\033[0m 2,\n \033[38;2;180;180;180mcarrot:\033[0m 3)",
|
|
16 /*soft_right_margin*/),
|
|
};
|
|
|
|
void
|
|
ppstate_test_fn(const TestCase_PpState & tc,
|
|
UtestRehearser * p_rh)
|
|
{
|
|
PpLayoutConfig cfg;
|
|
if (tc.soft_right_margin_ > 0)
|
|
cfg = cfg.with_soft_right_margin(tc.soft_right_margin_);
|
|
|
|
PpState pps(cfg);
|
|
|
|
ArenaConfig buf_cfg { .name_ = flatstring("utest.PpState"),
|
|
.size_ = 64*1024 };
|
|
LogBuffer logbuf(buf_cfg, p_rh->enable_debug());
|
|
|
|
pps.connect_output(&logbuf);
|
|
|
|
for (const PpTokenInput & tki : tc.token_v_) {
|
|
switch (tki.tk_type_) {
|
|
case k_string:
|
|
pps.put_cstr(tki.tk_str_);
|
|
break;
|
|
case k_begin:
|
|
pps.begin();
|
|
break;
|
|
case k_split:
|
|
pps.split();
|
|
break;
|
|
case k_end:
|
|
pps.end();
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
LogBuffer::Span used = logbuf.char_used_span();
|
|
|
|
std::string expected = tc.exp_output_;
|
|
|
|
REHEARSE(*p_rh,
|
|
std::string_view(used.lo(), used.hi()) == expected);
|
|
REHEARSE(*p_rh, used.size() == expected.size());
|
|
|
|
{
|
|
MemorySizeInfo tk_buffer_info;
|
|
MemorySizeInfo scan_stack_info;
|
|
MemorySizeInfo print_stack_info;
|
|
bool unexpected = false;
|
|
|
|
auto visitor = [&tk_buffer_info,
|
|
&scan_stack_info,
|
|
&print_stack_info,
|
|
&unexpected]
|
|
(const MemorySizeInfo & info)
|
|
{
|
|
if (info.resource_name_ == flatstring("pp-tk-buffer")) {
|
|
tk_buffer_info = info;
|
|
} else if (info.resource_name_ == flatstring("pp-scan-stack")) {
|
|
scan_stack_info = info;
|
|
} else if (info.resource_name_ == flatstring("pp-print-stack")) {
|
|
print_stack_info = info;
|
|
} else {
|
|
unexpected = true;
|
|
}
|
|
};
|
|
|
|
pps.visit_pools(visitor);
|
|
|
|
REHEARSE(*p_rh, unexpected == false);
|
|
REHEARSE(*p_rh, tk_buffer_info.used_ <= tk_buffer_info.allocated_);
|
|
REHEARSE(*p_rh, tk_buffer_info.allocated_ <= tk_buffer_info.committed_);
|
|
REHEARSE(*p_rh, tk_buffer_info.committed_ <= tk_buffer_info.reserved_);
|
|
|
|
REHEARSE(*p_rh, scan_stack_info.used_ <= scan_stack_info.allocated_);
|
|
REHEARSE(*p_rh, scan_stack_info.allocated_ <= scan_stack_info.committed_);
|
|
REHEARSE(*p_rh, scan_stack_info.committed_ <= scan_stack_info.reserved_);
|
|
|
|
REHEARSE(*p_rh, print_stack_info.used_ <= print_stack_info.allocated_);
|
|
REHEARSE(*p_rh, print_stack_info.allocated_ <= print_stack_info.committed_);
|
|
REHEARSE(*p_rh, print_stack_info.committed_ <= print_stack_info.reserved_);
|
|
};
|
|
}
|
|
|
|
TEST_CASE("PpState", "[PpState]")
|
|
{
|
|
try_test_array(s_ppstate_testcase_v,
|
|
&ppstate_test_fn);
|
|
}
|
|
}
|
|
|
|
/* end PpState.test.cpp */
|