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