X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ceph%2Fsrc%2Ffmt%2Ftest%2Fxchar-test.cc;fp=ceph%2Fsrc%2Ffmt%2Ftest%2Fxchar-test.cc;h=290042a63b4d656cc4f428a7dbb435364be9706b;hb=1e59de90020f1d8d374046ef9cca56ccd4e806e2;hp=78ecb2c7563026e05f08b6d51b312e184c756be5;hpb=bd41e436e25044e8e83156060a37c23cb661c364;p=ceph.git diff --git a/ceph/src/fmt/test/xchar-test.cc b/ceph/src/fmt/test/xchar-test.cc index 78ecb2c75..290042a63 100644 --- a/ceph/src/fmt/test/xchar-test.cc +++ b/ceph/src/fmt/test/xchar-test.cc @@ -8,14 +8,18 @@ #include "fmt/xchar.h" #include +#include +#include #include "fmt/chrono.h" #include "fmt/color.h" #include "fmt/ostream.h" #include "fmt/ranges.h" -#include "gtest/gtest.h" +#include "gtest-extra.h" // Contains +#include "util.h" // get_locale using fmt::detail::max_value; +using testing::Contains; namespace test_ns { template class test_string { @@ -62,14 +66,16 @@ TYPED_TEST(is_string_test, is_string) { } // std::is_constructible is broken in MSVC until version 2015. -#if !FMT_MSC_VER || FMT_MSC_VER >= 1900 +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900 struct explicitly_convertible_to_wstring_view { explicit operator fmt::wstring_view() const { return L"foo"; } }; TEST(xchar_test, format_explicitly_convertible_to_wstring_view) { - EXPECT_EQ(L"foo", - fmt::format(L"{}", explicitly_convertible_to_wstring_view())); + // Types explicitly convertible to wstring_view are not formattable by + // default because it may introduce ODR violations. + static_assert( + !fmt::is_formattable::value, ""); } #endif @@ -78,7 +84,7 @@ TEST(xchar_test, format) { EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2)); EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc")); EXPECT_EQ(L"z", fmt::format(L"{}", L'z')); - EXPECT_THROW(fmt::format(L"{:*\x343E}", 42), fmt::format_error); + EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error); EXPECT_EQ(L"true", fmt::format(L"{}", true)); EXPECT_EQ(L"a", fmt::format(L"{0}", 'a')); EXPECT_EQ(L"a", fmt::format(L"{0}", L'a')); @@ -87,13 +93,18 @@ TEST(xchar_test, format) { EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1)); } +TEST(xchar_test, is_formattable) { + static_assert(!fmt::is_formattable::value, ""); +} + TEST(xchar_test, compile_time_string) { -#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L - EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42)); + EXPECT_EQ(fmt::format(fmt::wformat_string(L"{}"), 42), L"42"); +#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L + EXPECT_EQ(fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42), L"42"); #endif } -#if __cplusplus > 201103L +#if FMT_CPLUSPLUS > 201103L struct custom_char { int value; custom_char() = default; @@ -175,11 +186,6 @@ TEST(format_test, wide_format_to_n) { } #if FMT_USE_USER_DEFINED_LITERALS -TEST(xchar_test, format_udl) { - using namespace fmt::literals; - EXPECT_EQ(L"{}c{}"_format(L"ab", 1), fmt::format(L"{}c{}", L"ab", 1)); -} - TEST(xchar_test, named_arg_udl) { using namespace fmt::literals; auto udl_a = @@ -210,40 +216,38 @@ std::wostream& operator<<(std::wostream& os, streamable_enum) { return os << L"streamable_enum"; } +namespace fmt { +template <> +struct formatter : basic_ostream_formatter { +}; +} // namespace fmt + enum unstreamable_enum {}; +auto format_as(unstreamable_enum e) -> int { return e; } TEST(xchar_test, enum) { EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum())); EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum())); } -TEST(xchar_test, sign_not_truncated) { - wchar_t format_str[] = { - L'{', L':', - '+' | static_cast(1 << fmt::detail::num_bits()), L'}', 0}; - EXPECT_THROW(fmt::format(format_str, 42), fmt::format_error); -} - -namespace fake_qt { -class QString { - public: - QString(const wchar_t* s) : s_(s) {} - const wchar_t* utf16() const FMT_NOEXCEPT { return s_.data(); } - int size() const FMT_NOEXCEPT { return static_cast(s_.size()); } +struct streamable_and_unformattable {}; - private: - std::wstring s_; -}; +auto operator<<(std::wostream& os, streamable_and_unformattable) + -> std::wostream& { + return os << L"foo"; +} -fmt::basic_string_view to_string_view(const QString& s) FMT_NOEXCEPT { - return {s.utf16(), static_cast(s.size())}; +TEST(xchar_test, streamed) { + EXPECT_FALSE(fmt::is_formattable()); + EXPECT_EQ(fmt::format(L"{}", fmt::streamed(streamable_and_unformattable())), + L"foo"); } -} // namespace fake_qt -TEST(format_test, format_foreign_strings) { - using fake_qt::QString; - EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42"); - EXPECT_EQ(fmt::format(QString(L"{}"), QString(L"42")), L"42"); +TEST(xchar_test, sign_not_truncated) { + wchar_t format_str[] = { + L'{', L':', + '+' | static_cast(1 << fmt::detail::num_bits()), L'}', 0}; + EXPECT_THROW(fmt::format(fmt::runtime(format_str), 42), fmt::format_error); } TEST(xchar_test, chrono) { @@ -257,6 +261,60 @@ TEST(xchar_test, chrono) { EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), "The date is 2016-04-25 11:22:33."); EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); + EXPECT_EQ(fmt::format(L"{:%F}", tm), L"2016-04-25"); + EXPECT_EQ(fmt::format(L"{:%T}", tm), L"11:22:33"); +} + +std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr, + std::locale* locptr = nullptr) { + auto loc = locptr ? *locptr : std::locale::classic(); + auto& facet = std::use_facet>(loc); + std::wostringstream os; + os.imbue(loc); + facet.put(os, os, L' ', timeptr, format.c_str(), + format.c_str() + format.size()); +#ifdef _WIN32 + // Workaround a bug in older versions of Universal CRT. + auto str = os.str(); + if (str == L"-0000") str = L"+0000"; + return str; +#else + return os.str(); +#endif +} + +TEST(chrono_test_wchar, time_point) { + auto t1 = std::chrono::system_clock::now(); + + std::vector spec_list = { + L"%%", L"%n", L"%t", L"%Y", L"%EY", L"%y", L"%Oy", L"%Ey", L"%C", + L"%EC", L"%G", L"%g", L"%b", L"%h", L"%B", L"%m", L"%Om", L"%U", + L"%OU", L"%W", L"%OW", L"%V", L"%OV", L"%j", L"%d", L"%Od", L"%e", + L"%Oe", L"%a", L"%A", L"%w", L"%Ow", L"%u", L"%Ou", L"%H", L"%OH", + L"%I", L"%OI", L"%M", L"%OM", L"%S", L"%OS", L"%x", L"%Ex", L"%X", + L"%EX", L"%D", L"%F", L"%R", L"%T", L"%p", L"%z", L"%Z"}; +#ifndef _WIN32 + // Disabled on Windows, because these formats is not consistent among + // platforms. + spec_list.insert(spec_list.end(), {L"%c", L"%Ec", L"%r"}); +#elif defined(__MINGW32__) && !defined(_UCRT) + // Only C89 conversion specifiers when using MSVCRT instead of UCRT + spec_list = {L"%%", L"%Y", L"%y", L"%b", L"%B", L"%m", L"%U", + L"%W", L"%j", L"%d", L"%a", L"%A", L"%w", L"%H", + L"%I", L"%M", L"%S", L"%x", L"%X", L"%p", L"%Z"}; +#endif + spec_list.push_back(L"%Y-%m-%d %H:%M:%S"); + + for (const auto& spec : spec_list) { + auto t = std::chrono::system_clock::to_time_t(t1); + auto tm = *std::localtime(&t); + + auto sys_output = system_wcsftime(spec, &tm); + + auto fmt_spec = fmt::format(L"{{:{}}}", spec); + EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1)); + EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); + } } TEST(xchar_test, color) { @@ -265,9 +323,22 @@ TEST(xchar_test, color) { } TEST(xchar_test, ostream) { +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409 std::wostringstream wos; fmt::print(wos, L"Don't {}!", L"panic"); - EXPECT_EQ(L"Don't panic!", wos.str()); + EXPECT_EQ(wos.str(), L"Don't panic!"); +#endif +} + +TEST(xchar_test, format_map) { + auto m = std::map{{L"one", 1}, {L"t\"wo", 2}}; + EXPECT_EQ(fmt::format(L"{}", m), L"{\"one\": 1, \"t\\\"wo\": 2}"); +} + +TEST(xchar_test, escape_string) { + using vec = std::vector; + EXPECT_EQ(fmt::format(L"{}", vec{L"\n\r\t\"\\"}), L"[\"\\n\\r\\t\\\"\\\\\"]"); + EXPECT_EQ(fmt::format(L"{}", vec{L"понедельник"}), L"[\"понедельник\"]"); } TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } @@ -301,10 +372,13 @@ template struct small_grouping : std::numpunct { Char do_thousands_sep() const override { return ','; } }; -TEST(locale_test, double_decimal_point) { +TEST(locale_test, localized_double) { auto loc = std::locale(std::locale(), new numpunct()); - EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23)); - EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23)); + EXPECT_EQ(fmt::format(loc, "{:L}", 1.23), "1?23"); + EXPECT_EQ(fmt::format(loc, "{:Lf}", 1.23), "1?230000"); + EXPECT_EQ(fmt::format(loc, "{:L}", 1234.5), "1~234?5"); + EXPECT_EQ(fmt::format(loc, "{:L}", 12000.0), "12~000"); + EXPECT_EQ(fmt::format(loc, "{:8L}", 1230.0), " 1~230"); } TEST(locale_test, format) { @@ -403,7 +477,7 @@ template struct formatter, charT> { specs_.precision, specs_.precision_ref, ctx); auto specs = std::string(); if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision); - if (specs_.type) specs += specs_.type; + if (specs_.type == presentation_type::fixed_lower) specs += 'f'; auto real = fmt::format(ctx.locale().template get(), fmt::runtime("{:" + specs + "}"), c.real()); auto imag = fmt::format(ctx.locale().template get(), @@ -424,4 +498,24 @@ TEST(locale_test, complex) { EXPECT_EQ(fmt::format("{:8}", std::complex(1, 2)), " (1+2i)"); } +TEST(locale_test, chrono_weekday) { + auto loc = get_locale("ru_RU.UTF-8", "Russian_Russia.1251"); + auto loc_old = std::locale::global(loc); + auto mon = fmt::weekday(1); + EXPECT_EQ(fmt::format(L"{}", mon), L"Mon"); + if (loc != std::locale::classic()) { + // {L"\x43F\x43D", L"\x41F\x43D", L"\x43F\x43D\x434", L"\x41F\x43D\x434"} + // {L"пн", L"Пн", L"пнд", L"Пнд"} + EXPECT_THAT( + (std::vector{L"\x43F\x43D", L"\x41F\x43D", + L"\x43F\x43D\x434", L"\x41F\x43D\x434"}), + Contains(fmt::format(loc, L"{:L}", mon))); + } + std::locale::global(loc_old); +} + +TEST(locale_test, sign) { + EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50"); +} + #endif // FMT_STATIC_THOUSANDS_SEPARATOR