241 lines
6.9 KiB
C++
241 lines
6.9 KiB
C++
#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 <cstdio>
|
|
#include <limits>
|
|
|
|
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 Char>
|
|
class rexy::formatter<X,Char> : public rexy::formatter<int,Char>
|
|
{
|
|
public:
|
|
template<class FormatContext>
|
|
auto format(X&, FormatContext& ctx) -> decltype(ctx.out()){
|
|
*(ctx.out()++) = 'X';
|
|
return std::move(ctx.out());
|
|
//return rexy::formatter<int,Char>::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<float>::infinity());
|
|
PERFORM_TEST("-nan"_sv, "{:.1f}", -std::numeric_limits<float>::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<float>::infinity());
|
|
PERFORM_TEST("nan", "{:06}", std::numeric_limits<float>::quiet_NaN());
|
|
}
|
|
|
|
void do_type_specifiers(void){
|
|
//integers
|
|
PERFORM_TEST("0b1000", "{:b}", 8);
|
|
PERFORM_TEST("0B1000", "{:B}", 8);
|
|
PERFORM_TEST("a", "{:c}", static_cast<int>('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<char>(5));
|
|
PERFORM_TEST("0B101", "{:B}", static_cast<char>(5));
|
|
PERFORM_TEST("5", "{:d}", static_cast<char>(5));
|
|
PERFORM_TEST("0", "{:o}", static_cast<char>(0));
|
|
PERFORM_TEST("05", "{:o}", static_cast<char>(5));
|
|
PERFORM_TEST("0xa", "{:x}", static_cast<char>(10));
|
|
PERFORM_TEST("0XA", "{:X}", static_cast<char>(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();
|
|
rexy::debug::print_succ("Format test success\n");
|
|
}
|
|
|