Mercurial > minori
comparison dep/fmt/test/ostream-test.cc @ 343:1faa72660932
*: transfer back to cmake from autotools
autotools just made lots of things more complicated than
they should have and many things broke (i.e. translations)
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Thu, 20 Jun 2024 05:56:06 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
342:adb79bdde329 | 343:1faa72660932 |
---|---|
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 <fstream> | |
9 | |
10 #include "fmt/format.h" | |
11 | |
12 using fmt::runtime; | |
13 | |
14 struct test {}; | |
15 | |
16 // Test that there is no issues with specializations when fmt/ostream.h is | |
17 // included after fmt/format.h. | |
18 namespace fmt { | |
19 template <> struct formatter<test> : formatter<int> { | |
20 auto format(const test&, format_context& ctx) const -> decltype(ctx.out()) { | |
21 return formatter<int>::format(42, ctx); | |
22 } | |
23 }; | |
24 } // namespace fmt | |
25 | |
26 #include <sstream> | |
27 | |
28 #include "fmt/compile.h" | |
29 #include "fmt/ostream.h" | |
30 #include "fmt/ranges.h" | |
31 #include "gmock/gmock.h" | |
32 #include "gtest-extra.h" | |
33 #include "util.h" | |
34 | |
35 auto operator<<(std::ostream& os, const date& d) -> std::ostream& { | |
36 os << d.year() << '-' << d.month() << '-' << d.day(); | |
37 return os; | |
38 } | |
39 | |
40 auto operator<<(std::wostream& os, const date& d) -> std::wostream& { | |
41 os << d.year() << L'-' << d.month() << L'-' << d.day(); | |
42 return os; | |
43 } | |
44 | |
45 // Make sure that overloaded comma operators do no harm to is_streamable. | |
46 struct type_with_comma_op {}; | |
47 template <typename T> void operator,(type_with_comma_op, const T&); | |
48 template <typename T> type_with_comma_op operator<<(T&, const date&); | |
49 | |
50 enum streamable_enum {}; | |
51 | |
52 auto operator<<(std::ostream& os, streamable_enum) -> std::ostream& { | |
53 return os << "streamable_enum"; | |
54 } | |
55 | |
56 enum unstreamable_enum {}; | |
57 auto format_as(unstreamable_enum e) -> int { return e; } | |
58 | |
59 struct empty_test {}; | |
60 auto operator<<(std::ostream& os, empty_test) -> std::ostream& { | |
61 return os << ""; | |
62 } | |
63 | |
64 namespace fmt { | |
65 template <> struct formatter<test_string> : ostream_formatter {}; | |
66 template <> struct formatter<date> : ostream_formatter {}; | |
67 template <> struct formatter<streamable_enum> : ostream_formatter {}; | |
68 template <> struct formatter<empty_test> : ostream_formatter {}; | |
69 } // namespace fmt | |
70 | |
71 TEST(ostream_test, enum) { | |
72 EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum())); | |
73 EXPECT_EQ("0", fmt::format("{}", unstreamable_enum())); | |
74 } | |
75 | |
76 TEST(ostream_test, format) { | |
77 EXPECT_EQ("a string", fmt::format("{0}", test_string("a string"))); | |
78 EXPECT_EQ("The date is 2012-12-9", | |
79 fmt::format("The date is {0}", date(2012, 12, 9))); | |
80 } | |
81 | |
82 TEST(ostream_test, format_specs) { | |
83 using fmt::format_error; | |
84 EXPECT_EQ("def ", fmt::format("{0:<5}", test_string("def"))); | |
85 EXPECT_EQ(" def", fmt::format("{0:>5}", test_string("def"))); | |
86 EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def"))); | |
87 EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def"))); | |
88 EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), test_string()), | |
89 format_error, "invalid format specifier"); | |
90 EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), test_string()), | |
91 format_error, "invalid format specifier"); | |
92 EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), test_string()), | |
93 format_error, "invalid format specifier"); | |
94 EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), test_string()), | |
95 format_error, "invalid format specifier"); | |
96 EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), test_string()), | |
97 format_error, "format specifier requires numeric argument"); | |
98 EXPECT_EQ("test ", fmt::format("{0:13}", test_string("test"))); | |
99 EXPECT_EQ("test ", fmt::format("{0:{1}}", test_string("test"), 13)); | |
100 EXPECT_EQ("te", fmt::format("{0:.2}", test_string("test"))); | |
101 EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2)); | |
102 } | |
103 | |
104 TEST(ostream_test, empty_custom_output) { | |
105 EXPECT_EQ("", fmt::format("{}", empty_test())); | |
106 } | |
107 | |
108 TEST(ostream_test, print) { | |
109 { | |
110 std::ostringstream os; | |
111 fmt::print(os, "Don't {}!", "panic"); | |
112 EXPECT_EQ("Don't panic!", os.str()); | |
113 } | |
114 | |
115 { | |
116 std::ostringstream os; | |
117 fmt::println(os, "Don't {}!", "panic"); | |
118 EXPECT_EQ("Don't panic!\n", os.str()); | |
119 } | |
120 } | |
121 | |
122 TEST(ostream_test, write_to_ostream) { | |
123 std::ostringstream os; | |
124 fmt::memory_buffer buffer; | |
125 const char* foo = "foo"; | |
126 buffer.append(foo, foo + std::strlen(foo)); | |
127 fmt::detail::write_buffer(os, buffer); | |
128 EXPECT_EQ("foo", os.str()); | |
129 } | |
130 | |
131 TEST(ostream_test, write_to_ostream_max_size) { | |
132 auto max_size = fmt::detail::max_value<size_t>(); | |
133 auto max_streamsize = fmt::detail::max_value<std::streamsize>(); | |
134 if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return; | |
135 | |
136 struct test_buffer final : fmt::detail::buffer<char> { | |
137 explicit test_buffer(size_t size) | |
138 : fmt::detail::buffer<char>(nullptr, size, size) {} | |
139 void grow(size_t) override {} | |
140 } buffer(max_size); | |
141 | |
142 struct mock_streambuf : std::streambuf { | |
143 MOCK_METHOD(std::streamsize, xsputn, (const void*, std::streamsize)); | |
144 auto xsputn(const char* s, std::streamsize n) -> std::streamsize override { | |
145 const void* v = s; | |
146 return xsputn(v, n); | |
147 } | |
148 } streambuf; | |
149 | |
150 struct test_ostream : std::ostream { | |
151 explicit test_ostream(mock_streambuf& output_buffer) | |
152 : std::ostream(&output_buffer) {} | |
153 } os(streambuf); | |
154 | |
155 testing::InSequence sequence; | |
156 const char* data = nullptr; | |
157 using ustreamsize = std::make_unsigned<std::streamsize>::type; | |
158 ustreamsize size = max_size; | |
159 do { | |
160 auto n = std::min(size, fmt::detail::to_unsigned(max_streamsize)); | |
161 EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n))) | |
162 .WillOnce(testing::Return(max_streamsize)); | |
163 data += n; | |
164 size -= n; | |
165 } while (size != 0); | |
166 fmt::detail::write_buffer(os, buffer); | |
167 } | |
168 | |
169 TEST(ostream_test, join) { | |
170 int v[3] = {1, 2, 3}; | |
171 EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", "))); | |
172 } | |
173 | |
174 TEST(ostream_test, join_fallback_formatter) { | |
175 auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")}; | |
176 EXPECT_EQ("foo, bar", fmt::format("{}", fmt::join(strs, ", "))); | |
177 } | |
178 | |
179 #if FMT_USE_CONSTEXPR | |
180 TEST(ostream_test, constexpr_string) { | |
181 EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), std::string("42"))); | |
182 EXPECT_EQ("a string", | |
183 fmt::format(FMT_STRING("{0}"), test_string("a string"))); | |
184 } | |
185 #endif | |
186 | |
187 namespace fmt_test { | |
188 struct abc {}; | |
189 | |
190 template <typename Output> auto operator<<(Output& out, abc) -> Output& { | |
191 return out << "abc"; | |
192 } | |
193 } // namespace fmt_test | |
194 | |
195 template <typename T> struct test_template {}; | |
196 | |
197 template <typename T> | |
198 auto operator<<(std::ostream& os, test_template<T>) -> std::ostream& { | |
199 return os << 1; | |
200 } | |
201 | |
202 namespace fmt { | |
203 template <typename T> struct formatter<test_template<T>> : formatter<int> { | |
204 auto format(test_template<T>, format_context& ctx) -> decltype(ctx.out()) { | |
205 return formatter<int>::format(2, ctx); | |
206 } | |
207 }; | |
208 | |
209 template <> struct formatter<fmt_test::abc> : ostream_formatter {}; | |
210 } // namespace fmt | |
211 | |
212 TEST(ostream_test, template) { | |
213 EXPECT_EQ("2", fmt::format("{}", test_template<int>())); | |
214 } | |
215 | |
216 TEST(ostream_test, format_to_n) { | |
217 char buffer[4]; | |
218 buffer[3] = 'x'; | |
219 auto result = fmt::format_to_n(buffer, 3, "{}", fmt_test::abc()); | |
220 EXPECT_EQ(3u, result.size); | |
221 EXPECT_EQ(buffer + 3, result.out); | |
222 EXPECT_EQ("abcx", fmt::string_view(buffer, 4)); | |
223 result = fmt::format_to_n(buffer, 3, "x{}y", fmt_test::abc()); | |
224 EXPECT_EQ(5u, result.size); | |
225 EXPECT_EQ(buffer + 3, result.out); | |
226 EXPECT_EQ("xabx", fmt::string_view(buffer, 4)); | |
227 } | |
228 | |
229 struct copyfmt_test {}; | |
230 | |
231 std::ostream& operator<<(std::ostream& os, copyfmt_test) { | |
232 std::ios ios(nullptr); | |
233 ios.copyfmt(os); | |
234 return os << "foo"; | |
235 } | |
236 | |
237 namespace fmt { | |
238 template <> struct formatter<copyfmt_test> : ostream_formatter {}; | |
239 } // namespace fmt | |
240 | |
241 TEST(ostream_test, copyfmt) { | |
242 EXPECT_EQ("foo", fmt::format("{}", copyfmt_test())); | |
243 } | |
244 | |
245 TEST(ostream_test, to_string) { | |
246 EXPECT_EQ("abc", fmt::to_string(fmt_test::abc())); | |
247 } | |
248 | |
249 TEST(ostream_test, range) { | |
250 auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")}; | |
251 EXPECT_EQ("[foo, bar]", fmt::format("{}", strs)); | |
252 } | |
253 | |
254 struct abstract { | |
255 virtual ~abstract() = default; | |
256 virtual void f() = 0; | |
257 friend auto operator<<(std::ostream& os, const abstract&) -> std::ostream& { | |
258 return os; | |
259 } | |
260 }; | |
261 | |
262 namespace fmt { | |
263 template <> struct formatter<abstract> : ostream_formatter {}; | |
264 } // namespace fmt | |
265 | |
266 void format_abstract_compiles(const abstract& a) { | |
267 fmt::format(FMT_COMPILE("{}"), a); | |
268 } | |
269 | |
270 TEST(ostream_test, is_formattable) { | |
271 EXPECT_TRUE(fmt::is_formattable<std::string>()); | |
272 EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>()); | |
273 } | |
274 | |
275 struct streamable_and_unformattable {}; | |
276 | |
277 auto operator<<(std::ostream& os, streamable_and_unformattable) | |
278 -> std::ostream& { | |
279 return os << "foo"; | |
280 } | |
281 | |
282 TEST(ostream_test, streamed) { | |
283 EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>()); | |
284 EXPECT_EQ(fmt::format("{}", fmt::streamed(streamable_and_unformattable())), | |
285 "foo"); | |
286 } | |
287 | |
288 TEST(ostream_test, closed_ofstream) { | |
289 std::ofstream ofs; | |
290 fmt::print(ofs, "discard"); | |
291 } | |
292 | |
293 struct unlocalized {}; | |
294 | |
295 auto operator<<(std::ostream& os, unlocalized) | |
296 -> std::ostream& { | |
297 return os << 12345; | |
298 } | |
299 | |
300 namespace fmt { | |
301 template <> struct formatter<unlocalized> : ostream_formatter {}; | |
302 } // namespace fmt | |
303 | |
304 TEST(ostream_test, unlocalized) { | |
305 auto loc = get_locale("en_US.UTF-8"); | |
306 std::locale::global(loc); | |
307 EXPECT_EQ(fmt::format(loc, "{}", unlocalized()), "12345"); | |
308 } |