/* @file fixed.test.cpp */ #include "xo/indentlog/scope.hpp" #include "xo/indentlog/print/quoted.hpp" //#include "xo/indentlog/print/tag.hpp" #include "xo/indentlog/print/hex.hpp" #include #include namespace ut { using namespace xo; using namespace xo::print; struct quot_tcase { quot_tcase() = default; quot_tcase(std::string x, bool unq_flag, std::string s) : x_{std::move(x)}, unq_flag_{unq_flag}, s_{std::move(s)} {} /* string to be printed-in-machine-readable-form */ std::string x_; /* if true: omit surrounding " chars when unambiguous * (printed string does not contain spaces or escaped chars) * if false: always require surrounding " chars */ bool unq_flag_ = true; /* expected result */ std::string s_; }; /*quot_tcase*/ /* NOTE: spelled out tests here in aftermath * of hard-to-diagnose regression in gcc 13.2; * turned out to have something to originate in confusion * between xo::print::quoted and std::quoted. * * Problem does not occur in gcc 12.3 and earlier, * perhaps some alias for std::quoted appears somewhere in global * namespace?? * * Resolved by renaming xo::print::quoted -> xo::print::quot */ TEST_CASE("sstream.1char", "[sstream]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.sstream.1char")); /* testing unexpected sstream behavior */ { std::stringstream ss; log && log("empty stream"); ss << '\\'; log && log("after: lone escaped backslash"); log && log(hex_view(ss.view().begin(), ss.view().end(), true)); REQUIRE(ss.view() == std::string_view("\\")); ss << 'n'; log && log("after: lone 'n' char"); log && log(hex_view(ss.view().begin(), ss.view().end(), true)); REQUIRE(ss.view() == std::string_view("\\n")); log && log("ss.str()=[", ss.str(), "]"); } } /*TEST_CASE(sstream.1char)*/ TEST_CASE("sstream.2bslash", "[sstream]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.sstream.2bslash")); /* testing unexpected sstream behavior */ { std::stringstream ss; log && log("empty stream"); ss << "\\\\"; log && log("after: 2x escaped backslash"); log && log(hex_view(ss.view().begin(), ss.view().end(), true)); REQUIRE(ss.view() == std::string_view("\\\\")); log && log("ss.str()=[", ss.str(), "]"); } } /*TEST_CASE(sstream.2bslash)*/ TEST_CASE("sstream.2char", "[sstream]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.sstream.2char")); /* testing unexpected sstream behavior */ { std::stringstream ss; log && log("empty stream"); ss << "\\n"; log && log("after: '\\n' escaped backslash + n"); log && log(hex_view(ss.view().begin(), ss.view().end(), true)); } } TEST_CASE("sstream.3char", "[sstream]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.sstream.3char")); /* testing unexpected sstream behavior */ { std::stringstream ss; log && log("empty stream"); /* this is what quot("\\n") should wind up executing.. */ ss << "\\\\"; ss << 'n'; log && log("after: '\\\\n' 2x escaped backslash + n"); log && log(hex_view(ss.view().begin(), ss.view().end(), true)); REQUIRE(ss.view() == std::string_view("\\\\n")); } } TEST_CASE("sstream.quot.1bslash", "[quot]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.sstream.quot.1bslash")); log && log("quot(\"\\\")=[", quot("\\"), "]"); std::stringstream ss2; ss2 << quot("\\"); REQUIRE(ss2.view() == std::string_view("\"\\\\\"")); /* ["\\"] */ } TEST_CASE("sstream.quot.newline", "[quot]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.sstream.quot.newline")); log && log("quot(\"\\n\")=[", quot("\n"), "]"); std::stringstream ss2; ss2 << quot("\n"); REQUIRE(ss2.view() == std::string_view("\"\\n\"")); /* ["\n"] */ } TEST_CASE("sstream.quot.2bslash", "[quot]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.quot.2bslash")); log && log("quot(\"\\\\\")=[", quot("\\\\"), "]"); std::stringstream ss2; ss2 << quot("\\\\"); /* quoting string with two backslashes need to give ["\\\\"] */ REQUIRE(ss2.view() == std::string_view("\"\\\\\\\\\"")); /* rhs is ["\\\\"] */ } TEST_CASE("sstream.quot.2charnewline", "[quot]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.sstream.quot.2charnewline")); log && log("quot(\"x\\n\")=[", quot("x\n"), "]"); std::stringstream ss2; ss2 << quot("x\n"); REQUIRE(ss2.view() == std::string_view("\"x\\n\"")); /* ["\n"] */ } TEST_CASE("sstream.quot.2char", "[quot]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.quot.2char")); log && log("quot(\"\\n\")=[", quot("\\n"), "]"); std::stringstream ss2; ss2 << quot("\\n"); //std::cerr << quoted_debug::s_log_last_quoted.view() << std::endl; //log && log("debug_log=[", quoted_debug::s_log_last_quoted.view() , "]"); REQUIRE(ss2.view() == std::string_view("\"\\\\n\"")); } TEST_CASE("sstream.quot.foonewline", "[quot]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.sstream.quot.2charnewline")); std::stringstream ss2; ss2 << quot("foo\n"); REQUIRE(ss2.view() == std::string_view("\"foo\\n\"")); /* ["\n"] */ } TEST_CASE("sstream.rest", "[quot]") { constexpr bool c_debug_flag = false; scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.sstream")); /* testing unexpected sstream behavior */ { std::stringstream ss; log && log("empty stream"); ss << quot("\n"); log && log("after: quot('\\n')"); log && log(hex_view(ss.view().begin(), ss.view().end(), true)); } /* testing unexpected sstream behavior */ { std::stringstream ss; log && log("empty stream"); ss << quot("foo\n"); log && log("after: quot(\"foo\n\")"); log && log(hex_view(ss.view().begin(), ss.view().end(), true)); log && log("> ss.str ----------------"); log && log(ss.str()); log && log("< ss.str ----------------"); } /* testing unexpected sstream behavior */ { std::stringstream ss; log && log("empty stream"); ss << unq("\n"); log && log("after: unq('\\n')"); log && log(hex_view(ss.view().begin(), ss.view().end(), true)); } } /*TEST_CASE(sstream)*/ std::vector s_quot_tcase_v( { quot_tcase("", true, "\"\""), quot_tcase("", false, "\"\""), quot_tcase("foo", true, "foo"), quot_tcase("foo", false, "\"foo\""), quot_tcase("foo\n", true, "\"foo\\n\""), quot_tcase("foo\n", false, "\"foo\\n\""), quot_tcase("two words", true, "\"two words\""), quot_tcase("two words", false, "\"two words\""), quot_tcase("1st\n2nd", true, "\"1st\\n2nd\""), quot_tcase("1st\n2nd", false, "\"1st\\n2nd\""), quot_tcase("misakte\rfix", true, "\"misakte\\rfix\""), quot_tcase("misakte\rfix", false, "\"misakte\\rfix\""), quot_tcase("\"oh!\", she said", true, "\"\\\"oh!\\\", she said\""), quot_tcase("\"oh!\", she said", false, "\"\\\"oh!\\\", she said\""), // special carveout for strings bracketed by <..>; assume already well-formed quot_tcase("", true, ""), quot_tcase("", false, ""), }); TEST_CASE("quot", "[quot]") { for (std::uint32_t i_tc = 0, z_tc = s_quot_tcase_v.size(); i_tc < z_tc; ++i_tc) { quot_tcase const & tc = s_quot_tcase_v[i_tc]; /* NOTE: don't use tag()/xtag() here, * since implementation relies on the inserter we are testing */ INFO(tostr("i_tc=", i_tc, " unq_flag=", tc.unq_flag_)); INFO("tc.x_ ----------------"); INFO(tostr("[", tc.x_, "]")); INFO("tc.x_ ----------------"); std::stringstream ss; if (tc.unq_flag_) ss << unq(tc.x_); else ss << quot(tc.x_); INFO("tc.s ----------------"); INFO(tostr("[", tc.s_, "]")); INFO("tc.s ----------------"); INFO("ss.str ----------------"); INFO(tostr("[", hex_view(ss.view().begin(), ss.view().end(), true), "]")); INFO(tostr("[", ss.str(), "]")); INFO("ss.str ----------------"); REQUIRE(ss.str() == tc.s_); if (ss.str() != tc.s_) break; } REQUIRE(s_quot_tcase_v.size() > 1); } } /*namespace ut*/ /* end quoted.test.cpp */