#define LIBREXY_ENABLE_DEBUG_LEVEL 2 #include "rexy/format.hpp" #include "rexy/string.hpp" #include "rexy/debug_print.hpp" #include "rexy/cx/string.hpp" #include #include using namespace rexy::str_literals; [[noreturn]] void test_fail(rexy::string_view failed, rexy::string_view expected){ rexy::debug::print_error("expected \"%s\", got \"%s\"\n", expected.c_str(), failed.c_str()); exit(-1); } [[noreturn]] void test_fail(void){ exit(-1); } #define PERFORM_TEST(desired, formt, ...) \ do{ \ rexy::string str = rexy::format( (formt), __VA_ARGS__); \ if(str != (desired) ){ \ test_fail( (str), (desired) ); \ } \ }while(0) struct X { int i; constexpr operator int(void)const{return i;} }; template class rexy::formatter : public rexy::formatter { public: template auto format(X&, FormatContext& ctx) -> decltype(ctx.out()){ *(ctx.out()++) = 'X'; return std::move(ctx.out()); //return rexy::formatter::format(t.i, ctx); } }; void do_brace_escapes(void){ PERFORM_TEST("this is } a test string"_sv, "this is }} a test string", nullptr); PERFORM_TEST("this is a test {string"_sv, "this is a test {{string", nullptr); PERFORM_TEST("this }is a test {string"_sv, "this }}is a test {{string", nullptr); PERFORM_TEST("this {is a tes}t string"_sv, "this {{is a tes}}t string", nullptr); } void do_empty_format(void){ PERFORM_TEST("0"_sv, "{}", 0); PERFORM_TEST("0"_sv, "{}", 0ul); PERFORM_TEST("0"_sv, "{}", short{0}); PERFORM_TEST("0"_sv, "{}", 0.0f); PERFORM_TEST("0x0"_sv, "{}", nullptr); PERFORM_TEST("true"_sv, "{}", true); PERFORM_TEST("cstring"_sv, "{}", "cstring"); PERFORM_TEST("string_view"_sv, "{}", "string_view"_sv); PERFORM_TEST("string"_sv, "{}", rexy::string{"string"}); PERFORM_TEST("c"_sv, "{}", 'c'); } void do_index_specifiers(void){ PERFORM_TEST("2"_sv, "{2}", 0, 1, 2); PERFORM_TEST("0"_sv, "{0}", 0, 1, 2); PERFORM_TEST("1"_sv, "{1}", "string", 1, 2.8f); } void do_named_index_specifiers(void){ using namespace rexy::fmt_literals; PERFORM_TEST("2"_sv, "{two}", "zero"_a=0, "one"_a=1, "two"_a=2); PERFORM_TEST("0"_sv, "{zero}", rexy::arg<"zero">(0), rexy::arg<"one">(1), rexy::arg<"two">(2)); PERFORM_TEST("1"_sv, "{named}", "string", "named"_a = 1, 2.8f); } void do_dynamic_index_specifiers(void){ PERFORM_TEST("| 5|"_sv, "|{:{}}|", 5, 10); PERFORM_TEST("| 5.80|"_sv, "|{:{}.{}f}|", 5.8f, 10, 2); PERFORM_TEST("| 5|"_sv, "|{1:{0}}|", 10, 5); PERFORM_TEST("| 5.80|"_sv, "|{0:{4}.{1}f}|", 5.8f, 2, 7, 8, 10); } void do_dynamic_named_index_specifiers(void){ using namespace rexy::fmt_literals; PERFORM_TEST("| 5|"_sv, "|{0:{ten}}|", 5, "ten"_a=10); PERFORM_TEST("| 5.80|"_sv, "|{0:{first}.{second}f}|", 5.8f, rexy::arg<"first">(10), "second"_a=2); PERFORM_TEST("| 5|"_sv, "|{1:{arg}}|", "arg"_a=10, 5); PERFORM_TEST("| 5.80|"_sv, "|{value:{width}.{precision}f}|", "value"_a=5.8f, "precision"_a=2, 7, 8, "width"_a=10); } void do_width(void){ //integer right align PERFORM_TEST("| 6|", "|{:6}|", 6); //float right align PERFORM_TEST("| 6.4|", "|{:6}|", 6.4f); //anything els left align PERFORM_TEST("|str |", "|{:6}|", "str"); PERFORM_TEST("|true |", "|{:6}|", true); } void do_precision(void){ //how many decimal digits for floating PERFORM_TEST("6.00000", "{:.5f}", 6.0f); //max width for string PERFORM_TEST("strin", "{:.5}", "string"); } void do_fill_and_align(void){ //basic left, right, center checks PERFORM_TEST("*****6"_sv, "{:*>6}", 6); PERFORM_TEST("6*****"_sv, "{:*<6}", 6); PERFORM_TEST("**6***"_sv, "{:*^6}", 6); //Check types with added info after initial conversion PERFORM_TEST("*8.20*"_sv, "{:*^6.2f}", 8.2f); PERFORM_TEST("-2****"_sv, "{:*<6}", -2); PERFORM_TEST("***str"_sv, "{:*>6}", "str"); } void do_sign(void){ //Positive numbers PERFORM_TEST("6"_sv, "{}", 6); PERFORM_TEST("6"_sv, "{:-}", 6); PERFORM_TEST("+6"_sv, "{:+}", 6); PERFORM_TEST(" 6"_sv, "{: }", 6); //Negative numbers PERFORM_TEST("-6"_sv, "{}", -6); PERFORM_TEST("-6"_sv, "{:-}", -6); PERFORM_TEST("-6"_sv, "{:+}", -6); PERFORM_TEST("-6"_sv, "{: }", -6); //Works for inf and nan and negative zero PERFORM_TEST("-0.0"_sv, "{:.1f}", -0.0f); PERFORM_TEST("-inf"_sv, "{:.1f}", -std::numeric_limits::infinity()); PERFORM_TEST("-nan"_sv, "{:.1f}", -std::numeric_limits::quiet_NaN()); } void do_alt_form(void){ //Put base prefix after the sign for integers PERFORM_TEST("-0x6"_sv, "{:#x}", -6); //Always output decimal point for floating point PERFORM_TEST("6."_sv, "{:#}", 6.0f); //Using 'g' or 'G' type, don't trim trailing zeros PERFORM_TEST("6.000000"_sv, "{:#g}", 6.0f); } void do_zero_pad(void){ PERFORM_TEST("000006"_sv, "{:06}", 6); PERFORM_TEST("0x0006"_sv, "{:06x}", 6); //Overriden by fill align option PERFORM_TEST("*****6"_sv, "{:*>06}", 6); //should not fill for inf or nan PERFORM_TEST("inf", "{:06}", std::numeric_limits::infinity()); PERFORM_TEST("nan", "{:06}", std::numeric_limits::quiet_NaN()); } void do_type_specifiers(void){ //integers PERFORM_TEST("0b1000", "{:b}", 8); PERFORM_TEST("0B1000", "{:B}", 8); PERFORM_TEST("a", "{:c}", static_cast('a')); PERFORM_TEST("8", "{:d}", 8); PERFORM_TEST("0", "{:o}", 0); PERFORM_TEST("010", "{:o}", 8); PERFORM_TEST("0xc", "{:x}", 12); PERFORM_TEST("0XC", "{:X}", 12); //strings PERFORM_TEST("str", "{:s}", "str"_sv); //char PERFORM_TEST("a", "{:c}", 'a'); PERFORM_TEST("0b101", "{:b}", static_cast(5)); PERFORM_TEST("0B101", "{:B}", static_cast(5)); PERFORM_TEST("5", "{:d}", static_cast(5)); PERFORM_TEST("0", "{:o}", static_cast(0)); PERFORM_TEST("05", "{:o}", static_cast(5)); PERFORM_TEST("0xa", "{:x}", static_cast(10)); PERFORM_TEST("0XA", "{:X}", static_cast(10)); //bool PERFORM_TEST("true", "{:s}", true); PERFORM_TEST("false", "{:s}", false); PERFORM_TEST("0b1", "{:b}", true); PERFORM_TEST("0B1", "{:B}", true); PERFORM_TEST("\u0001", "{:c}", true); PERFORM_TEST("1", "{:d}", true); PERFORM_TEST("0", "{:o}", false); PERFORM_TEST("01", "{:o}", true); PERFORM_TEST("0x1", "{:x}", true); PERFORM_TEST("0X1", "{:X}", true); //float PERFORM_TEST("1.ap+2", "{:a}", 6.5f); PERFORM_TEST("1.8dp+2", "{:.2a}", 6.2f); PERFORM_TEST("1.AP+2", "{:A}", 6.5f); PERFORM_TEST("1.8DP+2", "{:.2A}", 6.2f); PERFORM_TEST("6.200000e+00", "{:e}", 6.2f); PERFORM_TEST("6.200000E+00", "{:E}", 6.2f); PERFORM_TEST("6.200000", "{:f}", 6.2f); PERFORM_TEST("6.200000", "{:F}", 6.2f); PERFORM_TEST("6.2", "{:g}", 6.2f); PERFORM_TEST("6.2", "{:G}", 6.2f); //pointer PERFORM_TEST("0x0", "{:p}", nullptr); } //TODO more test coverage int main(){ do_brace_escapes(); do_empty_format(); do_width(); do_precision(); do_index_specifiers(); do_named_index_specifiers(); do_dynamic_index_specifiers(); do_dynamic_named_index_specifiers(); do_fill_and_align(); do_sign(); do_alt_form(); do_zero_pad(); do_type_specifiers(); }