• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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