Mercurial > minori
comparison dep/fmt/test/xchar-test.cc @ 343:1faa72660932
*: transfer back to cmake from autotools
autotools just made lots of things more complicated than
they should have and many things broke (i.e. translations)
| author | Paper <paper@paper.us.eu.org> |
|---|---|
| date | Thu, 20 Jun 2024 05:56:06 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 342:adb79bdde329 | 343:1faa72660932 |
|---|---|
| 1 // Formatting library for C++ - formatting library tests | |
| 2 // | |
| 3 // Copyright (c) 2012 - present, Victor Zverovich | |
| 4 // All rights reserved. | |
| 5 // | |
| 6 // For the license information refer to format.h. | |
| 7 | |
| 8 #include "fmt/xchar.h" | |
| 9 | |
| 10 #include <algorithm> | |
| 11 #include <complex> | |
| 12 #include <cwchar> | |
| 13 #include <vector> | |
| 14 | |
| 15 #include "fmt/chrono.h" | |
| 16 #include "fmt/color.h" | |
| 17 #include "fmt/ostream.h" | |
| 18 #include "fmt/ranges.h" | |
| 19 #include "fmt/std.h" | |
| 20 #include "gtest-extra.h" // Contains | |
| 21 #include "util.h" // get_locale | |
| 22 | |
| 23 using fmt::detail::max_value; | |
| 24 using testing::Contains; | |
| 25 | |
| 26 #if defined(__MINGW32__) && !defined(_UCRT) | |
| 27 // Only C89 conversion specifiers when using MSVCRT instead of UCRT | |
| 28 # define FMT_HAS_C99_STRFTIME 0 | |
| 29 #else | |
| 30 # define FMT_HAS_C99_STRFTIME 1 | |
| 31 #endif | |
| 32 | |
| 33 struct non_string {}; | |
| 34 | |
| 35 template <typename T> class is_string_test : public testing::Test {}; | |
| 36 | |
| 37 using string_char_types = testing::Types<char, wchar_t, char16_t, char32_t>; | |
| 38 TYPED_TEST_SUITE(is_string_test, string_char_types); | |
| 39 | |
| 40 template <typename Char> | |
| 41 struct derived_from_string_view : fmt::basic_string_view<Char> {}; | |
| 42 | |
| 43 TYPED_TEST(is_string_test, is_string) { | |
| 44 EXPECT_TRUE(fmt::detail::is_string<TypeParam*>::value); | |
| 45 EXPECT_TRUE(fmt::detail::is_string<const TypeParam*>::value); | |
| 46 EXPECT_TRUE(fmt::detail::is_string<TypeParam[2]>::value); | |
| 47 EXPECT_TRUE(fmt::detail::is_string<const TypeParam[2]>::value); | |
| 48 EXPECT_TRUE(fmt::detail::is_string<std::basic_string<TypeParam>>::value); | |
| 49 EXPECT_TRUE(fmt::detail::is_string<fmt::basic_string_view<TypeParam>>::value); | |
| 50 EXPECT_TRUE( | |
| 51 fmt::detail::is_string<derived_from_string_view<TypeParam>>::value); | |
| 52 using fmt_string_view = fmt::detail::std_string_view<TypeParam>; | |
| 53 EXPECT_TRUE(std::is_empty<fmt_string_view>::value != | |
| 54 fmt::detail::is_string<fmt_string_view>::value); | |
| 55 EXPECT_FALSE(fmt::detail::is_string<non_string>::value); | |
| 56 } | |
| 57 | |
| 58 // std::is_constructible is broken in MSVC until version 2015. | |
| 59 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900 | |
| 60 struct explicitly_convertible_to_wstring_view { | |
| 61 explicit operator fmt::wstring_view() const { return L"foo"; } | |
| 62 }; | |
| 63 | |
| 64 TEST(xchar_test, format_explicitly_convertible_to_wstring_view) { | |
| 65 // Types explicitly convertible to wstring_view are not formattable by | |
| 66 // default because it may introduce ODR violations. | |
| 67 static_assert( | |
| 68 !fmt::is_formattable<explicitly_convertible_to_wstring_view>::value, ""); | |
| 69 } | |
| 70 #endif | |
| 71 | |
| 72 TEST(xchar_test, format) { | |
| 73 EXPECT_EQ(L"42", fmt::format(L"{}", 42)); | |
| 74 EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2)); | |
| 75 EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc")); | |
| 76 EXPECT_EQ(L"z", fmt::format(L"{}", L'z')); | |
| 77 EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error); | |
| 78 EXPECT_EQ(L"true", fmt::format(L"{}", true)); | |
| 79 EXPECT_EQ(L"a", fmt::format(L"{0}", 'a')); | |
| 80 EXPECT_EQ(L"a", fmt::format(L"{0}", L'a')); | |
| 81 EXPECT_EQ(L"Cyrillic letter \x42e", | |
| 82 fmt::format(L"Cyrillic letter {}", L'\x42e')); | |
| 83 EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1)); | |
| 84 } | |
| 85 | |
| 86 TEST(xchar_test, is_formattable) { | |
| 87 static_assert(!fmt::is_formattable<const wchar_t*>::value, ""); | |
| 88 } | |
| 89 | |
| 90 TEST(xchar_test, compile_time_string) { | |
| 91 EXPECT_EQ(fmt::format(fmt::wformat_string<int>(L"{}"), 42), L"42"); | |
| 92 #if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L | |
| 93 EXPECT_EQ(fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42), L"42"); | |
| 94 #endif | |
| 95 } | |
| 96 | |
| 97 #if FMT_CPLUSPLUS > 201103L | |
| 98 struct custom_char { | |
| 99 int value; | |
| 100 custom_char() = default; | |
| 101 | |
| 102 template <typename T> | |
| 103 constexpr custom_char(T val) : value(static_cast<int>(val)) {} | |
| 104 | |
| 105 constexpr operator char() const { | |
| 106 return value <= 0xff ? static_cast<char>(value) : '\0'; | |
| 107 } | |
| 108 constexpr bool operator<(custom_char c) const { return value < c.value; } | |
| 109 }; | |
| 110 | |
| 111 namespace std { | |
| 112 | |
| 113 template <> struct char_traits<custom_char> { | |
| 114 using char_type = custom_char; | |
| 115 using int_type = int; | |
| 116 using off_type = streamoff; | |
| 117 using pos_type = streampos; | |
| 118 using state_type = mbstate_t; | |
| 119 | |
| 120 static constexpr void assign(char_type& r, const char_type& a) { r = a; } | |
| 121 static constexpr bool eq(char_type a, char_type b) { return a == b; } | |
| 122 static constexpr bool lt(char_type a, char_type b) { return a < b; } | |
| 123 static FMT_CONSTEXPR int compare(const char_type* s1, const char_type* s2, | |
| 124 size_t count) { | |
| 125 for (; count; count--, s1++, s2++) { | |
| 126 if (lt(*s1, *s2)) return -1; | |
| 127 if (lt(*s2, *s1)) return 1; | |
| 128 } | |
| 129 return 0; | |
| 130 } | |
| 131 static FMT_CONSTEXPR size_t length(const char_type* s) { | |
| 132 size_t count = 0; | |
| 133 while (!eq(*s++, custom_char(0))) count++; | |
| 134 return count; | |
| 135 } | |
| 136 static const char_type* find(const char_type*, size_t, const char_type&); | |
| 137 static FMT_CONSTEXPR char_type* move(char_type* dest, const char_type* src, | |
| 138 size_t count) { | |
| 139 if (count == 0) return dest; | |
| 140 char_type* ret = dest; | |
| 141 if (src < dest) { | |
| 142 dest += count; | |
| 143 src += count; | |
| 144 for (; count; count--) assign(*--dest, *--src); | |
| 145 } else if (src > dest) | |
| 146 copy(dest, src, count); | |
| 147 return ret; | |
| 148 } | |
| 149 static FMT_CONSTEXPR char_type* copy(char_type* dest, const char_type* src, | |
| 150 size_t count) { | |
| 151 char_type* ret = dest; | |
| 152 for (; count; count--) assign(*dest++, *src++); | |
| 153 return ret; | |
| 154 } | |
| 155 static FMT_CONSTEXPR char_type* assign(char_type* dest, std::size_t count, | |
| 156 char_type a) { | |
| 157 char_type* ret = dest; | |
| 158 for (; count; count--) assign(*dest++, a); | |
| 159 return ret; | |
| 160 } | |
| 161 static int_type not_eof(int_type); | |
| 162 static char_type to_char_type(int_type); | |
| 163 static int_type to_int_type(char_type); | |
| 164 static bool eq_int_type(int_type, int_type); | |
| 165 static int_type eof(); | |
| 166 }; | |
| 167 | |
| 168 } // namespace std | |
| 169 | |
| 170 auto to_ascii(custom_char c) -> char { return c; } | |
| 171 | |
| 172 FMT_BEGIN_NAMESPACE | |
| 173 template <> struct is_char<custom_char> : std::true_type {}; | |
| 174 FMT_END_NAMESPACE | |
| 175 | |
| 176 TEST(xchar_test, format_custom_char) { | |
| 177 const custom_char format[] = {'{', '}', 0}; | |
| 178 auto result = fmt::format(format, custom_char('x')); | |
| 179 EXPECT_EQ(result.size(), 1); | |
| 180 EXPECT_EQ(result[0], custom_char('x')); | |
| 181 } | |
| 182 #endif | |
| 183 | |
| 184 // Convert a char8_t string to std::string. Otherwise GTest will insist on | |
| 185 // inserting `char8_t` NTBS into a `char` stream which is disabled by P1423. | |
| 186 template <typename S> std::string from_u8str(const S& str) { | |
| 187 return std::string(str.begin(), str.end()); | |
| 188 } | |
| 189 | |
| 190 TEST(xchar_test, format_to) { | |
| 191 auto buf = std::vector<wchar_t>(); | |
| 192 fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0'); | |
| 193 EXPECT_STREQ(buf.data(), L"42"); | |
| 194 } | |
| 195 | |
| 196 TEST(xchar_test, vformat_to) { | |
| 197 auto args = fmt::make_wformat_args(42); | |
| 198 auto w = std::wstring(); | |
| 199 fmt::vformat_to(std::back_inserter(w), L"{}", args); | |
| 200 EXPECT_EQ(L"42", w); | |
| 201 } | |
| 202 | |
| 203 namespace test { | |
| 204 struct struct_as_wstring_view {}; | |
| 205 auto format_as(struct_as_wstring_view) -> fmt::wstring_view { return L"foo"; } | |
| 206 } // namespace test | |
| 207 | |
| 208 TEST(xchar_test, format_as) { | |
| 209 EXPECT_EQ(fmt::format(L"{}", test::struct_as_wstring_view()), L"foo"); | |
| 210 } | |
| 211 | |
| 212 TEST(format_test, wide_format_to_n) { | |
| 213 wchar_t buffer[4]; | |
| 214 buffer[3] = L'x'; | |
| 215 auto result = fmt::format_to_n(buffer, 3, L"{}", 12345); | |
| 216 EXPECT_EQ(5u, result.size); | |
| 217 EXPECT_EQ(buffer + 3, result.out); | |
| 218 EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4)); | |
| 219 buffer[0] = L'x'; | |
| 220 buffer[1] = L'x'; | |
| 221 buffer[2] = L'x'; | |
| 222 result = fmt::format_to_n(buffer, 3, L"{}", L'A'); | |
| 223 EXPECT_EQ(1u, result.size); | |
| 224 EXPECT_EQ(buffer + 1, result.out); | |
| 225 EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4)); | |
| 226 result = fmt::format_to_n(buffer, 3, L"{}{} ", L'B', L'C'); | |
| 227 EXPECT_EQ(3u, result.size); | |
| 228 EXPECT_EQ(buffer + 3, result.out); | |
| 229 EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4)); | |
| 230 } | |
| 231 | |
| 232 #if FMT_USE_USER_DEFINED_LITERALS | |
| 233 TEST(xchar_test, named_arg_udl) { | |
| 234 using namespace fmt::literals; | |
| 235 auto udl_a = | |
| 236 fmt::format(L"{first}{second}{first}{third}", L"first"_a = L"abra", | |
| 237 L"second"_a = L"cad", L"third"_a = 99); | |
| 238 EXPECT_EQ( | |
| 239 fmt::format(L"{first}{second}{first}{third}", fmt::arg(L"first", L"abra"), | |
| 240 fmt::arg(L"second", L"cad"), fmt::arg(L"third", 99)), | |
| 241 udl_a); | |
| 242 } | |
| 243 #endif // FMT_USE_USER_DEFINED_LITERALS | |
| 244 | |
| 245 TEST(xchar_test, print) { | |
| 246 // Check that the wide print overload compiles. | |
| 247 if (fmt::detail::const_check(false)) { | |
| 248 fmt::print(L"test"); | |
| 249 fmt::println(L"test"); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 TEST(xchar_test, join) { | |
| 254 int v[3] = {1, 2, 3}; | |
| 255 EXPECT_EQ(fmt::format(L"({})", fmt::join(v, v + 3, L", ")), L"(1, 2, 3)"); | |
| 256 auto t = std::tuple<wchar_t, int, float>('a', 1, 2.0f); | |
| 257 EXPECT_EQ(fmt::format(L"({})", fmt::join(t, L", ")), L"(a, 1, 2)"); | |
| 258 } | |
| 259 | |
| 260 enum streamable_enum {}; | |
| 261 | |
| 262 std::wostream& operator<<(std::wostream& os, streamable_enum) { | |
| 263 return os << L"streamable_enum"; | |
| 264 } | |
| 265 | |
| 266 namespace fmt { | |
| 267 template <> | |
| 268 struct formatter<streamable_enum, wchar_t> : basic_ostream_formatter<wchar_t> { | |
| 269 }; | |
| 270 } // namespace fmt | |
| 271 | |
| 272 enum unstreamable_enum {}; | |
| 273 auto format_as(unstreamable_enum e) -> int { return e; } | |
| 274 | |
| 275 TEST(xchar_test, enum) { | |
| 276 EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum())); | |
| 277 EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum())); | |
| 278 } | |
| 279 | |
| 280 struct streamable_and_unformattable {}; | |
| 281 | |
| 282 auto operator<<(std::wostream& os, streamable_and_unformattable) | |
| 283 -> std::wostream& { | |
| 284 return os << L"foo"; | |
| 285 } | |
| 286 | |
| 287 TEST(xchar_test, streamed) { | |
| 288 EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>()); | |
| 289 EXPECT_EQ(fmt::format(L"{}", fmt::streamed(streamable_and_unformattable())), | |
| 290 L"foo"); | |
| 291 } | |
| 292 | |
| 293 TEST(xchar_test, sign_not_truncated) { | |
| 294 wchar_t format_str[] = { | |
| 295 L'{', L':', | |
| 296 '+' | static_cast<wchar_t>(1 << fmt::detail::num_bits<char>()), L'}', 0}; | |
| 297 EXPECT_THROW(fmt::format(fmt::runtime(format_str), 42), fmt::format_error); | |
| 298 } | |
| 299 | |
| 300 TEST(xchar_test, chrono) { | |
| 301 auto tm = std::tm(); | |
| 302 tm.tm_year = 116; | |
| 303 tm.tm_mon = 3; | |
| 304 tm.tm_mday = 25; | |
| 305 tm.tm_hour = 11; | |
| 306 tm.tm_min = 22; | |
| 307 tm.tm_sec = 33; | |
| 308 EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), | |
| 309 "The date is 2016-04-25 11:22:33."); | |
| 310 EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); | |
| 311 EXPECT_EQ(fmt::format(L"{:%F}", tm), L"2016-04-25"); | |
| 312 EXPECT_EQ(fmt::format(L"{:%T}", tm), L"11:22:33"); | |
| 313 } | |
| 314 | |
| 315 std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr, | |
| 316 std::locale* locptr = nullptr) { | |
| 317 auto loc = locptr ? *locptr : std::locale::classic(); | |
| 318 auto& facet = std::use_facet<std::time_put<wchar_t>>(loc); | |
| 319 std::wostringstream os; | |
| 320 os.imbue(loc); | |
| 321 facet.put(os, os, L' ', timeptr, format.c_str(), | |
| 322 format.c_str() + format.size()); | |
| 323 #ifdef _WIN32 | |
| 324 // Workaround a bug in older versions of Universal CRT. | |
| 325 auto str = os.str(); | |
| 326 if (str == L"-0000") str = L"+0000"; | |
| 327 return str; | |
| 328 #else | |
| 329 return os.str(); | |
| 330 #endif | |
| 331 } | |
| 332 | |
| 333 TEST(chrono_test_wchar, time_point) { | |
| 334 auto t1 = std::chrono::time_point_cast<std::chrono::seconds>( | |
| 335 std::chrono::system_clock::now()); | |
| 336 | |
| 337 std::vector<std::wstring> spec_list = { | |
| 338 L"%%", L"%n", L"%t", L"%Y", L"%EY", L"%y", L"%Oy", L"%Ey", L"%C", | |
| 339 L"%EC", L"%G", L"%g", L"%b", L"%h", L"%B", L"%m", L"%Om", L"%U", | |
| 340 L"%OU", L"%W", L"%OW", L"%V", L"%OV", L"%j", L"%d", L"%Od", L"%e", | |
| 341 L"%Oe", L"%a", L"%A", L"%w", L"%Ow", L"%u", L"%Ou", L"%H", L"%OH", | |
| 342 L"%I", L"%OI", L"%M", L"%OM", L"%S", L"%OS", L"%x", L"%Ex", L"%X", | |
| 343 L"%EX", L"%D", L"%F", L"%R", L"%T", L"%p"}; | |
| 344 #ifndef _WIN32 | |
| 345 // Disabled on Windows, because these formats is not consistent among | |
| 346 // platforms. | |
| 347 spec_list.insert(spec_list.end(), {L"%c", L"%Ec", L"%r"}); | |
| 348 #elif !FMT_HAS_C99_STRFTIME | |
| 349 // Only C89 conversion specifiers when using MSVCRT instead of UCRT | |
| 350 spec_list = {L"%%", L"%Y", L"%y", L"%b", L"%B", L"%m", L"%U", | |
| 351 L"%W", L"%j", L"%d", L"%a", L"%A", L"%w", L"%H", | |
| 352 L"%I", L"%M", L"%S", L"%x", L"%X", L"%p"}; | |
| 353 #endif | |
| 354 spec_list.push_back(L"%Y-%m-%d %H:%M:%S"); | |
| 355 | |
| 356 for (const auto& spec : spec_list) { | |
| 357 auto t = std::chrono::system_clock::to_time_t(t1); | |
| 358 auto tm = *std::gmtime(&t); | |
| 359 | |
| 360 auto sys_output = system_wcsftime(spec, &tm); | |
| 361 | |
| 362 auto fmt_spec = fmt::format(L"{{:{}}}", spec); | |
| 363 EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1)); | |
| 364 EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); | |
| 365 } | |
| 366 | |
| 367 // Timezone formatters tests makes sense for localtime. | |
| 368 #if FMT_HAS_C99_STRFTIME | |
| 369 spec_list = {L"%z", L"%Z"}; | |
| 370 #else | |
| 371 spec_list = {L"%Z"}; | |
| 372 #endif | |
| 373 for (const auto& spec : spec_list) { | |
| 374 auto t = std::chrono::system_clock::to_time_t(t1); | |
| 375 auto tm = *std::localtime(&t); | |
| 376 | |
| 377 auto sys_output = system_wcsftime(spec, &tm); | |
| 378 | |
| 379 auto fmt_spec = fmt::format(L"{{:{}}}", spec); | |
| 380 EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm)); | |
| 381 | |
| 382 if (spec == L"%z") { | |
| 383 sys_output.insert(sys_output.end() - 2, 1, L':'); | |
| 384 EXPECT_EQ(sys_output, fmt::format(L"{:%Ez}", tm)); | |
| 385 EXPECT_EQ(sys_output, fmt::format(L"{:%Oz}", tm)); | |
| 386 } | |
| 387 } | |
| 388 | |
| 389 // Separate tests for UTC, since std::time_put can use local time and ignoring | |
| 390 // the timezone in std::tm (if it presents on platform). | |
| 391 if (fmt::detail::has_member_data_tm_zone<std::tm>::value) { | |
| 392 auto t = std::chrono::system_clock::to_time_t(t1); | |
| 393 auto tm = *std::gmtime(&t); | |
| 394 | |
| 395 std::vector<std::wstring> tz_names = {L"GMT", L"UTC"}; | |
| 396 EXPECT_THAT(tz_names, Contains(fmt::format(L"{:%Z}", t1))); | |
| 397 EXPECT_THAT(tz_names, Contains(fmt::format(L"{:%Z}", tm))); | |
| 398 } | |
| 399 | |
| 400 if (fmt::detail::has_member_data_tm_gmtoff<std::tm>::value) { | |
| 401 auto t = std::chrono::system_clock::to_time_t(t1); | |
| 402 auto tm = *std::gmtime(&t); | |
| 403 | |
| 404 EXPECT_EQ(L"+0000", fmt::format(L"{:%z}", t1)); | |
| 405 EXPECT_EQ(L"+0000", fmt::format(L"{:%z}", tm)); | |
| 406 | |
| 407 EXPECT_EQ(L"+00:00", fmt::format(L"{:%Ez}", t1)); | |
| 408 EXPECT_EQ(L"+00:00", fmt::format(L"{:%Ez}", tm)); | |
| 409 | |
| 410 EXPECT_EQ(L"+00:00", fmt::format(L"{:%Oz}", t1)); | |
| 411 EXPECT_EQ(L"+00:00", fmt::format(L"{:%Oz}", tm)); | |
| 412 } | |
| 413 } | |
| 414 | |
| 415 TEST(xchar_test, color) { | |
| 416 EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"), | |
| 417 L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m"); | |
| 418 } | |
| 419 | |
| 420 TEST(xchar_test, ostream) { | |
| 421 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409 | |
| 422 { | |
| 423 std::wostringstream wos; | |
| 424 fmt::print(wos, L"Don't {}!", L"panic"); | |
| 425 EXPECT_EQ(wos.str(), L"Don't panic!"); | |
| 426 } | |
| 427 | |
| 428 { | |
| 429 std::wostringstream wos; | |
| 430 fmt::println(wos, L"Don't {}!", L"panic"); | |
| 431 EXPECT_EQ(wos.str(), L"Don't panic!\n"); | |
| 432 } | |
| 433 #endif | |
| 434 } | |
| 435 | |
| 436 TEST(xchar_test, format_map) { | |
| 437 auto m = std::map<std::wstring, int>{{L"one", 1}, {L"t\"wo", 2}}; | |
| 438 EXPECT_EQ(fmt::format(L"{}", m), L"{\"one\": 1, \"t\\\"wo\": 2}"); | |
| 439 } | |
| 440 | |
| 441 TEST(xchar_test, escape_string) { | |
| 442 using vec = std::vector<std::wstring>; | |
| 443 EXPECT_EQ(fmt::format(L"{}", vec{L"\n\r\t\"\\"}), L"[\"\\n\\r\\t\\\"\\\\\"]"); | |
| 444 EXPECT_EQ(fmt::format(L"{}", vec{L"понедельник"}), L"[\"понедельник\"]"); | |
| 445 } | |
| 446 | |
| 447 TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } | |
| 448 | |
| 449 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR | |
| 450 | |
| 451 template <typename Char> struct numpunct : std::numpunct<Char> { | |
| 452 protected: | |
| 453 Char do_decimal_point() const override { return '?'; } | |
| 454 std::string do_grouping() const override { return "\03"; } | |
| 455 Char do_thousands_sep() const override { return '~'; } | |
| 456 }; | |
| 457 | |
| 458 template <typename Char> struct no_grouping : std::numpunct<Char> { | |
| 459 protected: | |
| 460 Char do_decimal_point() const override { return '.'; } | |
| 461 std::string do_grouping() const override { return ""; } | |
| 462 Char do_thousands_sep() const override { return ','; } | |
| 463 }; | |
| 464 | |
| 465 template <typename Char> struct special_grouping : std::numpunct<Char> { | |
| 466 protected: | |
| 467 Char do_decimal_point() const override { return '.'; } | |
| 468 std::string do_grouping() const override { return "\03\02"; } | |
| 469 Char do_thousands_sep() const override { return ','; } | |
| 470 }; | |
| 471 | |
| 472 template <typename Char> struct small_grouping : std::numpunct<Char> { | |
| 473 protected: | |
| 474 Char do_decimal_point() const override { return '.'; } | |
| 475 std::string do_grouping() const override { return "\01"; } | |
| 476 Char do_thousands_sep() const override { return ','; } | |
| 477 }; | |
| 478 | |
| 479 TEST(locale_test, localized_double) { | |
| 480 auto loc = std::locale(std::locale(), new numpunct<char>()); | |
| 481 EXPECT_EQ(fmt::format(loc, "{:L}", 1.23), "1?23"); | |
| 482 EXPECT_EQ(fmt::format(loc, "{:Lf}", 1.23), "1?230000"); | |
| 483 EXPECT_EQ(fmt::format(loc, "{:L}", 1234.5), "1~234?5"); | |
| 484 EXPECT_EQ(fmt::format(loc, "{:L}", 12000.0), "12~000"); | |
| 485 EXPECT_EQ(fmt::format(loc, "{:8L}", 1230.0), " 1~230"); | |
| 486 EXPECT_EQ(fmt::format(loc, "{:15.6Lf}", 0.1), " 0?100000"); | |
| 487 EXPECT_EQ(fmt::format(loc, "{:15.6Lf}", 1.0), " 1?000000"); | |
| 488 EXPECT_EQ(fmt::format(loc, "{:15.6Lf}", 1e3), " 1~000?000000"); | |
| 489 } | |
| 490 | |
| 491 TEST(locale_test, format) { | |
| 492 auto loc = std::locale(std::locale(), new numpunct<char>()); | |
| 493 EXPECT_EQ("1234567", fmt::format(std::locale(), "{:L}", 1234567)); | |
| 494 EXPECT_EQ("1~234~567", fmt::format(loc, "{:L}", 1234567)); | |
| 495 EXPECT_EQ("-1~234~567", fmt::format(loc, "{:L}", -1234567)); | |
| 496 EXPECT_EQ("-256", fmt::format(loc, "{:L}", -256)); | |
| 497 auto n = 1234567; | |
| 498 EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:L}", fmt::make_format_args(n))); | |
| 499 auto s = std::string(); | |
| 500 fmt::format_to(std::back_inserter(s), loc, "{:L}", 1234567); | |
| 501 EXPECT_EQ("1~234~567", s); | |
| 502 | |
| 503 auto no_grouping_loc = std::locale(std::locale(), new no_grouping<char>()); | |
| 504 EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:L}", 1234567)); | |
| 505 | |
| 506 auto special_grouping_loc = | |
| 507 std::locale(std::locale(), new special_grouping<char>()); | |
| 508 EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:L}", 12345678)); | |
| 509 EXPECT_EQ("12,345", fmt::format(special_grouping_loc, "{:L}", 12345)); | |
| 510 | |
| 511 auto small_grouping_loc = | |
| 512 std::locale(std::locale(), new small_grouping<char>()); | |
| 513 EXPECT_EQ("4,2,9,4,9,6,7,2,9,5", | |
| 514 fmt::format(small_grouping_loc, "{:L}", max_value<uint32_t>())); | |
| 515 } | |
| 516 | |
| 517 TEST(locale_test, format_detault_align) { | |
| 518 auto loc = std::locale({}, new special_grouping<char>()); | |
| 519 EXPECT_EQ(" 12,345", fmt::format(loc, "{:8L}", 12345)); | |
| 520 } | |
| 521 | |
| 522 TEST(locale_test, format_plus) { | |
| 523 auto loc = std::locale({}, new special_grouping<char>()); | |
| 524 EXPECT_EQ("+100", fmt::format(loc, "{:+L}", 100)); | |
| 525 } | |
| 526 | |
| 527 TEST(locale_test, wformat) { | |
| 528 auto loc = std::locale(std::locale(), new numpunct<wchar_t>()); | |
| 529 EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:L}", 1234567)); | |
| 530 EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:L}", 1234567)); | |
| 531 int n = 1234567; | |
| 532 EXPECT_EQ(L"1~234~567", | |
| 533 fmt::vformat(loc, L"{:L}", fmt::make_wformat_args(n))); | |
| 534 EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:L}", 1234567)); | |
| 535 | |
| 536 auto no_grouping_loc = std::locale(std::locale(), new no_grouping<wchar_t>()); | |
| 537 EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:L}", 1234567)); | |
| 538 | |
| 539 auto special_grouping_loc = | |
| 540 std::locale(std::locale(), new special_grouping<wchar_t>()); | |
| 541 EXPECT_EQ(L"1,23,45,678", | |
| 542 fmt::format(special_grouping_loc, L"{:L}", 12345678)); | |
| 543 | |
| 544 auto small_grouping_loc = | |
| 545 std::locale(std::locale(), new small_grouping<wchar_t>()); | |
| 546 EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5", | |
| 547 fmt::format(small_grouping_loc, L"{:L}", max_value<uint32_t>())); | |
| 548 } | |
| 549 | |
| 550 TEST(locale_test, int_formatter) { | |
| 551 auto loc = std::locale(std::locale(), new special_grouping<char>()); | |
| 552 auto f = fmt::formatter<int>(); | |
| 553 auto parse_ctx = fmt::format_parse_context("L"); | |
| 554 f.parse(parse_ctx); | |
| 555 auto buf = fmt::memory_buffer(); | |
| 556 fmt::basic_format_context<fmt::appender, char> format_ctx( | |
| 557 fmt::appender(buf), {}, fmt::detail::locale_ref(loc)); | |
| 558 f.format(12345, format_ctx); | |
| 559 EXPECT_EQ(fmt::to_string(buf), "12,345"); | |
| 560 } | |
| 561 | |
| 562 FMT_BEGIN_NAMESPACE | |
| 563 template <class charT> struct formatter<std::complex<double>, charT> { | |
| 564 private: | |
| 565 detail::dynamic_format_specs<char> specs_; | |
| 566 | |
| 567 public: | |
| 568 FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse( | |
| 569 basic_format_parse_context<charT>& ctx) { | |
| 570 auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, | |
| 571 detail::type::float_type); | |
| 572 detail::parse_float_type_spec(specs_); | |
| 573 return end; | |
| 574 } | |
| 575 | |
| 576 template <class FormatContext> | |
| 577 typename FormatContext::iterator format(const std::complex<double>& c, | |
| 578 FormatContext& ctx) { | |
| 579 detail::handle_dynamic_spec<detail::precision_checker>( | |
| 580 specs_.precision, specs_.precision_ref, ctx); | |
| 581 auto specs = std::string(); | |
| 582 if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision); | |
| 583 if (specs_.type == presentation_type::fixed_lower) specs += 'f'; | |
| 584 auto real = fmt::format(ctx.locale().template get<std::locale>(), | |
| 585 fmt::runtime("{:" + specs + "}"), c.real()); | |
| 586 auto imag = fmt::format(ctx.locale().template get<std::locale>(), | |
| 587 fmt::runtime("{:" + specs + "}"), c.imag()); | |
| 588 auto fill_align_width = std::string(); | |
| 589 if (specs_.width > 0) fill_align_width = fmt::format(">{}", specs_.width); | |
| 590 return fmt::format_to(ctx.out(), runtime("{:" + fill_align_width + "}"), | |
| 591 c.real() != 0 ? fmt::format("({}+{}i)", real, imag) | |
| 592 : fmt::format("{}i", imag)); | |
| 593 } | |
| 594 }; | |
| 595 FMT_END_NAMESPACE | |
| 596 | |
| 597 TEST(locale_test, complex) { | |
| 598 std::string s = fmt::format("{}", std::complex<double>(1, 2)); | |
| 599 EXPECT_EQ(s, "(1+2i)"); | |
| 600 EXPECT_EQ(fmt::format("{:.2f}", std::complex<double>(1, 2)), "(1.00+2.00i)"); | |
| 601 EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)"); | |
| 602 } | |
| 603 | |
| 604 TEST(locale_test, chrono_weekday) { | |
| 605 auto loc = get_locale("es_ES.UTF-8", "Spanish_Spain.1252"); | |
| 606 auto loc_old = std::locale::global(loc); | |
| 607 auto sat = fmt::weekday(6); | |
| 608 EXPECT_EQ(fmt::format(L"{}", sat), L"Sat"); | |
| 609 if (loc != std::locale::classic()) { | |
| 610 // L'\xE1' is 'á'. | |
| 611 auto saturdays = std::vector<std::wstring>{L"s\xE1""b", L"s\xE1."}; | |
| 612 EXPECT_THAT(saturdays, Contains(fmt::format(loc, L"{:L}", sat))); | |
| 613 } | |
| 614 std::locale::global(loc_old); | |
| 615 } | |
| 616 | |
| 617 TEST(locale_test, sign) { | |
| 618 EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50"); | |
| 619 } | |
| 620 | |
| 621 TEST(std_test_xchar, optional) { | |
| 622 # ifdef __cpp_lib_optional | |
| 623 EXPECT_EQ(fmt::format(L"{}", std::optional{L'C'}), L"optional(\'C\')"); | |
| 624 EXPECT_EQ(fmt::format(L"{}", std::optional{std::wstring{L"wide string"}}), | |
| 625 L"optional(\"wide string\")"); | |
| 626 # endif | |
| 627 } | |
| 628 | |
| 629 #endif // FMT_STATIC_THOUSANDS_SEPARATOR |
