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 #include <algorithm>
9 #include <climits>
10 #include <cstring>
11 #include <functional>
12 #include <iterator>
13 #include <limits>
14 #include <memory>
15 #include <string>
16 #include <type_traits>
17
18 #include "test-assert.h"
19
20 #include "gmock.h"
21
22 // Check if fmt/core.h compiles with windows.h included before it.
23 #ifdef _WIN32
24 # include <windows.h>
25 #endif
26
27 #include "fmt/core.h"
28
29 #undef min
30 #undef max
31
32 using fmt::basic_format_arg;
33 using fmt::string_view;
34 using fmt::internal::buffer;
35 using fmt::internal::value;
36
37 using testing::_;
38 using testing::StrictMock;
39
40 namespace {
41
42 struct test_struct {};
43
44 template <typename Context, typename T>
make_arg(const T & value)45 basic_format_arg<Context> make_arg(const T& value) {
46 return fmt::internal::make_arg<Context>(value);
47 }
48 } // namespace
49
50 FMT_BEGIN_NAMESPACE
51 template <typename Char> struct formatter<test_struct, Char> {
52 template <typename ParseContext>
parseformatter53 auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
54 return ctx.begin();
55 }
56
57 typedef std::back_insert_iterator<buffer<Char>> iterator;
58
formatformatter59 auto format(test_struct, basic_format_context<iterator, char>& ctx)
60 -> decltype(ctx.out()) {
61 const Char* test = "test";
62 return std::copy_n(test, std::strlen(test), ctx.out());
63 }
64 };
65 FMT_END_NAMESPACE
66
67 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(BufferTest,Noncopyable)68 TEST(BufferTest, Noncopyable) {
69 EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
70 # if !FMT_MSC_VER
71 // std::is_copy_assignable is broken in MSVC2013.
72 EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
73 # endif
74 }
75
TEST(BufferTest,Nonmoveable)76 TEST(BufferTest, Nonmoveable) {
77 EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
78 # if !FMT_MSC_VER
79 // std::is_move_assignable is broken in MSVC2013.
80 EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
81 # endif
82 }
83 #endif
84
85 // A test buffer with a dummy grow method.
86 template <typename T> struct test_buffer : buffer<T> {
growtest_buffer87 void grow(std::size_t capacity) { this->set(nullptr, capacity); }
88 };
89
90 template <typename T> struct mock_buffer : buffer<T> {
91 MOCK_METHOD1(do_grow, void(std::size_t capacity));
92
growmock_buffer93 void grow(std::size_t capacity) {
94 this->set(this->data(), capacity);
95 do_grow(capacity);
96 }
97
mock_buffermock_buffer98 mock_buffer() {}
mock_buffermock_buffer99 mock_buffer(T* data) { this->set(data, 0); }
mock_buffermock_buffer100 mock_buffer(T* data, std::size_t capacity) { this->set(data, capacity); }
101 };
102
TEST(BufferTest,Ctor)103 TEST(BufferTest, Ctor) {
104 {
105 mock_buffer<int> buffer;
106 EXPECT_EQ(nullptr, buffer.data());
107 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
108 EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
109 }
110 {
111 int dummy;
112 mock_buffer<int> buffer(&dummy);
113 EXPECT_EQ(&dummy, &buffer[0]);
114 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
115 EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
116 }
117 {
118 int dummy;
119 std::size_t capacity = std::numeric_limits<std::size_t>::max();
120 mock_buffer<int> buffer(&dummy, capacity);
121 EXPECT_EQ(&dummy, &buffer[0]);
122 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
123 EXPECT_EQ(capacity, buffer.capacity());
124 }
125 }
126
127 struct dying_buffer : test_buffer<int> {
128 MOCK_METHOD0(die, void());
~dying_bufferdying_buffer129 ~dying_buffer() { die(); }
130
131 private:
132 virtual void avoid_weak_vtable();
133 };
134
avoid_weak_vtable()135 void dying_buffer::avoid_weak_vtable() {}
136
TEST(BufferTest,VirtualDtor)137 TEST(BufferTest, VirtualDtor) {
138 typedef StrictMock<dying_buffer> stict_mock_buffer;
139 stict_mock_buffer* mock_buffer = new stict_mock_buffer();
140 EXPECT_CALL(*mock_buffer, die());
141 buffer<int>* buffer = mock_buffer;
142 delete buffer;
143 }
144
TEST(BufferTest,Access)145 TEST(BufferTest, Access) {
146 char data[10];
147 mock_buffer<char> buffer(data, sizeof(data));
148 buffer[0] = 11;
149 EXPECT_EQ(11, buffer[0]);
150 buffer[3] = 42;
151 EXPECT_EQ(42, *(&buffer[0] + 3));
152 const fmt::internal::buffer<char>& const_buffer = buffer;
153 EXPECT_EQ(42, const_buffer[3]);
154 }
155
TEST(BufferTest,Resize)156 TEST(BufferTest, Resize) {
157 char data[123];
158 mock_buffer<char> buffer(data, sizeof(data));
159 buffer[10] = 42;
160 EXPECT_EQ(42, buffer[10]);
161 buffer.resize(20);
162 EXPECT_EQ(20u, buffer.size());
163 EXPECT_EQ(123u, buffer.capacity());
164 EXPECT_EQ(42, buffer[10]);
165 buffer.resize(5);
166 EXPECT_EQ(5u, buffer.size());
167 EXPECT_EQ(123u, buffer.capacity());
168 EXPECT_EQ(42, buffer[10]);
169 // Check if resize calls grow.
170 EXPECT_CALL(buffer, do_grow(124));
171 buffer.resize(124);
172 EXPECT_CALL(buffer, do_grow(200));
173 buffer.resize(200);
174 }
175
TEST(BufferTest,Clear)176 TEST(BufferTest, Clear) {
177 test_buffer<char> buffer;
178 buffer.resize(20);
179 buffer.resize(0);
180 EXPECT_EQ(static_cast<size_t>(0), buffer.size());
181 EXPECT_EQ(20u, buffer.capacity());
182 }
183
TEST(BufferTest,Append)184 TEST(BufferTest, Append) {
185 char data[15];
186 mock_buffer<char> buffer(data, 10);
187 const char* test = "test";
188 buffer.append(test, test + 5);
189 EXPECT_STREQ(test, &buffer[0]);
190 EXPECT_EQ(5u, buffer.size());
191 buffer.resize(10);
192 EXPECT_CALL(buffer, do_grow(12));
193 buffer.append(test, test + 2);
194 EXPECT_EQ('t', buffer[10]);
195 EXPECT_EQ('e', buffer[11]);
196 EXPECT_EQ(12u, buffer.size());
197 }
198
TEST(BufferTest,AppendAllocatesEnoughStorage)199 TEST(BufferTest, AppendAllocatesEnoughStorage) {
200 char data[19];
201 mock_buffer<char> buffer(data, 10);
202 const char* test = "abcdefgh";
203 buffer.resize(10);
204 EXPECT_CALL(buffer, do_grow(19));
205 buffer.append(test, test + 9);
206 }
207
TEST(ArgTest,FormatArgs)208 TEST(ArgTest, FormatArgs) {
209 fmt::format_args args;
210 EXPECT_FALSE(args.get(1));
211 }
212
213 struct custom_context {
214 typedef char char_type;
215
216 template <typename T> struct formatter_type {
217 template <typename ParseContext>
parsecustom_context::formatter_type218 auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
219 return ctx.begin();
220 }
221
formatcustom_context::formatter_type222 const char* format(const T&, custom_context& ctx) {
223 ctx.called = true;
224 return nullptr;
225 }
226 };
227
228 bool called;
229 fmt::format_parse_context ctx;
230
parse_contextcustom_context231 fmt::format_parse_context& parse_context() { return ctx; }
advance_tocustom_context232 void advance_to(const char*) {}
233 };
234
TEST(ArgTest,MakeValueWithCustomContext)235 TEST(ArgTest, MakeValueWithCustomContext) {
236 test_struct t;
237 fmt::internal::value<custom_context> arg(
238 fmt::internal::arg_mapper<custom_context>().map(t));
239 custom_context ctx = {false, fmt::format_parse_context("")};
240 arg.custom.format(&t, ctx.parse_context(), ctx);
241 EXPECT_TRUE(ctx.called);
242 }
243
244 FMT_BEGIN_NAMESPACE
245 namespace internal {
246 template <typename Char>
operator ==(custom_value<Char> lhs,custom_value<Char> rhs)247 bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) {
248 return lhs.value == rhs.value;
249 }
250 } // namespace internal
251 FMT_END_NAMESPACE
252
253 // Use a unique result type to make sure that there are no undesirable
254 // conversions.
255 struct test_result {};
256
257 template <typename T> struct mock_visitor {
258 template <typename U> struct result { typedef test_result type; };
259
mock_visitormock_visitor260 mock_visitor() {
261 ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result()));
262 }
263
264 MOCK_METHOD1_T(visit, test_result(T value));
265 MOCK_METHOD0_T(unexpected, void());
266
operator ()mock_visitor267 test_result operator()(T value) { return visit(value); }
268
operator ()mock_visitor269 template <typename U> test_result operator()(U) {
270 unexpected();
271 return test_result();
272 }
273 };
274
275 template <typename T> struct visit_type { typedef T Type; };
276
277 #define VISIT_TYPE(Type_, visit_type_) \
278 template <> struct visit_type<Type_> { typedef visit_type_ Type; }
279
280 VISIT_TYPE(signed char, int);
281 VISIT_TYPE(unsigned char, unsigned);
282 VISIT_TYPE(short, int);
283 VISIT_TYPE(unsigned short, unsigned);
284
285 #if LONG_MAX == INT_MAX
286 VISIT_TYPE(long, int);
287 VISIT_TYPE(unsigned long, unsigned);
288 #else
289 VISIT_TYPE(long, long long);
290 VISIT_TYPE(unsigned long, unsigned long long);
291 #endif
292
293 #define CHECK_ARG_(Char, expected, value) \
294 { \
295 testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
296 EXPECT_CALL(visitor, visit(expected)); \
297 typedef std::back_insert_iterator<buffer<Char>> iterator; \
298 fmt::visit_format_arg( \
299 visitor, make_arg<fmt::basic_format_context<iterator, Char>>(value)); \
300 }
301
302 #define CHECK_ARG(value, typename_) \
303 { \
304 typedef decltype(value) value_type; \
305 typename_ visit_type<value_type>::Type expected = value; \
306 CHECK_ARG_(char, expected, value) \
307 CHECK_ARG_(wchar_t, expected, value) \
308 }
309
310 template <typename T> class NumericArgTest : public testing::Test {};
311
312 typedef ::testing::Types<bool, signed char, unsigned char, signed,
313 unsigned short, int, unsigned, long, unsigned long,
314 long long, unsigned long long, float, double,
315 long double>
316 Types;
317 TYPED_TEST_CASE(NumericArgTest, Types);
318
319 template <typename T>
test_value()320 typename std::enable_if<std::is_integral<T>::value, T>::type test_value() {
321 return static_cast<T>(42);
322 }
323
324 template <typename T>
325 typename std::enable_if<std::is_floating_point<T>::value, T>::type
test_value()326 test_value() {
327 return static_cast<T>(4.2);
328 }
329
TYPED_TEST(NumericArgTest,MakeAndVisit)330 TYPED_TEST(NumericArgTest, MakeAndVisit) {
331 CHECK_ARG(test_value<TypeParam>(), typename);
332 CHECK_ARG(std::numeric_limits<TypeParam>::min(), typename);
333 CHECK_ARG(std::numeric_limits<TypeParam>::max(), typename);
334 }
335
TEST(ArgTest,CharArg)336 TEST(ArgTest, CharArg) {
337 CHECK_ARG_(char, 'a', 'a');
338 CHECK_ARG_(wchar_t, L'a', 'a');
339 CHECK_ARG_(wchar_t, L'a', L'a');
340 }
341
TEST(ArgTest,StringArg)342 TEST(ArgTest, StringArg) {
343 char str_data[] = "test";
344 char* str = str_data;
345 const char* cstr = str;
346 CHECK_ARG_(char, cstr, str);
347
348 string_view sref(str);
349 CHECK_ARG_(char, sref, std::string(str));
350 }
351
TEST(ArgTest,WStringArg)352 TEST(ArgTest, WStringArg) {
353 wchar_t str_data[] = L"test";
354 wchar_t* str = str_data;
355 const wchar_t* cstr = str;
356
357 fmt::wstring_view sref(str);
358 CHECK_ARG_(wchar_t, cstr, str);
359 CHECK_ARG_(wchar_t, cstr, cstr);
360 CHECK_ARG_(wchar_t, sref, std::wstring(str));
361 CHECK_ARG_(wchar_t, sref, fmt::wstring_view(str));
362 }
363
TEST(ArgTest,PointerArg)364 TEST(ArgTest, PointerArg) {
365 void* p = nullptr;
366 const void* cp = nullptr;
367 CHECK_ARG_(char, cp, p);
368 CHECK_ARG_(wchar_t, cp, p);
369 CHECK_ARG(cp, );
370 }
371
372 struct check_custom {
operator ()check_custom373 test_result operator()(
374 fmt::basic_format_arg<fmt::format_context>::handle h) const {
375 struct test_buffer : fmt::internal::buffer<char> {
376 char data[10];
377 test_buffer() : fmt::internal::buffer<char>(data, 0, 10) {}
378 void grow(std::size_t) {}
379 } buffer;
380 fmt::internal::buffer<char>& base = buffer;
381 fmt::format_parse_context parse_ctx("");
382 fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
383 h.format(parse_ctx, ctx);
384 EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
385 return test_result();
386 }
387 };
388
TEST(ArgTest,CustomArg)389 TEST(ArgTest, CustomArg) {
390 test_struct test;
391 typedef mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>
392 visitor;
393 testing::StrictMock<visitor> v;
394 EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom()));
395 fmt::visit_format_arg(v, make_arg<fmt::format_context>(test));
396 }
397
TEST(ArgTest,VisitInvalidArg)398 TEST(ArgTest, VisitInvalidArg) {
399 testing::StrictMock<mock_visitor<fmt::monostate>> visitor;
400 EXPECT_CALL(visitor, visit(_));
401 fmt::basic_format_arg<fmt::format_context> arg;
402 fmt::visit_format_arg(visitor, arg);
403 }
404
TEST(StringViewTest,Length)405 TEST(StringViewTest, Length) {
406 // Test that StringRef::size() returns string length, not buffer size.
407 char str[100] = "some string";
408 EXPECT_EQ(std::strlen(str), string_view(str).size());
409 EXPECT_LT(std::strlen(str), sizeof(str));
410 }
411
412 // Check string_view's comparison operator.
check_op()413 template <template <typename> class Op> void check_op() {
414 const char* inputs[] = {"foo", "fop", "fo"};
415 std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
416 for (std::size_t i = 0; i < num_inputs; ++i) {
417 for (std::size_t j = 0; j < num_inputs; ++j) {
418 string_view lhs(inputs[i]), rhs(inputs[j]);
419 EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
420 }
421 }
422 }
423
TEST(StringViewTest,Compare)424 TEST(StringViewTest, Compare) {
425 EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0);
426 EXPECT_GT(string_view("fop").compare(string_view("foo")), 0);
427 EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
428 EXPECT_GT(string_view("foo").compare(string_view("fo")), 0);
429 EXPECT_LT(string_view("fo").compare(string_view("foo")), 0);
430 check_op<std::equal_to>();
431 check_op<std::not_equal_to>();
432 check_op<std::less>();
433 check_op<std::less_equal>();
434 check_op<std::greater>();
435 check_op<std::greater_equal>();
436 }
437
438 struct enabled_formatter {};
439 struct disabled_formatter {};
440 struct disabled_formatter_convertible {
operator intdisabled_formatter_convertible441 operator int() const { return 42; }
442 };
443
444 FMT_BEGIN_NAMESPACE
445 template <> struct formatter<enabled_formatter> {
parseformatter446 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
447 return ctx.begin();
448 }
formatformatter449 auto format(enabled_formatter, format_context& ctx) -> decltype(ctx.out()) {
450 return ctx.out();
451 }
452 };
453 FMT_END_NAMESPACE
454
TEST(CoreTest,HasFormatter)455 TEST(CoreTest, HasFormatter) {
456 using fmt::has_formatter;
457 using context = fmt::format_context;
458 static_assert(has_formatter<enabled_formatter, context>::value, "");
459 static_assert(!has_formatter<disabled_formatter, context>::value, "");
460 static_assert(!has_formatter<disabled_formatter_convertible, context>::value, "");
461 }
462
463 struct convertible_to_int {
operator intconvertible_to_int464 operator int() const { return 42; }
465 };
466
467 FMT_BEGIN_NAMESPACE
468 template <> struct formatter<convertible_to_int> {
parseformatter469 auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
470 return ctx.begin();
471 }
formatformatter472 auto format(convertible_to_int, format_context& ctx) -> decltype(ctx.out()) {
473 return std::copy_n("foo", 3, ctx.out());
474 }
475 };
476 FMT_END_NAMESPACE
477
TEST(CoreTest,FormatterOverridesImplicitConversion)478 TEST(CoreTest, FormatterOverridesImplicitConversion) {
479 EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo");
480 }
481
482 namespace my_ns {
483 template <typename Char> class my_string {
484 public:
my_string(const Char * s)485 my_string(const Char* s) : s_(s) {}
data() const486 const Char* data() const FMT_NOEXCEPT { return s_.data(); }
length() const487 std::size_t length() const FMT_NOEXCEPT { return s_.size(); }
operator const Char*() const488 operator const Char*() const { return s_.c_str(); }
489
490 private:
491 std::basic_string<Char> s_;
492 };
493
494 template <typename Char>
to_string_view(const my_string<Char> & s)495 inline fmt::basic_string_view<Char> to_string_view(const my_string<Char>& s)
496 FMT_NOEXCEPT {
497 return {s.data(), s.length()};
498 }
499
500 struct non_string {};
501 } // namespace my_ns
502
503 namespace FakeQt {
504 class QString {
505 public:
QString(const wchar_t * s)506 QString(const wchar_t* s) : s_(std::make_shared<std::wstring>(s)) {}
utf16() const507 const wchar_t* utf16() const FMT_NOEXCEPT { return s_->data(); }
size() const508 int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); }
509
510 private:
511 std::shared_ptr<std::wstring> s_;
512 };
513
to_string_view(const QString & s)514 inline fmt::basic_string_view<wchar_t> to_string_view(const QString& s)
515 FMT_NOEXCEPT {
516 return {s.utf16(), static_cast<std::size_t>(s.size())};
517 }
518 } // namespace FakeQt
519
520 template <typename T> class IsStringTest : public testing::Test {};
521
522 typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
523 TYPED_TEST_CASE(IsStringTest, StringCharTypes);
524
525 namespace {
526 template <typename Char>
527 struct derived_from_string_view : fmt::basic_string_view<Char> {};
528 } // namespace
529
TYPED_TEST(IsStringTest,IsString)530 TYPED_TEST(IsStringTest, IsString) {
531 EXPECT_TRUE(fmt::internal::is_string<TypeParam*>::value);
532 EXPECT_TRUE(fmt::internal::is_string<const TypeParam*>::value);
533 EXPECT_TRUE(fmt::internal::is_string<TypeParam[2]>::value);
534 EXPECT_TRUE(fmt::internal::is_string<const TypeParam[2]>::value);
535 EXPECT_TRUE(fmt::internal::is_string<std::basic_string<TypeParam>>::value);
536 EXPECT_TRUE(
537 fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value);
538 EXPECT_TRUE(
539 fmt::internal::is_string<derived_from_string_view<TypeParam>>::value);
540 using string_view = fmt::internal::std_string_view<TypeParam>;
541 EXPECT_TRUE(std::is_empty<string_view>::value !=
542 fmt::internal::is_string<string_view>::value);
543 EXPECT_TRUE(fmt::internal::is_string<my_ns::my_string<TypeParam>>::value);
544 EXPECT_FALSE(fmt::internal::is_string<my_ns::non_string>::value);
545 EXPECT_TRUE(fmt::internal::is_string<FakeQt::QString>::value);
546 }
547
TEST(CoreTest,Format)548 TEST(CoreTest, Format) {
549 // This should work without including fmt/format.h.
550 #ifdef FMT_FORMAT_H_
551 # error fmt/format.h must not be included in the core test
552 #endif
553 EXPECT_EQ(fmt::format("{}", 42), "42");
554 }
555
TEST(CoreTest,FormatTo)556 TEST(CoreTest, FormatTo) {
557 // This should work without including fmt/format.h.
558 #ifdef FMT_FORMAT_H_
559 # error fmt/format.h must not be included in the core test
560 #endif
561 std::string s;
562 fmt::format_to(std::back_inserter(s), "{}", 42);
563 EXPECT_EQ(s, "42");
564 }
565
TEST(CoreTest,ToStringViewForeignStrings)566 TEST(CoreTest, ToStringViewForeignStrings) {
567 using namespace my_ns;
568 using namespace FakeQt;
569 EXPECT_EQ(to_string_view(my_string<char>("42")), "42");
570 EXPECT_EQ(to_string_view(my_string<wchar_t>(L"42")), L"42");
571 EXPECT_EQ(to_string_view(QString(L"42")), L"42");
572 fmt::internal::type type =
573 fmt::internal::mapped_type_constant<my_string<char>,
574 fmt::format_context>::value;
575 EXPECT_EQ(type, fmt::internal::string_type);
576 type = fmt::internal::mapped_type_constant<my_string<wchar_t>,
577 fmt::wformat_context>::value;
578 EXPECT_EQ(type, fmt::internal::string_type);
579 type =
580 fmt::internal::mapped_type_constant<QString, fmt::wformat_context>::value;
581 EXPECT_EQ(type, fmt::internal::string_type);
582 // Does not compile: only wide format contexts are compatible with QString!
583 // type = fmt::internal::mapped_type_constant<QString,
584 // fmt::format_context>::value;
585 }
586
TEST(CoreTest,FormatForeignStrings)587 TEST(CoreTest, FormatForeignStrings) {
588 using namespace my_ns;
589 using namespace FakeQt;
590 EXPECT_EQ(fmt::format(my_string<char>("{}"), 42), "42");
591 EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), 42), L"42");
592 EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
593 EXPECT_EQ(fmt::format(QString(L"{}"), my_string<wchar_t>(L"42")), L"42");
594 EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), QString(L"42")), L"42");
595 }
596
597 struct implicitly_convertible_to_string {
operator std::stringimplicitly_convertible_to_string598 operator std::string() const { return "foo"; }
599 };
600
601 struct implicitly_convertible_to_string_view {
operator fmt::string_viewimplicitly_convertible_to_string_view602 operator fmt::string_view() const { return "foo"; }
603 };
604
TEST(FormatterTest,FormatImplicitlyConvertibleToStringView)605 TEST(FormatterTest, FormatImplicitlyConvertibleToStringView) {
606 EXPECT_EQ("foo", fmt::format("{}", implicitly_convertible_to_string_view()));
607 }
608
609 // std::is_constructible is broken in MSVC until version 2015.
610 #if !FMT_MSC_VER || FMT_MSC_VER >= 1900
611 struct explicitly_convertible_to_string_view {
operator fmt::string_viewexplicitly_convertible_to_string_view612 explicit operator fmt::string_view() const { return "foo"; }
613 };
614
TEST(FormatterTest,FormatExplicitlyConvertibleToStringView)615 TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
616 EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
617 }
618
619 #ifdef FMT_USE_STRING_VIEW
620 struct explicitly_convertible_to_std_string_view {
operator std::string_viewexplicitly_convertible_to_std_string_view621 explicit operator std::string_view() const { return "foo"; }
622 };
623
TEST(FormatterTest,FormatExplicitlyConvertibleToStdStringView)624 TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
625 EXPECT_EQ("foo",
626 fmt::format("{}", explicitly_convertible_to_std_string_view()));
627 }
628 #endif
629
630 struct explicitly_convertible_to_wstring_view {
operator fmt::wstring_viewexplicitly_convertible_to_wstring_view631 explicit operator fmt::wstring_view() const { return L"foo"; }
632 };
633
TEST(FormatterTest,FormatExplicitlyConvertibleToWStringView)634 TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
635 EXPECT_EQ(L"foo",
636 fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
637 }
638 #endif
639
640 struct disabled_rvalue_conversion {
operator const char*disabled_rvalue_conversion641 operator const char*() const& { return "foo"; }
operator const char*disabled_rvalue_conversion642 operator const char*()& { return "foo"; }
643 operator const char*() const&& = delete;
644 operator const char*()&& = delete;
645 };
646
TEST(FormatterTest,DisabledRValueConversion)647 TEST(FormatterTest, DisabledRValueConversion) {
648 EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
649 }
650