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/base.h"
13
14 #include <climits> // INT_MAX
15 #include <cstring> // std::strlen
16 #include <functional> // std::equal_to
17 #include <iterator> // std::back_insert_iterator, std::distance
18 #include <limits> // std::numeric_limits
19 #include <string> // std::string
20 #include <type_traits> // std::is_same
21
22 #include "gmock/gmock.h"
23
24 using fmt::string_view;
25 using fmt::detail::buffer;
26
27 using testing::_;
28 using testing::Invoke;
29 using testing::Return;
30
31 #ifdef FMT_FORMAT_H_
32 # error core-test includes format.h
33 #endif
34
copy(fmt::string_view s,fmt::appender out)35 fmt::appender copy(fmt::string_view s, fmt::appender out) {
36 for (char c : s) *out++ = c;
37 return out;
38 }
39
TEST(string_view_test,value_type)40 TEST(string_view_test, value_type) {
41 static_assert(std::is_same<string_view::value_type, char>::value, "");
42 }
43
TEST(string_view_test,ctor)44 TEST(string_view_test, ctor) {
45 EXPECT_STREQ("abc", fmt::string_view("abc").data());
46 EXPECT_EQ(3u, fmt::string_view("abc").size());
47
48 EXPECT_STREQ("defg", fmt::string_view(std::string("defg")).data());
49 EXPECT_EQ(4u, fmt::string_view(std::string("defg")).size());
50 }
51
TEST(string_view_test,length)52 TEST(string_view_test, length) {
53 // Test that string_view::size() returns string length, not buffer size.
54 char str[100] = "some string";
55 EXPECT_EQ(std::strlen(str), string_view(str).size());
56 EXPECT_LT(std::strlen(str), sizeof(str));
57 }
58
59 // Check string_view's comparison operator.
check_op()60 template <template <typename> class Op> void check_op() {
61 const char* inputs[] = {"foo", "fop", "fo"};
62 size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
63 for (size_t i = 0; i < num_inputs; ++i) {
64 for (size_t j = 0; j < num_inputs; ++j) {
65 string_view lhs(inputs[i]), rhs(inputs[j]);
66 EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
67 }
68 }
69 }
70
TEST(string_view_test,compare)71 TEST(string_view_test, compare) {
72 EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0);
73 EXPECT_GT(string_view("fop").compare(string_view("foo")), 0);
74 EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
75 EXPECT_GT(string_view("foo").compare(string_view("fo")), 0);
76 EXPECT_LT(string_view("fo").compare(string_view("foo")), 0);
77
78 EXPECT_TRUE(string_view("foo").starts_with('f'));
79 EXPECT_FALSE(string_view("foo").starts_with('o'));
80 EXPECT_FALSE(string_view().starts_with('o'));
81
82 EXPECT_TRUE(string_view("foo").starts_with("fo"));
83 EXPECT_TRUE(string_view("foo").starts_with("foo"));
84 EXPECT_FALSE(string_view("foo").starts_with("fooo"));
85 EXPECT_FALSE(string_view().starts_with("fooo"));
86
87 check_op<std::equal_to>();
88 check_op<std::not_equal_to>();
89 check_op<std::less>();
90 check_op<std::less_equal>();
91 check_op<std::greater>();
92 check_op<std::greater_equal>();
93 }
94
TEST(base_test,is_locking)95 TEST(base_test, is_locking) {
96 EXPECT_FALSE(fmt::detail::is_locking<const char(&)[3]>());
97 }
98
TEST(base_test,is_output_iterator)99 TEST(base_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
TEST(base_test,is_back_insert_iterator)112 TEST(base_test, is_back_insert_iterator) {
113 EXPECT_TRUE(fmt::detail::is_back_insert_iterator<
114 std::back_insert_iterator<std::string>>::value);
115 EXPECT_FALSE(fmt::detail::is_back_insert_iterator<
116 std::front_insert_iterator<std::string>>::value);
117 }
118
119 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(buffer_test,noncopyable)120 TEST(buffer_test, noncopyable) {
121 EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
122 # if !FMT_MSC_VERSION
123 // std::is_copy_assignable is broken in MSVC2013.
124 EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
125 # endif
126 }
127
TEST(buffer_test,nonmoveable)128 TEST(buffer_test, nonmoveable) {
129 EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
130 # if !FMT_MSC_VERSION
131 // std::is_move_assignable is broken in MSVC2013.
132 EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
133 # endif
134 }
135 #endif
136
TEST(buffer_test,indestructible)137 TEST(buffer_test, indestructible) {
138 static_assert(!std::is_destructible<fmt::detail::buffer<int>>(),
139 "buffer's destructor is protected");
140 }
141
142 template <typename T> struct mock_buffer final : buffer<T> {
143 MOCK_METHOD(size_t, do_grow, (size_t));
144
growmock_buffer145 static void grow(buffer<T>& buf, size_t capacity) {
146 auto& self = static_cast<mock_buffer&>(buf);
147 self.set(buf.data(), self.do_grow(capacity));
148 }
149
mock_buffermock_buffer150 mock_buffer(T* data = nullptr, size_t buf_capacity = 0) : buffer<T>(grow) {
151 this->set(data, buf_capacity);
152 ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) {
153 return capacity;
154 }));
155 }
156 };
157
TEST(buffer_test,ctor)158 TEST(buffer_test, ctor) {
159 {
160 mock_buffer<int> buffer;
161 EXPECT_EQ(nullptr, buffer.data());
162 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
163 EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
164 }
165 {
166 int dummy;
167 mock_buffer<int> buffer(&dummy);
168 EXPECT_EQ(&dummy, &buffer[0]);
169 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
170 EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
171 }
172 {
173 int dummy;
174 size_t capacity = std::numeric_limits<size_t>::max();
175 mock_buffer<int> buffer(&dummy, capacity);
176 EXPECT_EQ(&dummy, &buffer[0]);
177 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
178 EXPECT_EQ(capacity, buffer.capacity());
179 }
180 }
181
TEST(buffer_test,access)182 TEST(buffer_test, access) {
183 char data[10];
184 mock_buffer<char> buffer(data, sizeof(data));
185 buffer[0] = 11;
186 EXPECT_EQ(11, buffer[0]);
187 buffer[3] = 42;
188 EXPECT_EQ(42, *(&buffer[0] + 3));
189 const fmt::detail::buffer<char>& const_buffer = buffer;
190 EXPECT_EQ(42, const_buffer[3]);
191 }
192
TEST(buffer_test,try_resize)193 TEST(buffer_test, try_resize) {
194 char data[123];
195 mock_buffer<char> buffer(data, sizeof(data));
196 buffer[10] = 42;
197 EXPECT_EQ(42, buffer[10]);
198 buffer.try_resize(20);
199 EXPECT_EQ(20u, buffer.size());
200 EXPECT_EQ(123u, buffer.capacity());
201 EXPECT_EQ(42, buffer[10]);
202 buffer.try_resize(5);
203 EXPECT_EQ(5u, buffer.size());
204 EXPECT_EQ(123u, buffer.capacity());
205 EXPECT_EQ(42, buffer[10]);
206 // Check if try_resize calls grow.
207 EXPECT_CALL(buffer, do_grow(124));
208 buffer.try_resize(124);
209 EXPECT_CALL(buffer, do_grow(200));
210 buffer.try_resize(200);
211 }
212
TEST(buffer_test,try_resize_partial)213 TEST(buffer_test, try_resize_partial) {
214 char data[10];
215 mock_buffer<char> buffer(data, sizeof(data));
216 EXPECT_CALL(buffer, do_grow(20)).WillOnce(Return(15));
217 buffer.try_resize(20);
218 EXPECT_EQ(buffer.capacity(), 15);
219 EXPECT_EQ(buffer.size(), 15);
220 }
221
TEST(buffer_test,clear)222 TEST(buffer_test, clear) {
223 mock_buffer<char> buffer;
224 EXPECT_CALL(buffer, do_grow(20));
225 buffer.try_resize(20);
226 buffer.try_resize(0);
227 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
228 EXPECT_EQ(20u, buffer.capacity());
229 }
230
TEST(buffer_test,append)231 TEST(buffer_test, append) {
232 char data[15];
233 mock_buffer<char> buffer(data, 10);
234 auto test = "test";
235 buffer.append(test, test + 5);
236 EXPECT_STREQ(test, &buffer[0]);
237 EXPECT_EQ(5u, buffer.size());
238 buffer.try_resize(10);
239 EXPECT_CALL(buffer, do_grow(12));
240 buffer.append(test, test + 2);
241 EXPECT_EQ('t', buffer[10]);
242 EXPECT_EQ('e', buffer[11]);
243 EXPECT_EQ(12u, buffer.size());
244 }
245
TEST(buffer_test,append_partial)246 TEST(buffer_test, append_partial) {
247 char data[10];
248 mock_buffer<char> buffer(data, sizeof(data));
249 testing::InSequence seq;
250 EXPECT_CALL(buffer, do_grow(15)).WillOnce(Return(10));
251 EXPECT_CALL(buffer, do_grow(15)).WillOnce(Invoke([&buffer](size_t) {
252 EXPECT_EQ(fmt::string_view(buffer.data(), buffer.size()), "0123456789");
253 buffer.clear();
254 return 10;
255 }));
256 auto test = "0123456789abcde";
257 buffer.append(test, test + 15);
258 }
259
TEST(buffer_test,append_allocates_enough_storage)260 TEST(buffer_test, append_allocates_enough_storage) {
261 char data[19];
262 mock_buffer<char> buffer(data, 10);
263 auto test = "abcdefgh";
264 buffer.try_resize(10);
265 EXPECT_CALL(buffer, do_grow(19));
266 buffer.append(test, test + 9);
267 }
268
TEST(base_test,get_buffer)269 TEST(base_test, get_buffer) {
270 mock_buffer<char> buffer;
271 void* buffer_ptr = &buffer;
272 auto&& appender_result = fmt::detail::get_buffer<char>(fmt::appender(buffer));
273 EXPECT_EQ(&appender_result, buffer_ptr);
274 auto&& back_inserter_result =
275 fmt::detail::get_buffer<char>(std::back_inserter(buffer));
276 EXPECT_EQ(&back_inserter_result, buffer_ptr);
277 }
278
279 struct test_struct {};
280
281 FMT_BEGIN_NAMESPACE
282 template <typename Char> struct formatter<test_struct, Char> {
parseformatter283 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
284 return ctx.begin();
285 }
286
formatformatter287 auto format(test_struct, format_context& ctx) const -> decltype(ctx.out()) {
288 return copy("test", ctx.out());
289 }
290 };
291 FMT_END_NAMESPACE
292
TEST(arg_test,format_args)293 TEST(arg_test, format_args) {
294 auto args = fmt::format_args();
295 EXPECT_FALSE(args.get(1));
296 }
297
298 // Use a unique result type to make sure that there are no undesirable
299 // conversions.
300 struct test_result {};
301
302 template <typename T> struct mock_visitor {
303 template <typename U> struct result {
304 using type = test_result;
305 };
306
mock_visitormock_visitor307 mock_visitor() {
308 ON_CALL(*this, visit(_)).WillByDefault(Return(test_result()));
309 }
310
311 MOCK_METHOD(test_result, visit, (T));
312 MOCK_METHOD(void, unexpected, ());
313
operator ()mock_visitor314 auto operator()(T value) -> test_result { return visit(value); }
315
operator ()mock_visitor316 template <typename U> auto operator()(U) -> test_result {
317 unexpected();
318 return test_result();
319 }
320 };
321
322 template <typename T> struct visit_type {
323 using type = T;
324 };
325
326 #define VISIT_TYPE(type_, visit_type_) \
327 template <> struct visit_type<type_> { \
328 using type = visit_type_; \
329 }
330
331 VISIT_TYPE(signed char, int);
332 VISIT_TYPE(unsigned char, unsigned);
333 VISIT_TYPE(short, int);
334 VISIT_TYPE(unsigned short, unsigned);
335
336 #if LONG_MAX == INT_MAX
337 VISIT_TYPE(long, int);
338 VISIT_TYPE(unsigned long, unsigned);
339 #else
340 VISIT_TYPE(long, long long);
341 VISIT_TYPE(unsigned long, unsigned long long);
342 #endif
343
344 #if FMT_BUILTIN_TYPES
345 # define CHECK_ARG(expected, value) \
346 { \
347 testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
348 EXPECT_CALL(visitor, visit(expected)); \
349 auto var = value; \
350 fmt::basic_format_arg<fmt::format_context>(var).visit(visitor); \
351 }
352 #else
353 # define CHECK_ARG(expected, value)
354 #endif
355
356 #define CHECK_ARG_SIMPLE(value) \
357 { \
358 using value_type = decltype(value); \
359 typename visit_type<value_type>::type expected = value; \
360 CHECK_ARG(expected, value) \
361 }
362
363 template <typename T> class numeric_arg_test : public testing::Test {};
364
365 #if FMT_BUILTIN_TYPES
366 using test_types =
367 testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
368 unsigned, long, unsigned long, long long, unsigned long long,
369 float, double, long double>;
370 #else
371 using test_types = testing::Types<int>;
372 #endif
373 TYPED_TEST_SUITE(numeric_arg_test, test_types);
374
375 template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
test_value()376 auto test_value() -> T {
377 return static_cast<T>(42);
378 }
379
380 template <typename T,
381 fmt::enable_if_t<std::is_floating_point<T>::value, int> = 0>
test_value()382 auto test_value() -> T {
383 return static_cast<T>(4.2);
384 }
385
TYPED_TEST(numeric_arg_test,make_and_visit)386 TYPED_TEST(numeric_arg_test, make_and_visit) {
387 CHECK_ARG_SIMPLE(test_value<TypeParam>());
388 CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::min());
389 CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
390 }
391
TEST(arg_test,char_arg)392 TEST(arg_test, char_arg) { CHECK_ARG('a', 'a'); }
393
TEST(arg_test,string_arg)394 TEST(arg_test, string_arg) {
395 char str_data[] = "test";
396 char* str = str_data;
397 const char* cstr = str;
398 CHECK_ARG(cstr, str);
399
400 auto sv = fmt::string_view(str);
401 CHECK_ARG(sv, std::string(str));
402 }
403
TEST(arg_test,pointer_arg)404 TEST(arg_test, pointer_arg) {
405 void* p = nullptr;
406 const void* cp = nullptr;
407 CHECK_ARG(cp, p);
408 CHECK_ARG_SIMPLE(cp);
409 }
410
TEST(arg_test,volatile_pointer_arg)411 TEST(arg_test, volatile_pointer_arg) {
412 const void* p = nullptr;
413 volatile int* vip = nullptr;
414 const volatile int* cvip = nullptr;
415 CHECK_ARG(p, static_cast<volatile void*>(vip));
416 CHECK_ARG(p, static_cast<const volatile void*>(cvip));
417 }
418
419 struct check_custom {
operator ()check_custom420 auto operator()(fmt::basic_format_arg<fmt::format_context>::handle h) const
421 -> test_result {
422 struct test_buffer final : fmt::detail::buffer<char> {
423 char data[10];
424 test_buffer()
425 : fmt::detail::buffer<char>([](buffer<char>&, size_t) {}, data, 0,
426 10) {}
427 } buffer;
428 auto parse_ctx = fmt::format_parse_context("");
429 auto ctx = fmt::format_context(fmt::appender(buffer), fmt::format_args());
430 h.format(parse_ctx, ctx);
431 EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
432 return test_result();
433 }
434 };
435
TEST(arg_test,custom_arg)436 TEST(arg_test, custom_arg) {
437 auto test = test_struct();
438 using visitor =
439 mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>;
440 auto&& v = testing::StrictMock<visitor>();
441 EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom()));
442 fmt::basic_format_arg<fmt::format_context>(test).visit(v);
443 }
444
TEST(arg_test,visit_invalid_arg)445 TEST(arg_test, visit_invalid_arg) {
446 auto&& visitor = testing::StrictMock<mock_visitor<fmt::monostate>>();
447 EXPECT_CALL(visitor, visit(_));
448 fmt::basic_format_arg<fmt::format_context>().visit(visitor);
449 }
450
451 #if FMT_USE_CONSTEXPR
452
453 enum class arg_id_result { none, index, name };
454 struct test_arg_id_handler {
455 arg_id_result res = arg_id_result::none;
456 int index = 0;
457 string_view name;
458
on_indextest_arg_id_handler459 constexpr void on_index(int i) {
460 res = arg_id_result::index;
461 index = i;
462 }
463
on_nametest_arg_id_handler464 constexpr void on_name(string_view n) {
465 res = arg_id_result::name;
466 name = n;
467 }
468 };
469
470 template <size_t N>
parse_arg_id(const char (& s)[N])471 constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) {
472 auto h = test_arg_id_handler();
473 fmt::detail::parse_arg_id(s, s + N, h);
474 return h;
475 }
476
TEST(base_test,constexpr_parse_arg_id)477 TEST(base_test, constexpr_parse_arg_id) {
478 static_assert(parse_arg_id("42:").res == arg_id_result::index, "");
479 static_assert(parse_arg_id("42:").index == 42, "");
480 static_assert(parse_arg_id("foo:").res == arg_id_result::name, "");
481 static_assert(parse_arg_id("foo:").name.size() == 3, "");
482 }
483
parse_test_specs(const char (& s)[N])484 template <size_t N> constexpr auto parse_test_specs(const char (&s)[N]) {
485 auto ctx = fmt::detail::compile_parse_context<char>(fmt::string_view(s, N),
486 43, nullptr);
487 auto specs = fmt::detail::dynamic_format_specs<>();
488 fmt::detail::parse_format_specs(s, s + N - 1, specs, ctx,
489 fmt::detail::type::float_type);
490 return specs;
491 }
492
TEST(base_test,constexpr_parse_format_specs)493 TEST(base_test, constexpr_parse_format_specs) {
494 static_assert(parse_test_specs("<").align() == fmt::align::left, "");
495 static_assert(parse_test_specs("*^").fill_unit<char>() == '*', "");
496 static_assert(parse_test_specs("+").sign() == fmt::sign::plus, "");
497 static_assert(parse_test_specs("-").sign() == fmt::sign::none, "");
498 static_assert(parse_test_specs(" ").sign() == fmt::sign::space, "");
499 static_assert(parse_test_specs("#").alt(), "");
500 static_assert(parse_test_specs("0").align() == fmt::align::numeric, "");
501 static_assert(parse_test_specs("L").localized(), "");
502 static_assert(parse_test_specs("42").width == 42, "");
503 static_assert(parse_test_specs("{42}").width_ref.index == 42, "");
504 static_assert(parse_test_specs(".42").precision == 42, "");
505 static_assert(parse_test_specs(".{42}").precision_ref.index == 42, "");
506 static_assert(parse_test_specs("f").type() == fmt::presentation_type::fixed,
507 "");
508 }
509
510 struct test_format_string_handler {
on_texttest_format_string_handler511 constexpr void on_text(const char*, const char*) {}
512
on_arg_idtest_format_string_handler513 constexpr auto on_arg_id() -> int { return 0; }
514
on_arg_idtest_format_string_handler515 template <typename T> constexpr auto on_arg_id(T) -> int { return 0; }
516
on_replacement_fieldtest_format_string_handler517 constexpr void on_replacement_field(int, const char*) {}
518
on_format_specstest_format_string_handler519 constexpr auto on_format_specs(int, const char* begin, const char*) -> const
520 char* {
521 return begin;
522 }
523
on_errortest_format_string_handler524 constexpr void on_error(const char*) { error = true; }
525
526 bool error = false;
527 };
528
parse_string(const char (& s)[N])529 template <size_t N> constexpr bool parse_string(const char (&s)[N]) {
530 auto h = test_format_string_handler();
531 fmt::detail::parse_format_string(fmt::string_view(s, N - 1), h);
532 return !h.error;
533 }
534
TEST(base_test,constexpr_parse_format_string)535 TEST(base_test, constexpr_parse_format_string) {
536 static_assert(parse_string("foo"), "");
537 static_assert(!parse_string("}"), "");
538 static_assert(parse_string("{}"), "");
539 static_assert(parse_string("{42}"), "");
540 static_assert(parse_string("{foo}"), "");
541 static_assert(parse_string("{:}"), "");
542 }
543 #endif // FMT_USE_CONSTEXPR
544
545 struct enabled_formatter {};
546 struct enabled_ptr_formatter {};
547 struct disabled_formatter {};
548 struct disabled_formatter_convertible {
operator intdisabled_formatter_convertible549 operator int() const { return 42; }
550 };
551
552 FMT_BEGIN_NAMESPACE
553 template <> struct formatter<enabled_formatter> {
parseformatter554 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
555 return ctx.begin();
556 }
formatformatter557 auto format(enabled_formatter, format_context& ctx) const
558 -> decltype(ctx.out()) {
559 return ctx.out();
560 }
561 };
562
563 template <> struct formatter<enabled_ptr_formatter*> {
parseformatter564 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
565 return ctx.begin();
566 }
formatformatter567 auto format(enabled_ptr_formatter*, format_context& ctx) const
568 -> decltype(ctx.out()) {
569 return ctx.out();
570 }
571 };
572 FMT_END_NAMESPACE
573
574 struct const_formattable {};
575 struct nonconst_formattable {};
576
577 FMT_BEGIN_NAMESPACE
578 template <> struct formatter<const_formattable> {
parseformatter579 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
580 return ctx.begin();
581 }
582
formatformatter583 auto format(const const_formattable&, format_context& ctx) const
584 -> decltype(ctx.out()) {
585 return copy("test", ctx.out());
586 }
587 };
588
589 template <> struct formatter<nonconst_formattable> {
parseformatter590 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
591 return ctx.begin();
592 }
593
formatformatter594 auto format(nonconst_formattable&, format_context& ctx) const
595 -> decltype(ctx.out()) {
596 return copy("test", ctx.out());
597 }
598 };
599 FMT_END_NAMESPACE
600
601 struct convertible_to_pointer {
operator const int*convertible_to_pointer602 operator const int*() const { return nullptr; }
603 };
604
605 struct convertible_to_pointer_formattable {
operator const int*convertible_to_pointer_formattable606 operator const int*() const { return nullptr; }
607 };
608
609 FMT_BEGIN_NAMESPACE
610 template <> struct formatter<convertible_to_pointer_formattable> {
parseformatter611 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
612 return ctx.begin();
613 }
614
formatformatter615 auto format(convertible_to_pointer_formattable, format_context& ctx) const
616 -> decltype(ctx.out()) {
617 return copy("test", ctx.out());
618 }
619 };
620 FMT_END_NAMESPACE
621
622 enum class unformattable_scoped_enum {};
623
TEST(base_test,is_formattable)624 TEST(base_test, is_formattable) {
625 EXPECT_FALSE(fmt::is_formattable<void>::value);
626 EXPECT_FALSE(fmt::is_formattable<wchar_t>::value);
627 #ifdef __cpp_char8_t
628 EXPECT_FALSE(fmt::is_formattable<char8_t>::value);
629 #endif
630 EXPECT_FALSE(fmt::is_formattable<char16_t>::value);
631 EXPECT_FALSE(fmt::is_formattable<char32_t>::value);
632 EXPECT_FALSE(fmt::is_formattable<signed char*>::value);
633 EXPECT_FALSE(fmt::is_formattable<unsigned char*>::value);
634 EXPECT_FALSE(fmt::is_formattable<const signed char*>::value);
635 EXPECT_FALSE(fmt::is_formattable<const unsigned char*>::value);
636 EXPECT_FALSE(fmt::is_formattable<const wchar_t*>::value);
637 EXPECT_FALSE(fmt::is_formattable<const wchar_t[3]>::value);
638 EXPECT_FALSE(fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value);
639 EXPECT_FALSE(fmt::is_formattable<enabled_ptr_formatter*>::value);
640 EXPECT_FALSE(fmt::is_formattable<disabled_formatter>::value);
641 EXPECT_FALSE(fmt::is_formattable<disabled_formatter_convertible>::value);
642
643 EXPECT_TRUE(fmt::is_formattable<enabled_formatter>::value);
644 EXPECT_TRUE(fmt::is_formattable<const_formattable&>::value);
645 EXPECT_TRUE(fmt::is_formattable<const const_formattable&>::value);
646
647 EXPECT_TRUE(fmt::is_formattable<nonconst_formattable&>::value);
648 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
649 EXPECT_FALSE(fmt::is_formattable<const nonconst_formattable&>::value);
650 #endif
651
652 EXPECT_FALSE(fmt::is_formattable<convertible_to_pointer>::value);
653 const auto f = convertible_to_pointer_formattable();
654 auto str = std::string();
655 fmt::format_to(std::back_inserter(str), "{}", f);
656 EXPECT_EQ(str, "test");
657
658 EXPECT_FALSE(fmt::is_formattable<void (*)()>::value);
659
660 struct s;
661 EXPECT_FALSE(fmt::is_formattable<int(s::*)>::value);
662 EXPECT_FALSE(fmt::is_formattable<int (s::*)()>::value);
663 EXPECT_FALSE(fmt::is_formattable<unformattable_scoped_enum>::value);
664 EXPECT_FALSE(fmt::is_formattable<unformattable_scoped_enum>::value);
665 }
666
667 #ifdef __cpp_concepts
TEST(base_test,formattable_concept)668 TEST(base_test, formattable_concept) {
669 static_assert(fmt::formattable<char>);
670 static_assert(fmt::formattable<char&>);
671 static_assert(fmt::formattable<char&&>);
672 static_assert(fmt::formattable<const char>);
673 static_assert(fmt::formattable<const char&>);
674 static_assert(fmt::formattable<const char&&>);
675 static_assert(fmt::formattable<int>);
676 static_assert(!fmt::formattable<wchar_t>);
677 }
678 #endif
679
TEST(base_test,format_to)680 TEST(base_test, format_to) {
681 auto s = std::string();
682 fmt::format_to(std::back_inserter(s), "{}", 42);
683 EXPECT_EQ(s, "42");
684 }
685
TEST(base_test,format_to_array)686 TEST(base_test, format_to_array) {
687 char buffer[4];
688 auto result = fmt::format_to(buffer, "{}", 12345);
689 EXPECT_EQ(4, std::distance(&buffer[0], result.out));
690 EXPECT_TRUE(result.truncated);
691 EXPECT_EQ(buffer + 4, result.out);
692 EXPECT_EQ("1234", fmt::string_view(buffer, 4));
693
694 char* out = nullptr;
695 EXPECT_THROW(out = result, std::runtime_error);
696 (void)out;
697
698 result = fmt::format_to(buffer, "{:s}", "foobar");
699 EXPECT_EQ(4, std::distance(&buffer[0], result.out));
700 EXPECT_TRUE(result.truncated);
701 EXPECT_EQ(buffer + 4, result.out);
702 EXPECT_EQ("foob", fmt::string_view(buffer, 4));
703
704 buffer[0] = 'x';
705 buffer[1] = 'x';
706 buffer[2] = 'x';
707 buffer[3] = 'x';
708 result = fmt::format_to(buffer, "{}", 'A');
709 EXPECT_EQ(1, std::distance(&buffer[0], result.out));
710 EXPECT_FALSE(result.truncated);
711 EXPECT_EQ(buffer + 1, result.out);
712 EXPECT_EQ("Axxx", fmt::string_view(buffer, 4));
713
714 result = fmt::format_to(buffer, "{}{} ", 'B', 'C');
715 EXPECT_EQ(3, std::distance(&buffer[0], result.out));
716 EXPECT_FALSE(result.truncated);
717 EXPECT_EQ(buffer + 3, result.out);
718 EXPECT_EQ("BC x", fmt::string_view(buffer, 4));
719
720 result = fmt::format_to(buffer, "{}", "ABCDE");
721 EXPECT_EQ(4, std::distance(&buffer[0], result.out));
722 EXPECT_TRUE(result.truncated);
723 EXPECT_EQ("ABCD", fmt::string_view(buffer, 4));
724
725 result = fmt::format_to(buffer, "{}", std::string(1000, '*').c_str());
726 EXPECT_EQ(4, std::distance(&buffer[0], result.out));
727 EXPECT_TRUE(result.truncated);
728 EXPECT_EQ("****", fmt::string_view(buffer, 4));
729 }
730
731 // Test that check is not found by ADL.
732 template <typename T> void check(T);
TEST(base_test,adl_check)733 TEST(base_test, adl_check) {
734 auto s = std::string();
735 fmt::format_to(std::back_inserter(s), "{}", test_struct());
736 EXPECT_EQ(s, "test");
737 }
738
739 struct implicitly_convertible_to_string_view {
operator fmt::string_viewimplicitly_convertible_to_string_view740 operator fmt::string_view() const { return "foo"; }
741 };
742
TEST(base_test,no_implicit_conversion_to_string_view)743 TEST(base_test, no_implicit_conversion_to_string_view) {
744 EXPECT_FALSE(
745 fmt::is_formattable<implicitly_convertible_to_string_view>::value);
746 }
747
748 #ifdef FMT_USE_STRING_VIEW
749 struct implicitly_convertible_to_std_string_view {
operator std::string_viewimplicitly_convertible_to_std_string_view750 operator std::string_view() const { return "foo"; }
751 };
752
TEST(base_test,no_implicit_conversion_to_std_string_view)753 TEST(base_test, no_implicit_conversion_to_std_string_view) {
754 EXPECT_FALSE(
755 fmt::is_formattable<implicitly_convertible_to_std_string_view>::value);
756 }
757 #endif
758
759 // std::is_constructible is broken in MSVC until version 2015.
760 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900
761 struct explicitly_convertible_to_string_view {
operator fmt::string_viewexplicitly_convertible_to_string_view762 explicit operator fmt::string_view() const { return "foo"; }
763 };
764
TEST(base_test,format_explicitly_convertible_to_string_view)765 TEST(base_test, format_explicitly_convertible_to_string_view) {
766 // Types explicitly convertible to string_view are not formattable by
767 // default because it may introduce ODR violations.
768 static_assert(
769 !fmt::is_formattable<explicitly_convertible_to_string_view>::value, "");
770 }
771
772 # ifdef FMT_USE_STRING_VIEW
773 struct explicitly_convertible_to_std_string_view {
operator std::string_viewexplicitly_convertible_to_std_string_view774 explicit operator std::string_view() const { return "foo"; }
775 };
776
TEST(base_test,format_explicitly_convertible_to_std_string_view)777 TEST(base_test, format_explicitly_convertible_to_std_string_view) {
778 // Types explicitly convertible to string_view are not formattable by
779 // default because it may introduce ODR violations.
780 static_assert(
781 !fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
782 "");
783 }
784 # endif
785 #endif
786
TEST(base_test,has_formatter)787 TEST(base_test, has_formatter) {
788 EXPECT_TRUE((fmt::detail::has_formatter<const const_formattable, char>()));
789 EXPECT_FALSE(
790 (fmt::detail::has_formatter<const nonconst_formattable, char>()));
791 }
792
TEST(base_test,format_nonconst)793 TEST(base_test, format_nonconst) {
794 auto s = std::string();
795 fmt::format_to(std::back_inserter(s), "{}", nonconst_formattable());
796 EXPECT_EQ(s, "test");
797 }
798
TEST(base_test,throw_in_buffer_dtor)799 TEST(base_test, throw_in_buffer_dtor) {
800 enum { buffer_size = 256 };
801
802 struct throwing_iterator {
803 int& count;
804
805 auto operator=(char) -> throwing_iterator& {
806 if (++count > buffer_size) throw std::exception();
807 return *this;
808 }
809 auto operator*() -> throwing_iterator& { return *this; }
810 auto operator++() -> throwing_iterator& { return *this; }
811 auto operator++(int) -> throwing_iterator { return *this; }
812 };
813
814 try {
815 int count = 0;
816 fmt::format_to(throwing_iterator{count}, fmt::runtime("{:{}}{"), "",
817 buffer_size + 1);
818 } catch (const std::exception&) {
819 }
820 }
821
822 struct its_a_trap {
operator Tits_a_trap823 template <typename T> operator T() const {
824 auto v = T();
825 v.x = 42;
826 return v;
827 }
828 };
829
830 FMT_BEGIN_NAMESPACE
831 template <> struct formatter<its_a_trap> {
parseformatter832 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
833 return ctx.begin();
834 }
835
formatformatter836 auto format(its_a_trap, format_context& ctx) const
837 -> decltype(ctx.out()) const {
838 auto out = ctx.out();
839 *out++ = 'x';
840 return out;
841 }
842 };
843 FMT_END_NAMESPACE
844
TEST(base_test,trappy_conversion)845 TEST(base_test, trappy_conversion) {
846 auto s = std::string();
847 fmt::format_to(std::back_inserter(s), "{}", its_a_trap());
848 EXPECT_EQ(s, "x");
849 }
850
851 struct custom_container {
852 char data;
853
854 using value_type = char;
855
sizecustom_container856 size_t size() const { return 0; }
resizecustom_container857 void resize(size_t) {}
858
push_backcustom_container859 void push_back(char) {}
operator []custom_container860 char& operator[](size_t) { return data; }
861 };
862
863 FMT_BEGIN_NAMESPACE
864 template <> struct is_contiguous<custom_container> : std::true_type {};
865 FMT_END_NAMESPACE
866
TEST(base_test,format_to_custom_container)867 TEST(base_test, format_to_custom_container) {
868 auto c = custom_container();
869 fmt::format_to(std::back_inserter(c), "");
870 }
871
872 struct nondeterministic_format_string {
873 mutable int i = 0;
operator string_viewnondeterministic_format_string874 FMT_CONSTEXPR operator string_view() const {
875 return string_view("{}", i++ != 0 ? 2 : 0);
876 }
877 };
878
TEST(base_test,no_repeated_format_string_conversions)879 TEST(base_test, no_repeated_format_string_conversions) {
880 #if !FMT_GCC_VERSION
881 char buf[10];
882 fmt::format_to(buf, nondeterministic_format_string());
883 #endif
884 }
885