1 // Formatting library for C++ - std::ostream support 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 "fmt/format.h"
9 
10 struct test {};
11 
12 // Test that there is no issues with specializations when fmt/ostream.h is
13 // included after fmt/format.h.
14 namespace fmt {
15 template <> struct formatter<test> : formatter<int> {
16   template <typename FormatContext>
formatfmt::formatter17   typename FormatContext::iterator format(const test&, FormatContext& ctx) {
18     return formatter<int>::format(42, ctx);
19   }
20 };
21 }  // namespace fmt
22 
23 #include <sstream>
24 
25 #include "fmt/ostream.h"
26 #include "fmt/ranges.h"
27 #include "gmock.h"
28 #include "gtest-extra.h"
29 #include "util.h"
30 
31 using fmt::format;
32 using fmt::format_error;
33 
operator <<(std::ostream & os,const Date & d)34 static std::ostream& operator<<(std::ostream& os, const Date& d) {
35   os << d.year() << '-' << d.month() << '-' << d.day();
36   return os;
37 }
38 
operator <<(std::wostream & os,const Date & d)39 static std::wostream& operator<<(std::wostream& os, const Date& d) {
40   os << d.year() << L'-' << d.month() << L'-' << d.day();
41   return os;
42 }
43 
44 // Make sure that overloaded comma operators do no harm to is_streamable.
45 struct type_with_comma_op {};
46 template <typename T> void operator,(type_with_comma_op, const T&);
47 template <typename T> type_with_comma_op operator<<(T&, const Date&);
48 
49 enum streamable_enum {};
operator <<(std::ostream & os,streamable_enum)50 static std::ostream& operator<<(std::ostream& os, streamable_enum) {
51   return os << "streamable_enum";
52 }
53 
operator <<(std::wostream & os,streamable_enum)54 static std::wostream& operator<<(std::wostream& os, streamable_enum) {
55   return os << L"streamable_enum";
56 }
57 
58 enum unstreamable_enum {};
59 
TEST(OStreamTest,Enum)60 TEST(OStreamTest, Enum) {
61   EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum()));
62   EXPECT_EQ("0", fmt::format("{}", unstreamable_enum()));
63   EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum()));
64   EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
65 }
66 
67 struct test_arg_formatter
68     : fmt::detail::arg_formatter<fmt::format_context::iterator, char> {
69   fmt::format_parse_context parse_ctx;
test_arg_formattertest_arg_formatter70   test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
71       : fmt::detail::arg_formatter<fmt::format_context::iterator, char>(
72             ctx, &parse_ctx, &s),
73         parse_ctx("") {}
74 };
75 
TEST(OStreamTest,CustomArg)76 TEST(OStreamTest, CustomArg) {
77   fmt::memory_buffer buffer;
78   fmt::format_context ctx(fmt::detail::buffer_appender<char>{buffer},
79                           fmt::format_args());
80   fmt::format_specs spec;
81   test_arg_formatter af(ctx, spec);
82   fmt::visit_format_arg(
83       af, fmt::detail::make_arg<fmt::format_context>(streamable_enum()));
84   EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size()));
85 }
86 
TEST(OStreamTest,Format)87 TEST(OStreamTest, Format) {
88   EXPECT_EQ("a string", format("{0}", TestString("a string")));
89   std::string s = format("The date is {0}", Date(2012, 12, 9));
90   EXPECT_EQ("The date is 2012-12-9", s);
91   Date date(2012, 12, 9);
92   EXPECT_EQ(L"The date is 2012-12-9",
93             format(L"The date is {0}", Date(2012, 12, 9)));
94 }
95 
TEST(OStreamTest,FormatSpecs)96 TEST(OStreamTest, FormatSpecs) {
97   EXPECT_EQ("def  ", format("{0:<5}", TestString("def")));
98   EXPECT_EQ("  def", format("{0:>5}", TestString("def")));
99 #if FMT_DEPRECATED_NUMERIC_ALIGN
100   EXPECT_THROW_MSG(format("{0:=5}", TestString("def")), format_error,
101                    "format specifier requires numeric argument");
102 #endif
103   EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
104   EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
105   EXPECT_THROW_MSG(format("{0:+}", TestString()), format_error,
106                    "format specifier requires numeric argument");
107   EXPECT_THROW_MSG(format("{0:-}", TestString()), format_error,
108                    "format specifier requires numeric argument");
109   EXPECT_THROW_MSG(format("{0: }", TestString()), format_error,
110                    "format specifier requires numeric argument");
111   EXPECT_THROW_MSG(format("{0:#}", TestString()), format_error,
112                    "format specifier requires numeric argument");
113   EXPECT_THROW_MSG(format("{0:05}", TestString()), format_error,
114                    "format specifier requires numeric argument");
115   EXPECT_EQ("test         ", format("{0:13}", TestString("test")));
116   EXPECT_EQ("test         ", format("{0:{1}}", TestString("test"), 13));
117   EXPECT_EQ("te", format("{0:.2}", TestString("test")));
118   EXPECT_EQ("te", format("{0:.{1}}", TestString("test"), 2));
119 }
120 
121 struct EmptyTest {};
operator <<(std::ostream & os,EmptyTest)122 static std::ostream& operator<<(std::ostream& os, EmptyTest) {
123   return os << "";
124 }
125 
TEST(OStreamTest,EmptyCustomOutput)126 TEST(OStreamTest, EmptyCustomOutput) {
127   EXPECT_EQ("", fmt::format("{}", EmptyTest()));
128 }
129 
TEST(OStreamTest,Print)130 TEST(OStreamTest, Print) {
131   std::ostringstream os;
132   fmt::print(os, "Don't {}!", "panic");
133   EXPECT_EQ("Don't panic!", os.str());
134   std::wostringstream wos;
135   fmt::print(wos, L"Don't {}!", L"panic");
136   EXPECT_EQ(L"Don't panic!", wos.str());
137 }
138 
TEST(OStreamTest,WriteToOStream)139 TEST(OStreamTest, WriteToOStream) {
140   std::ostringstream os;
141   fmt::memory_buffer buffer;
142   const char* foo = "foo";
143   buffer.append(foo, foo + std::strlen(foo));
144   fmt::detail::write_buffer(os, buffer);
145   EXPECT_EQ("foo", os.str());
146 }
147 
TEST(OStreamTest,WriteToOStreamMaxSize)148 TEST(OStreamTest, WriteToOStreamMaxSize) {
149   size_t max_size = fmt::detail::max_value<size_t>();
150   std::streamsize max_streamsize = fmt::detail::max_value<std::streamsize>();
151   if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return;
152 
153   struct test_buffer final : fmt::detail::buffer<char> {
154     explicit test_buffer(size_t size)
155       : fmt::detail::buffer<char>(nullptr, size, size) {}
156     void grow(size_t) {}
157   } buffer(max_size);
158 
159   struct mock_streambuf : std::streambuf {
160     MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n));
161     std::streamsize xsputn(const char* s, std::streamsize n) {
162       const void* v = s;
163       return xsputn(v, n);
164     }
165   } streambuf;
166 
167   struct test_ostream : std::ostream {
168     explicit test_ostream(mock_streambuf& buffer) : std::ostream(&buffer) {}
169   } os(streambuf);
170 
171   testing::InSequence sequence;
172   const char* data = nullptr;
173   typedef std::make_unsigned<std::streamsize>::type ustreamsize;
174   ustreamsize size = max_size;
175   do {
176     auto n = std::min(size, fmt::detail::to_unsigned(max_streamsize));
177     EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
178         .WillOnce(testing::Return(max_streamsize));
179     data += n;
180     size -= n;
181   } while (size != 0);
182   fmt::detail::write_buffer(os, buffer);
183 }
184 
TEST(OStreamTest,Join)185 TEST(OStreamTest, Join) {
186   int v[3] = {1, 2, 3};
187   EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", ")));
188 }
189 
190 #if FMT_USE_CONSTEXPR
TEST(OStreamTest,ConstexprString)191 TEST(OStreamTest, ConstexprString) {
192   EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
193   EXPECT_EQ("a string", format(FMT_STRING("{0}"), TestString("a string")));
194 }
195 #endif
196 
197 namespace fmt_test {
198 struct ABC {};
199 
operator <<(Output & out,ABC)200 template <typename Output> Output& operator<<(Output& out, ABC) {
201   out << "ABC";
202   return out;
203 }
204 }  // namespace fmt_test
205 
206 template <typename T> struct TestTemplate {};
207 
208 template <typename T>
operator <<(std::ostream & os,TestTemplate<T>)209 std::ostream& operator<<(std::ostream& os, TestTemplate<T>) {
210   return os << 1;
211 }
212 
213 namespace fmt {
214 template <typename T> struct formatter<TestTemplate<T>> : formatter<int> {
215   template <typename FormatContext>
formatfmt::formatter216   typename FormatContext::iterator format(TestTemplate<T>, FormatContext& ctx) {
217     return formatter<int>::format(2, ctx);
218   }
219 };
220 }  // namespace fmt
221 
222 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 407
TEST(OStreamTest,Template)223 TEST(OStreamTest, Template) {
224   EXPECT_EQ("2", fmt::format("{}", TestTemplate<int>()));
225 }
226 
TEST(FormatTest,FormatToN)227 TEST(FormatTest, FormatToN) {
228   char buffer[4];
229   buffer[3] = 'x';
230   auto result = fmt::format_to_n(buffer, 3, "{}", fmt_test::ABC());
231   EXPECT_EQ(3u, result.size);
232   EXPECT_EQ(buffer + 3, result.out);
233   EXPECT_EQ("ABCx", fmt::string_view(buffer, 4));
234   result = fmt::format_to_n(buffer, 3, "x{}y", fmt_test::ABC());
235   EXPECT_EQ(5u, result.size);
236   EXPECT_EQ(buffer + 3, result.out);
237   EXPECT_EQ("xABx", fmt::string_view(buffer, 4));
238 }
239 #endif
240 
241 #if FMT_USE_USER_DEFINED_LITERALS
TEST(FormatTest,UDL)242 TEST(FormatTest, UDL) {
243   using namespace fmt::literals;
244   EXPECT_EQ("{}"_format("test"), "test");
245 }
246 #endif
247 
248 template <typename T> struct convertible {
249   T value;
convertibleconvertible250   explicit convertible(const T& val) : value(val) {}
operator Tconvertible251   operator T() const { return value; }
252 };
253 
TEST(OStreamTest,DisableBuiltinOStreamOperators)254 TEST(OStreamTest, DisableBuiltinOStreamOperators) {
255   EXPECT_EQ("42", fmt::format("{:d}", convertible<unsigned short>(42)));
256   EXPECT_EQ(L"42", fmt::format(L"{:d}", convertible<unsigned short>(42)));
257   EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
258 }
259 
260 struct explicitly_convertible_to_string_like {
261   template <typename String,
262             typename = typename std::enable_if<std::is_constructible<
263                 String, const char*, size_t>::value>::type>
operator Stringexplicitly_convertible_to_string_like264   explicit operator String() const {
265     return String("foo", 3u);
266   }
267 };
268 
operator <<(std::ostream & os,explicitly_convertible_to_string_like)269 std::ostream& operator<<(std::ostream& os,
270                          explicitly_convertible_to_string_like) {
271   return os << "bar";
272 }
273 
TEST(OStreamTest,FormatExplicitlyConvertibleToStringLike)274 TEST(OStreamTest, FormatExplicitlyConvertibleToStringLike) {
275   EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
276 }
277 
278 #ifdef FMT_USE_STRING_VIEW
279 struct explicitly_convertible_to_std_string_view {
operator fmt::detail::std_string_view<char>explicitly_convertible_to_std_string_view280   explicit operator fmt::detail::std_string_view<char>() const {
281     return {"foo", 3u};
282   }
283 };
284 
operator <<(std::ostream & os,explicitly_convertible_to_std_string_view)285 std::ostream& operator<<(std::ostream& os,
286                          explicitly_convertible_to_std_string_view) {
287   return os << "bar";
288 }
289 
TEST(OStreamTest,FormatExplicitlyConvertibleToStdStringView)290 TEST(OStreamTest, FormatExplicitlyConvertibleToStdStringView) {
291   EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
292 }
293 #endif  // FMT_USE_STRING_VIEW
294 
295 struct streamable_and_convertible_to_bool {
operator boolstreamable_and_convertible_to_bool296   operator bool() const { return true; }
297 };
298 
operator <<(std::ostream & os,streamable_and_convertible_to_bool)299 std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) {
300   return os << "foo";
301 }
302 
TEST(OStreamTest,FormatConvertibleToBool)303 TEST(OStreamTest, FormatConvertibleToBool) {
304   EXPECT_EQ("foo", fmt::format("{}", streamable_and_convertible_to_bool()));
305 }
306 
307 struct copyfmt_test {};
308 
operator <<(std::ostream & os,copyfmt_test)309 std::ostream& operator<<(std::ostream& os, copyfmt_test) {
310   std::ios ios(nullptr);
311   ios.copyfmt(os);
312   return os << "foo";
313 }
314 
TEST(OStreamTest,CopyFmt)315 TEST(OStreamTest, CopyFmt) {
316   EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
317 }
318 
TEST(OStreamTest,CompileTimeString)319 TEST(OStreamTest, CompileTimeString) {
320   EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
321 }
322 
TEST(OStreamTest,ToString)323 TEST(OStreamTest, ToString) {
324   EXPECT_EQ("ABC", fmt::to_string(fmt_test::ABC()));
325 }
326 
TEST(OStreamTest,Range)327 TEST(OStreamTest, Range) {
328   auto strs = std::vector<TestString>{TestString("foo"), TestString("bar")};
329   EXPECT_EQ("{foo, bar}", format("{}", strs));
330 }