]> git.proxmox.com Git - ceph.git/blob - ceph/src/fmt/test/core-test.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / fmt / test / core-test.cc
1 // Formatting library for C++ - core 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 // clang-format off
9 #include "test-assert.h"
10 // clang-format on
11
12 #define I 42 // simulate https://en.cppreference.com/w/c/numeric/complex/I
13 #include "fmt/core.h"
14 #undef I
15
16 #include <algorithm> // std::copy_n
17 #include <climits> // INT_MAX
18 #include <cstring> // std::strlen
19 #include <functional> // std::equal_to
20 #include <iterator> // std::back_insert_iterator
21 #include <limits> // std::numeric_limits
22 #include <string> // std::string
23 #include <type_traits> // std::is_same
24
25 #include "gmock/gmock.h"
26
27 using fmt::string_view;
28 using fmt::detail::buffer;
29
30 using testing::_;
31 using testing::Invoke;
32 using testing::Return;
33
34 #ifdef FMT_FORMAT_H_
35 # error core-test includes format.h
36 #endif
37
38 TEST(string_view_test, value_type) {
39 static_assert(std::is_same<string_view::value_type, char>::value, "");
40 }
41
42 TEST(string_view_test, ctor) {
43 EXPECT_STREQ("abc", fmt::string_view("abc").data());
44 EXPECT_EQ(3u, fmt::string_view("abc").size());
45
46 EXPECT_STREQ("defg", fmt::string_view(std::string("defg")).data());
47 EXPECT_EQ(4u, fmt::string_view(std::string("defg")).size());
48 }
49
50 TEST(string_view_test, length) {
51 // Test that string_view::size() returns string length, not buffer size.
52 char str[100] = "some string";
53 EXPECT_EQ(std::strlen(str), string_view(str).size());
54 EXPECT_LT(std::strlen(str), sizeof(str));
55 }
56
57 // Check string_view's comparison operator.
58 template <template <typename> class Op> void check_op() {
59 const char* inputs[] = {"foo", "fop", "fo"};
60 size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
61 for (size_t i = 0; i < num_inputs; ++i) {
62 for (size_t j = 0; j < num_inputs; ++j) {
63 string_view lhs(inputs[i]), rhs(inputs[j]);
64 EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
65 }
66 }
67 }
68
69 TEST(string_view_test, compare) {
70 EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0);
71 EXPECT_GT(string_view("fop").compare(string_view("foo")), 0);
72 EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
73 EXPECT_GT(string_view("foo").compare(string_view("fo")), 0);
74 EXPECT_LT(string_view("fo").compare(string_view("foo")), 0);
75 check_op<std::equal_to>();
76 check_op<std::not_equal_to>();
77 check_op<std::less>();
78 check_op<std::less_equal>();
79 check_op<std::greater>();
80 check_op<std::greater_equal>();
81 }
82
83 namespace test_ns {
84 template <typename Char> class test_string {
85 private:
86 std::basic_string<Char> s_;
87
88 public:
89 test_string(const Char* s) : s_(s) {}
90 const Char* data() const { return s_.data(); }
91 size_t length() const { return s_.size(); }
92 operator const Char*() const { return s_.c_str(); }
93 };
94
95 template <typename Char>
96 fmt::basic_string_view<Char> to_string_view(const test_string<Char>& s) {
97 return {s.data(), s.length()};
98 }
99 } // namespace test_ns
100
101 TEST(core_test, is_output_iterator) {
102 EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value));
103 EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value));
104 EXPECT_FALSE((fmt::detail::is_output_iterator<std::string, char>::value));
105 EXPECT_TRUE(
106 (fmt::detail::is_output_iterator<std::back_insert_iterator<std::string>,
107 char>::value));
108 EXPECT_TRUE(
109 (fmt::detail::is_output_iterator<std::string::iterator, char>::value));
110 EXPECT_FALSE((fmt::detail::is_output_iterator<std::string::const_iterator,
111 char>::value));
112 }
113
114 TEST(core_test, buffer_appender) {
115 // back_insert_iterator is not default-constructible before C++20, so
116 // buffer_appender can only be default-constructible when back_insert_iterator
117 // is.
118 static_assert(
119 std::is_default_constructible<
120 std::back_insert_iterator<fmt::detail::buffer<char>>>::value ==
121 std::is_default_constructible<
122 fmt::detail::buffer_appender<char>>::value,
123 "");
124
125 #ifdef __cpp_lib_ranges
126 static_assert(std::output_iterator<fmt::detail::buffer_appender<char>, char>);
127 #endif
128 }
129
130 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
131 TEST(buffer_test, noncopyable) {
132 EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
133 # if !FMT_MSC_VERSION
134 // std::is_copy_assignable is broken in MSVC2013.
135 EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
136 # endif
137 }
138
139 TEST(buffer_test, nonmoveable) {
140 EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
141 # if !FMT_MSC_VERSION
142 // std::is_move_assignable is broken in MSVC2013.
143 EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
144 # endif
145 }
146 #endif
147
148 TEST(buffer_test, indestructible) {
149 static_assert(!std::is_destructible<fmt::detail::buffer<int>>(),
150 "buffer's destructor is protected");
151 }
152
153 template <typename T> struct mock_buffer final : buffer<T> {
154 MOCK_METHOD1(do_grow, size_t(size_t capacity));
155
156 void grow(size_t capacity) override {
157 this->set(this->data(), do_grow(capacity));
158 }
159
160 mock_buffer(T* data = nullptr, size_t buf_capacity = 0) {
161 this->set(data, buf_capacity);
162 ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) {
163 return capacity;
164 }));
165 }
166 };
167
168 TEST(buffer_test, ctor) {
169 {
170 mock_buffer<int> buffer;
171 EXPECT_EQ(nullptr, buffer.data());
172 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
173 EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
174 }
175 {
176 int dummy;
177 mock_buffer<int> buffer(&dummy);
178 EXPECT_EQ(&dummy, &buffer[0]);
179 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
180 EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
181 }
182 {
183 int dummy;
184 size_t capacity = std::numeric_limits<size_t>::max();
185 mock_buffer<int> buffer(&dummy, capacity);
186 EXPECT_EQ(&dummy, &buffer[0]);
187 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
188 EXPECT_EQ(capacity, buffer.capacity());
189 }
190 }
191
192 TEST(buffer_test, access) {
193 char data[10];
194 mock_buffer<char> buffer(data, sizeof(data));
195 buffer[0] = 11;
196 EXPECT_EQ(11, buffer[0]);
197 buffer[3] = 42;
198 EXPECT_EQ(42, *(&buffer[0] + 3));
199 const fmt::detail::buffer<char>& const_buffer = buffer;
200 EXPECT_EQ(42, const_buffer[3]);
201 }
202
203 TEST(buffer_test, try_resize) {
204 char data[123];
205 mock_buffer<char> buffer(data, sizeof(data));
206 buffer[10] = 42;
207 EXPECT_EQ(42, buffer[10]);
208 buffer.try_resize(20);
209 EXPECT_EQ(20u, buffer.size());
210 EXPECT_EQ(123u, buffer.capacity());
211 EXPECT_EQ(42, buffer[10]);
212 buffer.try_resize(5);
213 EXPECT_EQ(5u, buffer.size());
214 EXPECT_EQ(123u, buffer.capacity());
215 EXPECT_EQ(42, buffer[10]);
216 // Check if try_resize calls grow.
217 EXPECT_CALL(buffer, do_grow(124));
218 buffer.try_resize(124);
219 EXPECT_CALL(buffer, do_grow(200));
220 buffer.try_resize(200);
221 }
222
223 TEST(buffer_test, try_resize_partial) {
224 char data[10];
225 mock_buffer<char> buffer(data, sizeof(data));
226 EXPECT_CALL(buffer, do_grow(20)).WillOnce(Return(15));
227 buffer.try_resize(20);
228 EXPECT_EQ(buffer.capacity(), 15);
229 EXPECT_EQ(buffer.size(), 15);
230 }
231
232 TEST(buffer_test, clear) {
233 mock_buffer<char> buffer;
234 EXPECT_CALL(buffer, do_grow(20));
235 buffer.try_resize(20);
236 buffer.try_resize(0);
237 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
238 EXPECT_EQ(20u, buffer.capacity());
239 }
240
241 TEST(buffer_test, append) {
242 char data[15];
243 mock_buffer<char> buffer(data, 10);
244 auto test = "test";
245 buffer.append(test, test + 5);
246 EXPECT_STREQ(test, &buffer[0]);
247 EXPECT_EQ(5u, buffer.size());
248 buffer.try_resize(10);
249 EXPECT_CALL(buffer, do_grow(12));
250 buffer.append(test, test + 2);
251 EXPECT_EQ('t', buffer[10]);
252 EXPECT_EQ('e', buffer[11]);
253 EXPECT_EQ(12u, buffer.size());
254 }
255
256 TEST(buffer_test, append_partial) {
257 char data[10];
258 mock_buffer<char> buffer(data, sizeof(data));
259 testing::InSequence seq;
260 EXPECT_CALL(buffer, do_grow(15)).WillOnce(Return(10));
261 EXPECT_CALL(buffer, do_grow(15)).WillOnce(Invoke([&buffer](size_t) {
262 EXPECT_EQ(fmt::string_view(buffer.data(), buffer.size()), "0123456789");
263 buffer.clear();
264 return 10;
265 }));
266 auto test = "0123456789abcde";
267 buffer.append(test, test + 15);
268 }
269
270 TEST(buffer_test, append_allocates_enough_storage) {
271 char data[19];
272 mock_buffer<char> buffer(data, 10);
273 auto test = "abcdefgh";
274 buffer.try_resize(10);
275 EXPECT_CALL(buffer, do_grow(19));
276 buffer.append(test, test + 9);
277 }
278
279 struct custom_context {
280 using char_type = char;
281 using parse_context_type = fmt::format_parse_context;
282
283 bool called = false;
284
285 template <typename T> struct formatter_type {
286 auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin()) {
287 return ctx.begin();
288 }
289
290 const char* format(const T&, custom_context& ctx) {
291 ctx.called = true;
292 return nullptr;
293 }
294 };
295
296 void advance_to(const char*) {}
297 };
298
299 struct test_struct {};
300
301 FMT_BEGIN_NAMESPACE
302 template <typename Char> struct formatter<test_struct, Char> {
303 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
304 return ctx.begin();
305 }
306
307 auto format(test_struct, format_context& ctx) -> decltype(ctx.out()) {
308 auto test = string_view("test");
309 return std::copy_n(test.data(), test.size(), ctx.out());
310 }
311 };
312 FMT_END_NAMESPACE
313
314 TEST(arg_test, format_args) {
315 auto args = fmt::format_args();
316 EXPECT_FALSE(args.get(1));
317 }
318
319 TEST(arg_test, make_value_with_custom_context) {
320 auto t = test_struct();
321 fmt::detail::value<custom_context> arg(
322 fmt::detail::arg_mapper<custom_context>().map(t));
323 auto ctx = custom_context();
324 auto parse_ctx = fmt::format_parse_context("");
325 arg.custom.format(&t, parse_ctx, ctx);
326 EXPECT_TRUE(ctx.called);
327 }
328
329 // Use a unique result type to make sure that there are no undesirable
330 // conversions.
331 struct test_result {};
332
333 template <typename T> struct mock_visitor {
334 template <typename U> struct result { using type = test_result; };
335
336 mock_visitor() {
337 ON_CALL(*this, visit(_)).WillByDefault(Return(test_result()));
338 }
339
340 MOCK_METHOD1_T(visit, test_result(T value));
341 MOCK_METHOD0_T(unexpected, void());
342
343 test_result operator()(T value) { return visit(value); }
344
345 template <typename U> test_result operator()(U) {
346 unexpected();
347 return test_result();
348 }
349 };
350
351 template <typename T> struct visit_type { using type = T; };
352
353 #define VISIT_TYPE(type_, visit_type_) \
354 template <> struct visit_type<type_> { using type = visit_type_; }
355
356 VISIT_TYPE(signed char, int);
357 VISIT_TYPE(unsigned char, unsigned);
358 VISIT_TYPE(short, int);
359 VISIT_TYPE(unsigned short, unsigned);
360
361 #if LONG_MAX == INT_MAX
362 VISIT_TYPE(long, int);
363 VISIT_TYPE(unsigned long, unsigned);
364 #else
365 VISIT_TYPE(long, long long);
366 VISIT_TYPE(unsigned long, unsigned long long);
367 #endif
368
369 #define CHECK_ARG(Char, expected, value) \
370 { \
371 testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
372 EXPECT_CALL(visitor, visit(expected)); \
373 using iterator = std::back_insert_iterator<buffer<Char>>; \
374 fmt::visit_format_arg( \
375 visitor, \
376 fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>( \
377 value)); \
378 }
379
380 #define CHECK_ARG_SIMPLE(value) \
381 { \
382 using value_type = decltype(value); \
383 typename visit_type<value_type>::type expected = value; \
384 CHECK_ARG(char, expected, value) \
385 CHECK_ARG(wchar_t, expected, value) \
386 }
387
388 template <typename T> class numeric_arg_test : public testing::Test {};
389
390 using test_types =
391 testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
392 unsigned, long, unsigned long, long long, unsigned long long,
393 float, double, long double>;
394 TYPED_TEST_SUITE(numeric_arg_test, test_types);
395
396 template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
397 T test_value() {
398 return static_cast<T>(42);
399 }
400
401 template <typename T,
402 fmt::enable_if_t<std::is_floating_point<T>::value, int> = 0>
403 T test_value() {
404 return static_cast<T>(4.2);
405 }
406
407 TYPED_TEST(numeric_arg_test, make_and_visit) {
408 CHECK_ARG_SIMPLE(test_value<TypeParam>());
409 CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::min());
410 CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
411 }
412
413 TEST(arg_test, char_arg) { CHECK_ARG(char, 'a', 'a'); }
414
415 TEST(arg_test, string_arg) {
416 char str_data[] = "test";
417 char* str = str_data;
418 const char* cstr = str;
419 CHECK_ARG(char, cstr, str);
420
421 auto sv = fmt::string_view(str);
422 CHECK_ARG(char, sv, std::string(str));
423 }
424
425 TEST(arg_test, wstring_arg) {
426 wchar_t str_data[] = L"test";
427 wchar_t* str = str_data;
428 const wchar_t* cstr = str;
429
430 auto sv = fmt::basic_string_view<wchar_t>(str);
431 CHECK_ARG(wchar_t, cstr, str);
432 CHECK_ARG(wchar_t, cstr, cstr);
433 CHECK_ARG(wchar_t, sv, std::wstring(str));
434 CHECK_ARG(wchar_t, sv, fmt::basic_string_view<wchar_t>(str));
435 }
436
437 TEST(arg_test, pointer_arg) {
438 void* p = nullptr;
439 const void* cp = nullptr;
440 CHECK_ARG(char, cp, p);
441 CHECK_ARG(wchar_t, cp, p);
442 CHECK_ARG_SIMPLE(cp);
443 }
444
445 struct check_custom {
446 test_result operator()(
447 fmt::basic_format_arg<fmt::format_context>::handle h) const {
448 struct test_buffer final : fmt::detail::buffer<char> {
449 char data[10];
450 test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
451 void grow(size_t) override {}
452 } buffer;
453 auto parse_ctx = fmt::format_parse_context("");
454 auto ctx = fmt::format_context(fmt::detail::buffer_appender<char>(buffer),
455 fmt::format_args());
456 h.format(parse_ctx, ctx);
457 EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
458 return test_result();
459 }
460 };
461
462 TEST(arg_test, custom_arg) {
463 auto test = test_struct();
464 using visitor =
465 mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>;
466 testing::StrictMock<visitor> v;
467 EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom()));
468 fmt::visit_format_arg(v, fmt::detail::make_arg<fmt::format_context>(test));
469 }
470
471 TEST(arg_test, visit_invalid_arg) {
472 testing::StrictMock<mock_visitor<fmt::monostate>> visitor;
473 EXPECT_CALL(visitor, visit(_));
474 auto arg = fmt::basic_format_arg<fmt::format_context>();
475 fmt::visit_format_arg(visitor, arg);
476 }
477
478 #if FMT_USE_CONSTEXPR
479
480 enum class arg_id_result { none, empty, index, name, error };
481 struct test_arg_id_handler {
482 arg_id_result res = arg_id_result::none;
483 int index = 0;
484 string_view name;
485
486 constexpr void operator()() { res = arg_id_result::empty; }
487
488 constexpr void operator()(int i) {
489 res = arg_id_result::index;
490 index = i;
491 }
492
493 constexpr void operator()(string_view n) {
494 res = arg_id_result::name;
495 name = n;
496 }
497
498 constexpr void on_error(const char*) { res = arg_id_result::error; }
499 };
500
501 template <size_t N>
502 constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) {
503 test_arg_id_handler h;
504 fmt::detail::parse_arg_id(s, s + N, h);
505 return h;
506 }
507
508 TEST(format_test, constexpr_parse_arg_id) {
509 static_assert(parse_arg_id(":").res == arg_id_result::empty, "");
510 static_assert(parse_arg_id("}").res == arg_id_result::empty, "");
511 static_assert(parse_arg_id("42:").res == arg_id_result::index, "");
512 static_assert(parse_arg_id("42:").index == 42, "");
513 static_assert(parse_arg_id("foo:").res == arg_id_result::name, "");
514 static_assert(parse_arg_id("foo:").name.size() == 3, "");
515 static_assert(parse_arg_id("!").res == arg_id_result::error, "");
516 }
517
518 struct test_format_specs_handler {
519 enum result { none, hash, zero, loc, error };
520 result res = none;
521
522 fmt::align_t alignment = fmt::align::none;
523 fmt::sign_t sign = fmt::sign::none;
524 char fill = 0;
525 int width = 0;
526 fmt::detail::arg_ref<char> width_ref;
527 int precision = 0;
528 fmt::detail::arg_ref<char> precision_ref;
529 fmt::presentation_type type = fmt::presentation_type::none;
530
531 // Workaround for MSVC2017 bug that results in "expression did not evaluate
532 // to a constant" with compiler-generated copy ctor.
533 constexpr test_format_specs_handler() {}
534 constexpr test_format_specs_handler(const test_format_specs_handler& other) =
535 default;
536
537 constexpr void on_align(fmt::align_t a) { alignment = a; }
538 constexpr void on_fill(fmt::string_view f) { fill = f[0]; }
539 constexpr void on_sign(fmt::sign_t s) { sign = s; }
540 constexpr void on_hash() { res = hash; }
541 constexpr void on_zero() { res = zero; }
542 constexpr void on_localized() { res = loc; }
543
544 constexpr void on_width(int w) { width = w; }
545 constexpr void on_dynamic_width(fmt::detail::auto_id) {}
546 constexpr void on_dynamic_width(int index) { width_ref = index; }
547 constexpr void on_dynamic_width(string_view) {}
548
549 constexpr void on_precision(int p) { precision = p; }
550 constexpr void on_dynamic_precision(fmt::detail::auto_id) {}
551 constexpr void on_dynamic_precision(int index) { precision_ref = index; }
552 constexpr void on_dynamic_precision(string_view) {}
553
554 constexpr void end_precision() {}
555 constexpr void on_type(fmt::presentation_type t) { type = t; }
556 constexpr void on_error(const char*) { res = error; }
557 };
558
559 template <size_t N>
560 constexpr test_format_specs_handler parse_test_specs(const char (&s)[N]) {
561 auto h = test_format_specs_handler();
562 fmt::detail::parse_format_specs(s, s + N - 1, h);
563 return h;
564 }
565
566 TEST(core_test, constexpr_parse_format_specs) {
567 using handler = test_format_specs_handler;
568 static_assert(parse_test_specs("<").alignment == fmt::align::left, "");
569 static_assert(parse_test_specs("*^").fill == '*', "");
570 static_assert(parse_test_specs("+").sign == fmt::sign::plus, "");
571 static_assert(parse_test_specs("-").sign == fmt::sign::minus, "");
572 static_assert(parse_test_specs(" ").sign == fmt::sign::space, "");
573 static_assert(parse_test_specs("#").res == handler::hash, "");
574 static_assert(parse_test_specs("0").res == handler::zero, "");
575 static_assert(parse_test_specs("L").res == handler::loc, "");
576 static_assert(parse_test_specs("42").width == 42, "");
577 static_assert(parse_test_specs("{42}").width_ref.val.index == 42, "");
578 static_assert(parse_test_specs(".42").precision == 42, "");
579 static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, "");
580 static_assert(parse_test_specs("d").type == fmt::presentation_type::dec, "");
581 static_assert(parse_test_specs("{<").res == handler::error, "");
582 }
583
584 struct test_parse_context {
585 using char_type = char;
586
587 constexpr int next_arg_id() { return 11; }
588 template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {}
589 FMT_CONSTEXPR void check_dynamic_spec(int) {}
590
591 constexpr const char* begin() { return nullptr; }
592 constexpr const char* end() { return nullptr; }
593
594 void on_error(const char*) {}
595 };
596
597 template <size_t N>
598 constexpr fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
599 const char (&s)[N]) {
600 auto specs = fmt::detail::dynamic_format_specs<char>();
601 auto ctx = test_parse_context();
602 auto h = fmt::detail::dynamic_specs_handler<test_parse_context>(specs, ctx);
603 parse_format_specs(s, s + N - 1, h);
604 return specs;
605 }
606
607 TEST(format_test, constexpr_dynamic_specs_handler) {
608 static_assert(parse_dynamic_specs("<").align == fmt::align::left, "");
609 static_assert(parse_dynamic_specs("*^").fill[0] == '*', "");
610 static_assert(parse_dynamic_specs("+").sign == fmt::sign::plus, "");
611 static_assert(parse_dynamic_specs("-").sign == fmt::sign::minus, "");
612 static_assert(parse_dynamic_specs(" ").sign == fmt::sign::space, "");
613 static_assert(parse_dynamic_specs("#").alt, "");
614 static_assert(parse_dynamic_specs("0").align == fmt::align::numeric, "");
615 static_assert(parse_dynamic_specs("42").width == 42, "");
616 static_assert(parse_dynamic_specs("{}").width_ref.val.index == 11, "");
617 static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, "");
618 static_assert(parse_dynamic_specs(".42").precision == 42, "");
619 static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 11, "");
620 static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, "");
621 static_assert(parse_dynamic_specs("d").type == fmt::presentation_type::dec,
622 "");
623 }
624
625 template <size_t N>
626 constexpr test_format_specs_handler check_specs(const char (&s)[N]) {
627 fmt::detail::specs_checker<test_format_specs_handler> checker(
628 test_format_specs_handler(), fmt::detail::type::double_type);
629 parse_format_specs(s, s + N - 1, checker);
630 return checker;
631 }
632
633 TEST(format_test, constexpr_specs_checker) {
634 using handler = test_format_specs_handler;
635 static_assert(check_specs("<").alignment == fmt::align::left, "");
636 static_assert(check_specs("*^").fill == '*', "");
637 static_assert(check_specs("+").sign == fmt::sign::plus, "");
638 static_assert(check_specs("-").sign == fmt::sign::minus, "");
639 static_assert(check_specs(" ").sign == fmt::sign::space, "");
640 static_assert(check_specs("#").res == handler::hash, "");
641 static_assert(check_specs("0").res == handler::zero, "");
642 static_assert(check_specs("42").width == 42, "");
643 static_assert(check_specs("{42}").width_ref.val.index == 42, "");
644 static_assert(check_specs(".42").precision == 42, "");
645 static_assert(check_specs(".{42}").precision_ref.val.index == 42, "");
646 static_assert(check_specs("d").type == fmt::presentation_type::dec, "");
647 static_assert(check_specs("{<").res == handler::error, "");
648 }
649
650 struct test_format_string_handler {
651 constexpr void on_text(const char*, const char*) {}
652
653 constexpr int on_arg_id() { return 0; }
654
655 template <typename T> constexpr int on_arg_id(T) { return 0; }
656
657 constexpr void on_replacement_field(int, const char*) {}
658
659 constexpr const char* on_format_specs(int, const char* begin, const char*) {
660 return begin;
661 }
662
663 constexpr void on_error(const char*) { error = true; }
664
665 bool error = false;
666 };
667
668 template <size_t N> constexpr bool parse_string(const char (&s)[N]) {
669 auto h = test_format_string_handler();
670 fmt::detail::parse_format_string<true>(fmt::string_view(s, N - 1), h);
671 return !h.error;
672 }
673
674 TEST(format_test, constexpr_parse_format_string) {
675 static_assert(parse_string("foo"), "");
676 static_assert(!parse_string("}"), "");
677 static_assert(parse_string("{}"), "");
678 static_assert(parse_string("{42}"), "");
679 static_assert(parse_string("{foo}"), "");
680 static_assert(parse_string("{:}"), "");
681 }
682 #endif // FMT_USE_CONSTEXPR
683
684 struct enabled_formatter {};
685 struct enabled_ptr_formatter {};
686 struct disabled_formatter {};
687 struct disabled_formatter_convertible {
688 operator int() const { return 42; }
689 };
690
691 FMT_BEGIN_NAMESPACE
692 template <> struct formatter<enabled_formatter> {
693 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
694 return ctx.begin();
695 }
696 auto format(enabled_formatter, format_context& ctx) -> decltype(ctx.out()) {
697 return ctx.out();
698 }
699 };
700
701 template <> struct formatter<enabled_ptr_formatter*> {
702 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
703 return ctx.begin();
704 }
705 auto format(enabled_ptr_formatter*, format_context& ctx)
706 -> decltype(ctx.out()) {
707 return ctx.out();
708 }
709 };
710 FMT_END_NAMESPACE
711
712 TEST(core_test, has_formatter) {
713 using fmt::has_formatter;
714 using context = fmt::format_context;
715 static_assert(has_formatter<enabled_formatter, context>::value, "");
716 static_assert(!has_formatter<disabled_formatter, context>::value, "");
717 static_assert(!has_formatter<disabled_formatter_convertible, context>::value,
718 "");
719 }
720
721 struct const_formattable {};
722 struct nonconst_formattable {};
723
724 FMT_BEGIN_NAMESPACE
725 template <> struct formatter<const_formattable> {
726 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
727 return ctx.begin();
728 }
729
730 auto format(const const_formattable&, format_context& ctx)
731 -> decltype(ctx.out()) {
732 auto test = string_view("test");
733 return std::copy_n(test.data(), test.size(), ctx.out());
734 }
735 };
736
737 template <> struct formatter<nonconst_formattable> {
738 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
739 return ctx.begin();
740 }
741
742 auto format(nonconst_formattable&, format_context& ctx)
743 -> decltype(ctx.out()) {
744 auto test = string_view("test");
745 return std::copy_n(test.data(), test.size(), ctx.out());
746 }
747 };
748 FMT_END_NAMESPACE
749
750 struct convertible_to_pointer {
751 operator const int*() const { return nullptr; }
752 };
753
754 struct convertible_to_pointer_formattable {
755 operator const int*() const { return nullptr; }
756 };
757
758 FMT_BEGIN_NAMESPACE
759 template <> struct formatter<convertible_to_pointer_formattable> {
760 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
761 return ctx.begin();
762 }
763
764 auto format(convertible_to_pointer_formattable, format_context& ctx) const
765 -> decltype(ctx.out()) {
766 auto test = string_view("test");
767 return std::copy_n(test.data(), test.size(), ctx.out());
768 }
769 };
770 FMT_END_NAMESPACE
771
772 enum class unformattable_scoped_enum {};
773
774 namespace test {
775 enum class formattable_scoped_enum {};
776 auto format_as(formattable_scoped_enum) -> int { return 42; }
777
778 struct convertible_to_enum {
779 operator formattable_scoped_enum() const { return {}; }
780 };
781 } // namespace test
782
783 TEST(core_test, is_formattable) {
784 #if 0
785 // This should be enabled once corresponding map overloads are gone.
786 static_assert(fmt::is_formattable<signed char*>::value, "");
787 static_assert(fmt::is_formattable<unsigned char*>::value, "");
788 static_assert(fmt::is_formattable<const signed char*>::value, "");
789 static_assert(fmt::is_formattable<const unsigned char*>::value, "");
790 #endif
791 static_assert(!fmt::is_formattable<wchar_t>::value, "");
792 #ifdef __cpp_char8_t
793 static_assert(!fmt::is_formattable<char8_t>::value, "");
794 #endif
795 static_assert(!fmt::is_formattable<char16_t>::value, "");
796 static_assert(!fmt::is_formattable<char32_t>::value, "");
797 static_assert(!fmt::is_formattable<const wchar_t*>::value, "");
798 static_assert(!fmt::is_formattable<const wchar_t[3]>::value, "");
799 static_assert(!fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value,
800 "");
801 static_assert(fmt::is_formattable<enabled_formatter>::value, "");
802 static_assert(!fmt::is_formattable<enabled_ptr_formatter*>::value, "");
803 static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
804 static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
805
806 static_assert(fmt::is_formattable<const_formattable&>::value, "");
807 static_assert(fmt::is_formattable<const const_formattable&>::value, "");
808
809 static_assert(fmt::is_formattable<nonconst_formattable&>::value, "");
810 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
811 static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, "");
812 #endif
813
814 static_assert(!fmt::is_formattable<convertible_to_pointer>::value, "");
815 const auto f = convertible_to_pointer_formattable();
816 EXPECT_EQ(fmt::format("{}", f), "test");
817
818 static_assert(!fmt::is_formattable<void (*)()>::value, "");
819
820 struct s;
821 static_assert(!fmt::is_formattable<int(s::*)>::value, "");
822 static_assert(!fmt::is_formattable<int (s::*)()>::value, "");
823 static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, "");
824 static_assert(fmt::is_formattable<test::formattable_scoped_enum>::value, "");
825 static_assert(!fmt::is_formattable<test::convertible_to_enum>::value, "");
826 }
827
828 TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); }
829
830 TEST(core_test, format_to) {
831 std::string s;
832 fmt::format_to(std::back_inserter(s), "{}", 42);
833 EXPECT_EQ(s, "42");
834 }
835
836 TEST(core_test, format_as) {
837 EXPECT_EQ(fmt::format("{}", test::formattable_scoped_enum()), "42");
838 }
839
840 struct convertible_to_int {
841 operator int() const { return 42; }
842 };
843
844 struct convertible_to_c_string {
845 operator const char*() const { return "foo"; }
846 };
847
848 FMT_BEGIN_NAMESPACE
849 template <> struct formatter<convertible_to_int> {
850 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
851 return ctx.begin();
852 }
853 auto format(convertible_to_int, format_context& ctx) -> decltype(ctx.out()) {
854 return std::copy_n("foo", 3, ctx.out());
855 }
856 };
857
858 template <> struct formatter<convertible_to_c_string> {
859 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
860 return ctx.begin();
861 }
862 auto format(convertible_to_c_string, format_context& ctx)
863 -> decltype(ctx.out()) {
864 return std::copy_n("bar", 3, ctx.out());
865 }
866 };
867 FMT_END_NAMESPACE
868
869 TEST(core_test, formatter_overrides_implicit_conversion) {
870 EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo");
871 EXPECT_EQ(fmt::format("{}", convertible_to_c_string()), "bar");
872 }
873
874 // Test that check is not found by ADL.
875 template <typename T> void check(T);
876 TEST(core_test, adl_check) {
877 EXPECT_EQ(fmt::format("{}", test_struct()), "test");
878 }
879
880 TEST(core_test, to_string_view_foreign_strings) {
881 using namespace test_ns;
882 EXPECT_EQ(to_string_view(test_string<char>("42")), "42");
883 fmt::detail::type type =
884 fmt::detail::mapped_type_constant<test_string<char>,
885 fmt::format_context>::value;
886 EXPECT_EQ(type, fmt::detail::type::string_type);
887 }
888
889 struct implicitly_convertible_to_string {
890 operator std::string() const { return "foo"; }
891 };
892
893 struct implicitly_convertible_to_string_view {
894 operator fmt::string_view() const { return "foo"; }
895 };
896
897 TEST(core_test, format_implicitly_convertible_to_string_view) {
898 EXPECT_EQ("foo", fmt::format("{}", implicitly_convertible_to_string_view()));
899 }
900
901 // std::is_constructible is broken in MSVC until version 2015.
902 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900
903 struct explicitly_convertible_to_string_view {
904 explicit operator fmt::string_view() const { return "foo"; }
905 };
906
907 TEST(core_test, format_explicitly_convertible_to_string_view) {
908 // Types explicitly convertible to string_view are not formattable by
909 // default because it may introduce ODR violations.
910 static_assert(
911 !fmt::is_formattable<explicitly_convertible_to_string_view>::value, "");
912 }
913
914 # ifdef FMT_USE_STRING_VIEW
915 struct explicitly_convertible_to_std_string_view {
916 explicit operator std::string_view() const { return "foo"; }
917 };
918
919 TEST(core_test, format_explicitly_convertible_to_std_string_view) {
920 // Types explicitly convertible to string_view are not formattable by
921 // default because it may introduce ODR violations.
922 static_assert(
923 !fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
924 "");
925 }
926 # endif
927 #endif
928
929 struct convertible_to_long_long {
930 operator long long() const { return 1LL << 32; }
931 };
932
933 TEST(format_test, format_convertible_to_long_long) {
934 EXPECT_EQ("100000000", fmt::format("{:x}", convertible_to_long_long()));
935 }
936
937 struct disabled_rvalue_conversion {
938 operator const char*() const& { return "foo"; }
939 operator const char*() & { return "foo"; }
940 operator const char*() const&& = delete;
941 operator const char*() && = delete;
942 };
943
944 TEST(core_test, disabled_rvalue_conversion) {
945 EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
946 }
947
948 namespace adl_test {
949 template <typename... T> void make_format_args(const T&...) = delete;
950
951 struct string : std::string {};
952 } // namespace adl_test
953
954 // Test that formatting functions compile when make_format_args is found by ADL.
955 TEST(core_test, adl) {
956 // Only check compilation and don't run the code to avoid polluting the output
957 // and since the output is tested elsewhere.
958 if (fmt::detail::const_check(true)) return;
959 auto s = adl_test::string();
960 char buf[10];
961 (void)fmt::format("{}", s);
962 fmt::format_to(buf, "{}", s);
963 fmt::format_to_n(buf, 10, "{}", s);
964 (void)fmt::formatted_size("{}", s);
965 fmt::print("{}", s);
966 fmt::print(stdout, "{}", s);
967 }
968
969 TEST(core_test, has_const_formatter) {
970 EXPECT_TRUE((fmt::detail::has_const_formatter<const_formattable,
971 fmt::format_context>()));
972 EXPECT_FALSE((fmt::detail::has_const_formatter<nonconst_formattable,
973 fmt::format_context>()));
974 }
975
976 TEST(core_test, format_nonconst) {
977 EXPECT_EQ(fmt::format("{}", nonconst_formattable()), "test");
978 }