]> git.proxmox.com Git - ceph.git/blob - ceph/src/fmt/test/xchar-test.cc
78ecb2c7563026e05f08b6d51b312e184c756be5
[ceph.git] / ceph / src / fmt / test / xchar-test.cc
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 <complex>
11
12 #include "fmt/chrono.h"
13 #include "fmt/color.h"
14 #include "fmt/ostream.h"
15 #include "fmt/ranges.h"
16 #include "gtest/gtest.h"
17
18 using fmt::detail::max_value;
19
20 namespace test_ns {
21 template <typename Char> class test_string {
22 private:
23 std::basic_string<Char> s_;
24
25 public:
26 test_string(const Char* s) : s_(s) {}
27 const Char* data() const { return s_.data(); }
28 size_t length() const { return s_.size(); }
29 operator const Char*() const { return s_.c_str(); }
30 };
31
32 template <typename Char>
33 fmt::basic_string_view<Char> to_string_view(const test_string<Char>& s) {
34 return {s.data(), s.length()};
35 }
36
37 struct non_string {};
38 } // namespace test_ns
39
40 template <typename T> class is_string_test : public testing::Test {};
41
42 using string_char_types = testing::Types<char, wchar_t, char16_t, char32_t>;
43 TYPED_TEST_SUITE(is_string_test, string_char_types);
44
45 template <typename Char>
46 struct derived_from_string_view : fmt::basic_string_view<Char> {};
47
48 TYPED_TEST(is_string_test, is_string) {
49 EXPECT_TRUE(fmt::detail::is_string<TypeParam*>::value);
50 EXPECT_TRUE(fmt::detail::is_string<const TypeParam*>::value);
51 EXPECT_TRUE(fmt::detail::is_string<TypeParam[2]>::value);
52 EXPECT_TRUE(fmt::detail::is_string<const TypeParam[2]>::value);
53 EXPECT_TRUE(fmt::detail::is_string<std::basic_string<TypeParam>>::value);
54 EXPECT_TRUE(fmt::detail::is_string<fmt::basic_string_view<TypeParam>>::value);
55 EXPECT_TRUE(
56 fmt::detail::is_string<derived_from_string_view<TypeParam>>::value);
57 using fmt_string_view = fmt::detail::std_string_view<TypeParam>;
58 EXPECT_TRUE(std::is_empty<fmt_string_view>::value !=
59 fmt::detail::is_string<fmt_string_view>::value);
60 EXPECT_TRUE(fmt::detail::is_string<test_ns::test_string<TypeParam>>::value);
61 EXPECT_FALSE(fmt::detail::is_string<test_ns::non_string>::value);
62 }
63
64 // std::is_constructible is broken in MSVC until version 2015.
65 #if !FMT_MSC_VER || FMT_MSC_VER >= 1900
66 struct explicitly_convertible_to_wstring_view {
67 explicit operator fmt::wstring_view() const { return L"foo"; }
68 };
69
70 TEST(xchar_test, format_explicitly_convertible_to_wstring_view) {
71 EXPECT_EQ(L"foo",
72 fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
73 }
74 #endif
75
76 TEST(xchar_test, format) {
77 EXPECT_EQ(L"42", fmt::format(L"{}", 42));
78 EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2));
79 EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc"));
80 EXPECT_EQ(L"z", fmt::format(L"{}", L'z'));
81 EXPECT_THROW(fmt::format(L"{:*\x343E}", 42), fmt::format_error);
82 EXPECT_EQ(L"true", fmt::format(L"{}", true));
83 EXPECT_EQ(L"a", fmt::format(L"{0}", 'a'));
84 EXPECT_EQ(L"a", fmt::format(L"{0}", L'a'));
85 EXPECT_EQ(L"Cyrillic letter \x42e",
86 fmt::format(L"Cyrillic letter {}", L'\x42e'));
87 EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1));
88 }
89
90 TEST(xchar_test, compile_time_string) {
91 #if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L
92 EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42));
93 #endif
94 }
95
96 #if __cplusplus > 201103L
97 struct custom_char {
98 int value;
99 custom_char() = default;
100
101 template <typename T>
102 constexpr custom_char(T val) : value(static_cast<int>(val)) {}
103
104 operator int() const { return value; }
105 };
106
107 int to_ascii(custom_char c) { return c; }
108
109 FMT_BEGIN_NAMESPACE
110 template <> struct is_char<custom_char> : std::true_type {};
111 FMT_END_NAMESPACE
112
113 TEST(xchar_test, format_custom_char) {
114 const custom_char format[] = {'{', '}', 0};
115 auto result = fmt::format(format, custom_char('x'));
116 EXPECT_EQ(result.size(), 1);
117 EXPECT_EQ(result[0], custom_char('x'));
118 }
119 #endif
120
121 // Convert a char8_t string to std::string. Otherwise GTest will insist on
122 // inserting `char8_t` NTBS into a `char` stream which is disabled by P1423.
123 template <typename S> std::string from_u8str(const S& str) {
124 return std::string(str.begin(), str.end());
125 }
126
127 TEST(xchar_test, format_utf8_precision) {
128 using str_type = std::basic_string<fmt::detail::char8_type>;
129 auto format =
130 str_type(reinterpret_cast<const fmt::detail::char8_type*>(u8"{:.4}"));
131 auto str = str_type(reinterpret_cast<const fmt::detail::char8_type*>(
132 u8"caf\u00e9s")); // cafés
133 auto result = fmt::format(format, str);
134 EXPECT_EQ(fmt::detail::compute_width(result), 4);
135 EXPECT_EQ(result.size(), 5);
136 EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5)));
137 }
138
139 TEST(xchar_test, format_to) {
140 auto buf = std::vector<wchar_t>();
141 fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0');
142 EXPECT_STREQ(buf.data(), L"42");
143 }
144
145 TEST(xchar_test, vformat_to) {
146 using wcontext = fmt::wformat_context;
147 fmt::basic_format_arg<wcontext> warg = fmt::detail::make_arg<wcontext>(42);
148 auto wargs = fmt::basic_format_args<wcontext>(&warg, 1);
149 auto w = std::wstring();
150 fmt::vformat_to(std::back_inserter(w), L"{}", wargs);
151 EXPECT_EQ(L"42", w);
152 w.clear();
153 fmt::vformat_to(std::back_inserter(w), FMT_STRING(L"{}"), wargs);
154 EXPECT_EQ(L"42", w);
155 }
156
157 TEST(format_test, wide_format_to_n) {
158 wchar_t buffer[4];
159 buffer[3] = L'x';
160 auto result = fmt::format_to_n(buffer, 3, L"{}", 12345);
161 EXPECT_EQ(5u, result.size);
162 EXPECT_EQ(buffer + 3, result.out);
163 EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4));
164 buffer[0] = L'x';
165 buffer[1] = L'x';
166 buffer[2] = L'x';
167 result = fmt::format_to_n(buffer, 3, L"{}", L'A');
168 EXPECT_EQ(1u, result.size);
169 EXPECT_EQ(buffer + 1, result.out);
170 EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4));
171 result = fmt::format_to_n(buffer, 3, L"{}{} ", L'B', L'C');
172 EXPECT_EQ(3u, result.size);
173 EXPECT_EQ(buffer + 3, result.out);
174 EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4));
175 }
176
177 #if FMT_USE_USER_DEFINED_LITERALS
178 TEST(xchar_test, format_udl) {
179 using namespace fmt::literals;
180 EXPECT_EQ(L"{}c{}"_format(L"ab", 1), fmt::format(L"{}c{}", L"ab", 1));
181 }
182
183 TEST(xchar_test, named_arg_udl) {
184 using namespace fmt::literals;
185 auto udl_a =
186 fmt::format(L"{first}{second}{first}{third}", L"first"_a = L"abra",
187 L"second"_a = L"cad", L"third"_a = 99);
188 EXPECT_EQ(
189 fmt::format(L"{first}{second}{first}{third}", fmt::arg(L"first", L"abra"),
190 fmt::arg(L"second", L"cad"), fmt::arg(L"third", 99)),
191 udl_a);
192 }
193 #endif // FMT_USE_USER_DEFINED_LITERALS
194
195 TEST(xchar_test, print) {
196 // Check that the wide print overload compiles.
197 if (fmt::detail::const_check(false)) fmt::print(L"test");
198 }
199
200 TEST(xchar_test, join) {
201 int v[3] = {1, 2, 3};
202 EXPECT_EQ(fmt::format(L"({})", fmt::join(v, v + 3, L", ")), L"(1, 2, 3)");
203 auto t = std::tuple<wchar_t, int, float>('a', 1, 2.0f);
204 EXPECT_EQ(fmt::format(L"({})", fmt::join(t, L", ")), L"(a, 1, 2)");
205 }
206
207 enum streamable_enum {};
208
209 std::wostream& operator<<(std::wostream& os, streamable_enum) {
210 return os << L"streamable_enum";
211 }
212
213 enum unstreamable_enum {};
214
215 TEST(xchar_test, enum) {
216 EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum()));
217 EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
218 }
219
220 TEST(xchar_test, sign_not_truncated) {
221 wchar_t format_str[] = {
222 L'{', L':',
223 '+' | static_cast<wchar_t>(1 << fmt::detail::num_bits<char>()), L'}', 0};
224 EXPECT_THROW(fmt::format(format_str, 42), fmt::format_error);
225 }
226
227 namespace fake_qt {
228 class QString {
229 public:
230 QString(const wchar_t* s) : s_(s) {}
231 const wchar_t* utf16() const FMT_NOEXCEPT { return s_.data(); }
232 int size() const FMT_NOEXCEPT { return static_cast<int>(s_.size()); }
233
234 private:
235 std::wstring s_;
236 };
237
238 fmt::basic_string_view<wchar_t> to_string_view(const QString& s) FMT_NOEXCEPT {
239 return {s.utf16(), static_cast<size_t>(s.size())};
240 }
241 } // namespace fake_qt
242
243 TEST(format_test, format_foreign_strings) {
244 using fake_qt::QString;
245 EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
246 EXPECT_EQ(fmt::format(QString(L"{}"), QString(L"42")), L"42");
247 }
248
249 TEST(xchar_test, chrono) {
250 auto tm = std::tm();
251 tm.tm_year = 116;
252 tm.tm_mon = 3;
253 tm.tm_mday = 25;
254 tm.tm_hour = 11;
255 tm.tm_min = 22;
256 tm.tm_sec = 33;
257 EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
258 "The date is 2016-04-25 11:22:33.");
259 EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42)));
260 }
261
262 TEST(xchar_test, color) {
263 EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"),
264 L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m");
265 }
266
267 TEST(xchar_test, ostream) {
268 std::wostringstream wos;
269 fmt::print(wos, L"Don't {}!", L"panic");
270 EXPECT_EQ(L"Don't panic!", wos.str());
271 }
272
273 TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }
274
275 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
276 template <typename Char> struct numpunct : std::numpunct<Char> {
277 protected:
278 Char do_decimal_point() const override { return '?'; }
279 std::string do_grouping() const override { return "\03"; }
280 Char do_thousands_sep() const override { return '~'; }
281 };
282
283 template <typename Char> struct no_grouping : std::numpunct<Char> {
284 protected:
285 Char do_decimal_point() const override { return '.'; }
286 std::string do_grouping() const override { return ""; }
287 Char do_thousands_sep() const override { return ','; }
288 };
289
290 template <typename Char> struct special_grouping : std::numpunct<Char> {
291 protected:
292 Char do_decimal_point() const override { return '.'; }
293 std::string do_grouping() const override { return "\03\02"; }
294 Char do_thousands_sep() const override { return ','; }
295 };
296
297 template <typename Char> struct small_grouping : std::numpunct<Char> {
298 protected:
299 Char do_decimal_point() const override { return '.'; }
300 std::string do_grouping() const override { return "\01"; }
301 Char do_thousands_sep() const override { return ','; }
302 };
303
304 TEST(locale_test, double_decimal_point) {
305 auto loc = std::locale(std::locale(), new numpunct<char>());
306 EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23));
307 EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23));
308 }
309
310 TEST(locale_test, format) {
311 auto loc = std::locale(std::locale(), new numpunct<char>());
312 EXPECT_EQ("1234567", fmt::format(std::locale(), "{:L}", 1234567));
313 EXPECT_EQ("1~234~567", fmt::format(loc, "{:L}", 1234567));
314 EXPECT_EQ("-1~234~567", fmt::format(loc, "{:L}", -1234567));
315 EXPECT_EQ("-256", fmt::format(loc, "{:L}", -256));
316 fmt::format_arg_store<fmt::format_context, int> as{1234567};
317 EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:L}", fmt::format_args(as)));
318 auto s = std::string();
319 fmt::format_to(std::back_inserter(s), loc, "{:L}", 1234567);
320 EXPECT_EQ("1~234~567", s);
321
322 auto no_grouping_loc = std::locale(std::locale(), new no_grouping<char>());
323 EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:L}", 1234567));
324
325 auto special_grouping_loc =
326 std::locale(std::locale(), new special_grouping<char>());
327 EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:L}", 12345678));
328 EXPECT_EQ("12,345", fmt::format(special_grouping_loc, "{:L}", 12345));
329
330 auto small_grouping_loc =
331 std::locale(std::locale(), new small_grouping<char>());
332 EXPECT_EQ("4,2,9,4,9,6,7,2,9,5",
333 fmt::format(small_grouping_loc, "{:L}", max_value<uint32_t>()));
334 }
335
336 TEST(locale_test, format_detault_align) {
337 auto loc = std::locale({}, new special_grouping<char>());
338 EXPECT_EQ(" 12,345", fmt::format(loc, "{:8L}", 12345));
339 }
340
341 TEST(locale_test, format_plus) {
342 auto loc = std::locale({}, new special_grouping<char>());
343 EXPECT_EQ("+100", fmt::format(loc, "{:+L}", 100));
344 }
345
346 TEST(locale_test, wformat) {
347 auto loc = std::locale(std::locale(), new numpunct<wchar_t>());
348 EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:L}", 1234567));
349 EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:L}", 1234567));
350 using wcontext = fmt::buffer_context<wchar_t>;
351 fmt::format_arg_store<wcontext, int> as{1234567};
352 EXPECT_EQ(L"1~234~567",
353 fmt::vformat(loc, L"{:L}", fmt::basic_format_args<wcontext>(as)));
354 EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:L}", 1234567));
355
356 auto no_grouping_loc = std::locale(std::locale(), new no_grouping<wchar_t>());
357 EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:L}", 1234567));
358
359 auto special_grouping_loc =
360 std::locale(std::locale(), new special_grouping<wchar_t>());
361 EXPECT_EQ(L"1,23,45,678",
362 fmt::format(special_grouping_loc, L"{:L}", 12345678));
363
364 auto small_grouping_loc =
365 std::locale(std::locale(), new small_grouping<wchar_t>());
366 EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5",
367 fmt::format(small_grouping_loc, L"{:L}", max_value<uint32_t>()));
368 }
369
370 TEST(locale_test, double_formatter) {
371 auto loc = std::locale(std::locale(), new special_grouping<char>());
372 auto f = fmt::formatter<int>();
373 auto parse_ctx = fmt::format_parse_context("L");
374 f.parse(parse_ctx);
375 char buf[10] = {};
376 fmt::basic_format_context<char*, char> format_ctx(
377 buf, {}, fmt::detail::locale_ref(loc));
378 *f.format(12345, format_ctx) = 0;
379 EXPECT_STREQ("12,345", buf);
380 }
381
382 FMT_BEGIN_NAMESPACE
383 template <class charT> struct formatter<std::complex<double>, charT> {
384 private:
385 detail::dynamic_format_specs<char> specs_;
386
387 public:
388 FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse(
389 basic_format_parse_context<charT>& ctx) {
390 using handler_type =
391 detail::dynamic_specs_handler<basic_format_parse_context<charT>>;
392 detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
393 detail::type::string_type);
394 auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
395 detail::parse_float_type_spec(specs_, ctx.error_handler());
396 return it;
397 }
398
399 template <class FormatContext>
400 typename FormatContext::iterator format(const std::complex<double>& c,
401 FormatContext& ctx) {
402 detail::handle_dynamic_spec<detail::precision_checker>(
403 specs_.precision, specs_.precision_ref, ctx);
404 auto specs = std::string();
405 if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision);
406 if (specs_.type) specs += specs_.type;
407 auto real = fmt::format(ctx.locale().template get<std::locale>(),
408 fmt::runtime("{:" + specs + "}"), c.real());
409 auto imag = fmt::format(ctx.locale().template get<std::locale>(),
410 fmt::runtime("{:" + specs + "}"), c.imag());
411 auto fill_align_width = std::string();
412 if (specs_.width > 0) fill_align_width = fmt::format(">{}", specs_.width);
413 return format_to(ctx.out(), runtime("{:" + fill_align_width + "}"),
414 c.real() != 0 ? fmt::format("({}+{}i)", real, imag)
415 : fmt::format("{}i", imag));
416 }
417 };
418 FMT_END_NAMESPACE
419
420 TEST(locale_test, complex) {
421 std::string s = fmt::format("{}", std::complex<double>(1, 2));
422 EXPECT_EQ(s, "(1+2i)");
423 EXPECT_EQ(fmt::format("{:.2f}", std::complex<double>(1, 2)), "(1.00+2.00i)");
424 EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)");
425 }
426
427 #endif // FMT_STATIC_THOUSANDS_SEPARATOR