Mercurial > minori
comparison dep/fmt/test/core-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++ - 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/core.h" | |
13 | |
14 #include <algorithm> // std::copy_n | |
15 #include <climits> // INT_MAX | |
16 #include <cstring> // std::strlen | |
17 #include <functional> // std::equal_to | |
18 #include <iterator> // std::back_insert_iterator | |
19 #include <limits> // std::numeric_limits | |
20 #include <string> // std::string | |
21 #include <type_traits> // std::is_same | |
22 | |
23 #include "gmock/gmock.h" | |
24 | |
25 using fmt::string_view; | |
26 using fmt::detail::buffer; | |
27 | |
28 using testing::_; | |
29 using testing::Invoke; | |
30 using testing::Return; | |
31 | |
32 #ifdef FMT_FORMAT_H_ | |
33 # error core-test includes format.h | |
34 #endif | |
35 | |
36 TEST(string_view_test, value_type) { | |
37 static_assert(std::is_same<string_view::value_type, char>::value, ""); | |
38 } | |
39 | |
40 TEST(string_view_test, ctor) { | |
41 EXPECT_STREQ("abc", fmt::string_view("abc").data()); | |
42 EXPECT_EQ(3u, fmt::string_view("abc").size()); | |
43 | |
44 EXPECT_STREQ("defg", fmt::string_view(std::string("defg")).data()); | |
45 EXPECT_EQ(4u, fmt::string_view(std::string("defg")).size()); | |
46 } | |
47 | |
48 TEST(string_view_test, length) { | |
49 // Test that string_view::size() returns string length, not buffer size. | |
50 char str[100] = "some string"; | |
51 EXPECT_EQ(std::strlen(str), string_view(str).size()); | |
52 EXPECT_LT(std::strlen(str), sizeof(str)); | |
53 } | |
54 | |
55 // Check string_view's comparison operator. | |
56 template <template <typename> class Op> void check_op() { | |
57 const char* inputs[] = {"foo", "fop", "fo"}; | |
58 size_t num_inputs = sizeof(inputs) / sizeof(*inputs); | |
59 for (size_t i = 0; i < num_inputs; ++i) { | |
60 for (size_t j = 0; j < num_inputs; ++j) { | |
61 string_view lhs(inputs[i]), rhs(inputs[j]); | |
62 EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs)); | |
63 } | |
64 } | |
65 } | |
66 | |
67 TEST(string_view_test, compare) { | |
68 EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0); | |
69 EXPECT_GT(string_view("fop").compare(string_view("foo")), 0); | |
70 EXPECT_LT(string_view("foo").compare(string_view("fop")), 0); | |
71 EXPECT_GT(string_view("foo").compare(string_view("fo")), 0); | |
72 EXPECT_LT(string_view("fo").compare(string_view("foo")), 0); | |
73 | |
74 EXPECT_TRUE(string_view("foo").starts_with('f')); | |
75 EXPECT_FALSE(string_view("foo").starts_with('o')); | |
76 EXPECT_FALSE(string_view().starts_with('o')); | |
77 | |
78 EXPECT_TRUE(string_view("foo").starts_with("fo")); | |
79 EXPECT_TRUE(string_view("foo").starts_with("foo")); | |
80 EXPECT_FALSE(string_view("foo").starts_with("fooo")); | |
81 EXPECT_FALSE(string_view().starts_with("fooo")); | |
82 | |
83 check_op<std::equal_to>(); | |
84 check_op<std::not_equal_to>(); | |
85 check_op<std::less>(); | |
86 check_op<std::less_equal>(); | |
87 check_op<std::greater>(); | |
88 check_op<std::greater_equal>(); | |
89 } | |
90 | |
91 TEST(core_test, is_output_iterator) { | |
92 EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value)); | |
93 EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value)); | |
94 EXPECT_FALSE((fmt::detail::is_output_iterator<std::string, char>::value)); | |
95 EXPECT_TRUE( | |
96 (fmt::detail::is_output_iterator<std::back_insert_iterator<std::string>, | |
97 char>::value)); | |
98 EXPECT_TRUE( | |
99 (fmt::detail::is_output_iterator<std::string::iterator, char>::value)); | |
100 EXPECT_FALSE((fmt::detail::is_output_iterator<std::string::const_iterator, | |
101 char>::value)); | |
102 } | |
103 | |
104 TEST(core_test, buffer_appender) { | |
105 // back_insert_iterator is not default-constructible before C++20, so | |
106 // buffer_appender can only be default-constructible when back_insert_iterator | |
107 // is. | |
108 static_assert( | |
109 std::is_default_constructible< | |
110 std::back_insert_iterator<fmt::detail::buffer<char>>>::value == | |
111 std::is_default_constructible< | |
112 fmt::detail::buffer_appender<char>>::value, | |
113 ""); | |
114 | |
115 #ifdef __cpp_lib_ranges | |
116 static_assert(std::output_iterator<fmt::detail::buffer_appender<char>, char>); | |
117 #endif | |
118 } | |
119 | |
120 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470 | |
121 TEST(buffer_test, noncopyable) { | |
122 EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value); | |
123 # if !FMT_MSC_VERSION | |
124 // std::is_copy_assignable is broken in MSVC2013. | |
125 EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value); | |
126 # endif | |
127 } | |
128 | |
129 TEST(buffer_test, nonmoveable) { | |
130 EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value); | |
131 # if !FMT_MSC_VERSION | |
132 // std::is_move_assignable is broken in MSVC2013. | |
133 EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value); | |
134 # endif | |
135 } | |
136 #endif | |
137 | |
138 TEST(buffer_test, indestructible) { | |
139 static_assert(!std::is_destructible<fmt::detail::buffer<int>>(), | |
140 "buffer's destructor is protected"); | |
141 } | |
142 | |
143 template <typename T> struct mock_buffer final : buffer<T> { | |
144 MOCK_METHOD(size_t, do_grow, (size_t)); | |
145 | |
146 void grow(size_t capacity) override { | |
147 this->set(this->data(), do_grow(capacity)); | |
148 } | |
149 | |
150 mock_buffer(T* data = nullptr, size_t buf_capacity = 0) { | |
151 this->set(data, buf_capacity); | |
152 ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) { | |
153 return capacity; | |
154 })); | |
155 } | |
156 }; | |
157 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
269 struct custom_context { | |
270 using char_type = char; | |
271 using parse_context_type = fmt::format_parse_context; | |
272 | |
273 bool called = false; | |
274 | |
275 template <typename T> struct formatter_type { | |
276 FMT_CONSTEXPR auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin()) { | |
277 return ctx.begin(); | |
278 } | |
279 | |
280 const char* format(const T&, custom_context& ctx) { | |
281 ctx.called = true; | |
282 return nullptr; | |
283 } | |
284 }; | |
285 | |
286 void advance_to(const char*) {} | |
287 }; | |
288 | |
289 struct test_struct {}; | |
290 | |
291 FMT_BEGIN_NAMESPACE | |
292 template <typename Char> struct formatter<test_struct, Char> { | |
293 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { | |
294 return ctx.begin(); | |
295 } | |
296 | |
297 auto format(test_struct, format_context& ctx) const -> decltype(ctx.out()) { | |
298 auto test = string_view("test"); | |
299 return std::copy_n(test.data(), test.size(), ctx.out()); | |
300 } | |
301 }; | |
302 FMT_END_NAMESPACE | |
303 | |
304 TEST(arg_test, format_args) { | |
305 auto args = fmt::format_args(); | |
306 EXPECT_FALSE(args.get(1)); | |
307 } | |
308 | |
309 TEST(arg_test, make_value_with_custom_context) { | |
310 auto t = test_struct(); | |
311 auto arg = fmt::detail::value<custom_context>( | |
312 fmt::detail::arg_mapper<custom_context>().map(t)); | |
313 auto ctx = custom_context(); | |
314 auto parse_ctx = fmt::format_parse_context(""); | |
315 arg.custom.format(&t, parse_ctx, ctx); | |
316 EXPECT_TRUE(ctx.called); | |
317 } | |
318 | |
319 // Use a unique result type to make sure that there are no undesirable | |
320 // conversions. | |
321 struct test_result {}; | |
322 | |
323 template <typename T> struct mock_visitor { | |
324 template <typename U> struct result { using type = test_result; }; | |
325 | |
326 mock_visitor() { | |
327 ON_CALL(*this, visit(_)).WillByDefault(Return(test_result())); | |
328 } | |
329 | |
330 MOCK_METHOD(test_result, visit, (T)); | |
331 MOCK_METHOD(void, unexpected, ()); | |
332 | |
333 auto operator()(T value) -> test_result { return visit(value); } | |
334 | |
335 template <typename U> auto operator()(U) -> test_result { | |
336 unexpected(); | |
337 return test_result(); | |
338 } | |
339 }; | |
340 | |
341 template <typename T> struct visit_type { using type = T; }; | |
342 | |
343 #define VISIT_TYPE(type_, visit_type_) \ | |
344 template <> struct visit_type<type_> { using type = visit_type_; } | |
345 | |
346 VISIT_TYPE(signed char, int); | |
347 VISIT_TYPE(unsigned char, unsigned); | |
348 VISIT_TYPE(short, int); | |
349 VISIT_TYPE(unsigned short, unsigned); | |
350 | |
351 #if LONG_MAX == INT_MAX | |
352 VISIT_TYPE(long, int); | |
353 VISIT_TYPE(unsigned long, unsigned); | |
354 #else | |
355 VISIT_TYPE(long, long long); | |
356 VISIT_TYPE(unsigned long, unsigned long long); | |
357 #endif | |
358 | |
359 #define CHECK_ARG(Char, expected, value) \ | |
360 { \ | |
361 testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \ | |
362 EXPECT_CALL(visitor, visit(expected)); \ | |
363 using iterator = std::back_insert_iterator<buffer<Char>>; \ | |
364 auto var = value; \ | |
365 fmt::visit_format_arg( \ | |
366 visitor, \ | |
367 fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>( \ | |
368 var)); \ | |
369 } | |
370 | |
371 #define CHECK_ARG_SIMPLE(value) \ | |
372 { \ | |
373 using value_type = decltype(value); \ | |
374 typename visit_type<value_type>::type expected = value; \ | |
375 CHECK_ARG(char, expected, value) \ | |
376 CHECK_ARG(wchar_t, expected, value) \ | |
377 } | |
378 | |
379 template <typename T> class numeric_arg_test : public testing::Test {}; | |
380 | |
381 using test_types = | |
382 testing::Types<bool, signed char, unsigned char, short, unsigned short, int, | |
383 unsigned, long, unsigned long, long long, unsigned long long, | |
384 float, double, long double>; | |
385 TYPED_TEST_SUITE(numeric_arg_test, test_types); | |
386 | |
387 template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0> | |
388 auto test_value() -> T { | |
389 return static_cast<T>(42); | |
390 } | |
391 | |
392 template <typename T, | |
393 fmt::enable_if_t<std::is_floating_point<T>::value, int> = 0> | |
394 auto test_value() -> T { | |
395 return static_cast<T>(4.2); | |
396 } | |
397 | |
398 TYPED_TEST(numeric_arg_test, make_and_visit) { | |
399 CHECK_ARG_SIMPLE(test_value<TypeParam>()); | |
400 CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::min()); | |
401 CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max()); | |
402 } | |
403 | |
404 TEST(arg_test, char_arg) { CHECK_ARG(char, 'a', 'a'); } | |
405 | |
406 TEST(arg_test, string_arg) { | |
407 char str_data[] = "test"; | |
408 char* str = str_data; | |
409 const char* cstr = str; | |
410 CHECK_ARG(char, cstr, str); | |
411 | |
412 auto sv = fmt::string_view(str); | |
413 CHECK_ARG(char, sv, std::string(str)); | |
414 } | |
415 | |
416 TEST(arg_test, wstring_arg) { | |
417 wchar_t str_data[] = L"test"; | |
418 wchar_t* str = str_data; | |
419 const wchar_t* cstr = str; | |
420 | |
421 auto sv = fmt::basic_string_view<wchar_t>(str); | |
422 CHECK_ARG(wchar_t, cstr, str); | |
423 CHECK_ARG(wchar_t, cstr, cstr); | |
424 CHECK_ARG(wchar_t, sv, std::wstring(str)); | |
425 CHECK_ARG(wchar_t, sv, fmt::basic_string_view<wchar_t>(str)); | |
426 } | |
427 | |
428 TEST(arg_test, pointer_arg) { | |
429 void* p = nullptr; | |
430 const void* cp = nullptr; | |
431 CHECK_ARG(char, cp, p); | |
432 CHECK_ARG(wchar_t, cp, p); | |
433 CHECK_ARG_SIMPLE(cp); | |
434 } | |
435 | |
436 struct check_custom { | |
437 auto operator()(fmt::basic_format_arg<fmt::format_context>::handle h) const | |
438 -> test_result { | |
439 struct test_buffer final : fmt::detail::buffer<char> { | |
440 char data[10]; | |
441 test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {} | |
442 void grow(size_t) override {} | |
443 } buffer; | |
444 auto parse_ctx = fmt::format_parse_context(""); | |
445 auto ctx = fmt::format_context(fmt::detail::buffer_appender<char>(buffer), | |
446 fmt::format_args()); | |
447 h.format(parse_ctx, ctx); | |
448 EXPECT_EQ("test", std::string(buffer.data, buffer.size())); | |
449 return test_result(); | |
450 } | |
451 }; | |
452 | |
453 TEST(arg_test, custom_arg) { | |
454 auto test = test_struct(); | |
455 using visitor = | |
456 mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>; | |
457 auto&& v = testing::StrictMock<visitor>(); | |
458 EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom())); | |
459 fmt::visit_format_arg(v, fmt::detail::make_arg<fmt::format_context>(test)); | |
460 } | |
461 | |
462 TEST(arg_test, visit_invalid_arg) { | |
463 auto&& visitor = testing::StrictMock<mock_visitor<fmt::monostate>>(); | |
464 EXPECT_CALL(visitor, visit(_)); | |
465 auto arg = fmt::basic_format_arg<fmt::format_context>(); | |
466 fmt::visit_format_arg(visitor, arg); | |
467 } | |
468 | |
469 #if FMT_USE_CONSTEXPR | |
470 | |
471 enum class arg_id_result { none, empty, index, name }; | |
472 struct test_arg_id_handler { | |
473 arg_id_result res = arg_id_result::none; | |
474 int index = 0; | |
475 string_view name; | |
476 | |
477 constexpr void on_auto() { res = arg_id_result::empty; } | |
478 | |
479 constexpr void on_index(int i) { | |
480 res = arg_id_result::index; | |
481 index = i; | |
482 } | |
483 | |
484 constexpr void on_name(string_view n) { | |
485 res = arg_id_result::name; | |
486 name = n; | |
487 } | |
488 }; | |
489 | |
490 template <size_t N> | |
491 constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) { | |
492 auto h = test_arg_id_handler(); | |
493 fmt::detail::parse_arg_id(s, s + N, h); | |
494 return h; | |
495 } | |
496 | |
497 TEST(core_test, constexpr_parse_arg_id) { | |
498 static_assert(parse_arg_id(":").res == arg_id_result::empty, ""); | |
499 static_assert(parse_arg_id("}").res == arg_id_result::empty, ""); | |
500 static_assert(parse_arg_id("42:").res == arg_id_result::index, ""); | |
501 static_assert(parse_arg_id("42:").index == 42, ""); | |
502 static_assert(parse_arg_id("foo:").res == arg_id_result::name, ""); | |
503 static_assert(parse_arg_id("foo:").name.size() == 3, ""); | |
504 } | |
505 | |
506 template <size_t N> constexpr auto parse_test_specs(const char (&s)[N]) { | |
507 auto ctx = fmt::detail::compile_parse_context<char>(fmt::string_view(s, N), | |
508 43, nullptr); | |
509 auto specs = fmt::detail::dynamic_format_specs<>(); | |
510 fmt::detail::parse_format_specs(s, s + N - 1, specs, ctx, | |
511 fmt::detail::type::float_type); | |
512 return specs; | |
513 } | |
514 | |
515 TEST(core_test, constexpr_parse_format_specs) { | |
516 static_assert(parse_test_specs("<").align == fmt::align::left, ""); | |
517 static_assert(parse_test_specs("*^").fill[0] == '*', ""); | |
518 static_assert(parse_test_specs("+").sign == fmt::sign::plus, ""); | |
519 static_assert(parse_test_specs("-").sign == fmt::sign::minus, ""); | |
520 static_assert(parse_test_specs(" ").sign == fmt::sign::space, ""); | |
521 static_assert(parse_test_specs("#").alt, ""); | |
522 static_assert(parse_test_specs("0").align == fmt::align::numeric, ""); | |
523 static_assert(parse_test_specs("L").localized, ""); | |
524 static_assert(parse_test_specs("42").width == 42, ""); | |
525 static_assert(parse_test_specs("{42}").width_ref.val.index == 42, ""); | |
526 static_assert(parse_test_specs(".42").precision == 42, ""); | |
527 static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, ""); | |
528 static_assert( | |
529 parse_test_specs("f").type == fmt::presentation_type::fixed_lower, ""); | |
530 } | |
531 | |
532 struct test_format_string_handler { | |
533 constexpr void on_text(const char*, const char*) {} | |
534 | |
535 constexpr auto on_arg_id() -> int { return 0; } | |
536 | |
537 template <typename T> constexpr auto on_arg_id(T) -> int { return 0; } | |
538 | |
539 constexpr void on_replacement_field(int, const char*) {} | |
540 | |
541 constexpr auto on_format_specs(int, const char* begin, const char*) -> const | |
542 char* { | |
543 return begin; | |
544 } | |
545 | |
546 constexpr void on_error(const char*) { error = true; } | |
547 | |
548 bool error = false; | |
549 }; | |
550 | |
551 template <size_t N> constexpr bool parse_string(const char (&s)[N]) { | |
552 auto h = test_format_string_handler(); | |
553 fmt::detail::parse_format_string<true>(fmt::string_view(s, N - 1), h); | |
554 return !h.error; | |
555 } | |
556 | |
557 TEST(core_test, constexpr_parse_format_string) { | |
558 static_assert(parse_string("foo"), ""); | |
559 static_assert(!parse_string("}"), ""); | |
560 static_assert(parse_string("{}"), ""); | |
561 static_assert(parse_string("{42}"), ""); | |
562 static_assert(parse_string("{foo}"), ""); | |
563 static_assert(parse_string("{:}"), ""); | |
564 } | |
565 #endif // FMT_USE_CONSTEXPR | |
566 | |
567 struct enabled_formatter {}; | |
568 struct enabled_ptr_formatter {}; | |
569 struct disabled_formatter {}; | |
570 struct disabled_formatter_convertible { | |
571 operator int() const { return 42; } | |
572 }; | |
573 | |
574 FMT_BEGIN_NAMESPACE | |
575 template <> struct formatter<enabled_formatter> { | |
576 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { | |
577 return ctx.begin(); | |
578 } | |
579 auto format(enabled_formatter, format_context& ctx) const | |
580 -> decltype(ctx.out()) { | |
581 return ctx.out(); | |
582 } | |
583 }; | |
584 | |
585 template <> struct formatter<enabled_ptr_formatter*> { | |
586 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { | |
587 return ctx.begin(); | |
588 } | |
589 auto format(enabled_ptr_formatter*, format_context& ctx) const | |
590 -> decltype(ctx.out()) { | |
591 return ctx.out(); | |
592 } | |
593 }; | |
594 FMT_END_NAMESPACE | |
595 | |
596 TEST(core_test, has_formatter) { | |
597 using fmt::has_formatter; | |
598 using context = fmt::format_context; | |
599 static_assert(has_formatter<enabled_formatter, context>::value, ""); | |
600 static_assert(!has_formatter<disabled_formatter, context>::value, ""); | |
601 static_assert(!has_formatter<disabled_formatter_convertible, context>::value, | |
602 ""); | |
603 } | |
604 | |
605 struct const_formattable {}; | |
606 struct nonconst_formattable {}; | |
607 | |
608 FMT_BEGIN_NAMESPACE | |
609 template <> struct formatter<const_formattable> { | |
610 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { | |
611 return ctx.begin(); | |
612 } | |
613 | |
614 auto format(const const_formattable&, format_context& ctx) | |
615 -> decltype(ctx.out()) { | |
616 auto test = string_view("test"); | |
617 return std::copy_n(test.data(), test.size(), ctx.out()); | |
618 } | |
619 }; | |
620 | |
621 template <> struct formatter<nonconst_formattable> { | |
622 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { | |
623 return ctx.begin(); | |
624 } | |
625 | |
626 auto format(nonconst_formattable&, format_context& ctx) | |
627 -> decltype(ctx.out()) { | |
628 auto test = string_view("test"); | |
629 return std::copy_n(test.data(), test.size(), ctx.out()); | |
630 } | |
631 }; | |
632 FMT_END_NAMESPACE | |
633 | |
634 struct convertible_to_pointer { | |
635 operator const int*() const { return nullptr; } | |
636 }; | |
637 | |
638 struct convertible_to_pointer_formattable { | |
639 operator const int*() const { return nullptr; } | |
640 }; | |
641 | |
642 FMT_BEGIN_NAMESPACE | |
643 template <> struct formatter<convertible_to_pointer_formattable> { | |
644 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { | |
645 return ctx.begin(); | |
646 } | |
647 | |
648 auto format(convertible_to_pointer_formattable, format_context& ctx) const | |
649 -> decltype(ctx.out()) { | |
650 auto test = string_view("test"); | |
651 return std::copy_n(test.data(), test.size(), ctx.out()); | |
652 } | |
653 }; | |
654 FMT_END_NAMESPACE | |
655 | |
656 enum class unformattable_scoped_enum {}; | |
657 | |
658 TEST(core_test, is_formattable) { | |
659 static_assert(!fmt::is_formattable<wchar_t>::value, ""); | |
660 #ifdef __cpp_char8_t | |
661 static_assert(!fmt::is_formattable<char8_t>::value, ""); | |
662 #endif | |
663 static_assert(!fmt::is_formattable<char16_t>::value, ""); | |
664 static_assert(!fmt::is_formattable<char32_t>::value, ""); | |
665 static_assert(!fmt::is_formattable<signed char*>::value, ""); | |
666 static_assert(!fmt::is_formattable<unsigned char*>::value, ""); | |
667 static_assert(!fmt::is_formattable<const signed char*>::value, ""); | |
668 static_assert(!fmt::is_formattable<const unsigned char*>::value, ""); | |
669 static_assert(!fmt::is_formattable<const wchar_t*>::value, ""); | |
670 static_assert(!fmt::is_formattable<const wchar_t[3]>::value, ""); | |
671 static_assert(!fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value, | |
672 ""); | |
673 static_assert(fmt::is_formattable<enabled_formatter>::value, ""); | |
674 static_assert(!fmt::is_formattable<enabled_ptr_formatter*>::value, ""); | |
675 static_assert(!fmt::is_formattable<disabled_formatter>::value, ""); | |
676 static_assert(!fmt::is_formattable<disabled_formatter_convertible>::value, | |
677 ""); | |
678 | |
679 static_assert(fmt::is_formattable<const_formattable&>::value, ""); | |
680 static_assert(fmt::is_formattable<const const_formattable&>::value, ""); | |
681 | |
682 static_assert(fmt::is_formattable<nonconst_formattable&>::value, ""); | |
683 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 | |
684 static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, ""); | |
685 #endif | |
686 | |
687 static_assert(!fmt::is_formattable<convertible_to_pointer>::value, ""); | |
688 const auto f = convertible_to_pointer_formattable(); | |
689 EXPECT_EQ(fmt::format("{}", f), "test"); | |
690 | |
691 static_assert(!fmt::is_formattable<void (*)()>::value, ""); | |
692 | |
693 struct s; | |
694 static_assert(!fmt::is_formattable<int(s::*)>::value, ""); | |
695 static_assert(!fmt::is_formattable<int (s::*)()>::value, ""); | |
696 static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, ""); | |
697 static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, ""); | |
698 } | |
699 | |
700 TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); } | |
701 | |
702 TEST(core_test, format_to) { | |
703 auto s = std::string(); | |
704 fmt::format_to(std::back_inserter(s), "{}", 42); | |
705 EXPECT_EQ(s, "42"); | |
706 } | |
707 | |
708 #ifdef __cpp_lib_byte | |
709 TEST(core_test, format_byte) { | |
710 EXPECT_EQ(fmt::format("{}", std::byte(42)), "42"); | |
711 } | |
712 #endif | |
713 | |
714 struct convertible_to_int { | |
715 operator int() const { return 42; } | |
716 }; | |
717 | |
718 struct convertible_to_cstring { | |
719 operator const char*() const { return "foo"; } | |
720 }; | |
721 | |
722 FMT_BEGIN_NAMESPACE | |
723 template <> struct formatter<convertible_to_int> { | |
724 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { | |
725 return ctx.begin(); | |
726 } | |
727 auto format(convertible_to_int, format_context& ctx) const | |
728 -> decltype(ctx.out()) { | |
729 return std::copy_n("foo", 3, ctx.out()); | |
730 } | |
731 }; | |
732 | |
733 template <> struct formatter<convertible_to_cstring> { | |
734 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { | |
735 return ctx.begin(); | |
736 } | |
737 auto format(convertible_to_cstring, format_context& ctx) const | |
738 -> decltype(ctx.out()) { | |
739 return std::copy_n("bar", 3, ctx.out()); | |
740 } | |
741 }; | |
742 FMT_END_NAMESPACE | |
743 | |
744 TEST(core_test, formatter_overrides_implicit_conversion) { | |
745 EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo"); | |
746 EXPECT_EQ(fmt::format("{}", convertible_to_cstring()), "bar"); | |
747 } | |
748 | |
749 // Test that check is not found by ADL. | |
750 template <typename T> void check(T); | |
751 TEST(core_test, adl_check) { | |
752 EXPECT_EQ(fmt::format("{}", test_struct()), "test"); | |
753 } | |
754 | |
755 struct implicitly_convertible_to_string_view { | |
756 operator fmt::string_view() const { return "foo"; } | |
757 }; | |
758 | |
759 TEST(core_test, no_implicit_conversion_to_string_view) { | |
760 EXPECT_FALSE( | |
761 fmt::is_formattable<implicitly_convertible_to_string_view>::value); | |
762 } | |
763 | |
764 #ifdef FMT_USE_STRING_VIEW | |
765 struct implicitly_convertible_to_std_string_view { | |
766 operator std::string_view() const { return "foo"; } | |
767 }; | |
768 | |
769 TEST(core_test, no_implicit_conversion_to_std_string_view) { | |
770 EXPECT_FALSE( | |
771 fmt::is_formattable<implicitly_convertible_to_std_string_view>::value); | |
772 } | |
773 #endif | |
774 | |
775 // std::is_constructible is broken in MSVC until version 2015. | |
776 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900 | |
777 struct explicitly_convertible_to_string_view { | |
778 explicit operator fmt::string_view() const { return "foo"; } | |
779 }; | |
780 | |
781 TEST(core_test, format_explicitly_convertible_to_string_view) { | |
782 // Types explicitly convertible to string_view are not formattable by | |
783 // default because it may introduce ODR violations. | |
784 static_assert( | |
785 !fmt::is_formattable<explicitly_convertible_to_string_view>::value, ""); | |
786 } | |
787 | |
788 # ifdef FMT_USE_STRING_VIEW | |
789 struct explicitly_convertible_to_std_string_view { | |
790 explicit operator std::string_view() const { return "foo"; } | |
791 }; | |
792 | |
793 TEST(core_test, format_explicitly_convertible_to_std_string_view) { | |
794 // Types explicitly convertible to string_view are not formattable by | |
795 // default because it may introduce ODR violations. | |
796 static_assert( | |
797 !fmt::is_formattable<explicitly_convertible_to_std_string_view>::value, | |
798 ""); | |
799 } | |
800 # endif | |
801 #endif | |
802 | |
803 namespace adl_test { | |
804 template <typename... T> void make_format_args(const T&...) = delete; | |
805 | |
806 struct string : std::string {}; | |
807 } // namespace adl_test | |
808 | |
809 // Test that formatting functions compile when make_format_args is found by ADL. | |
810 TEST(core_test, adl) { | |
811 // Only check compilation and don't run the code to avoid polluting the output | |
812 // and since the output is tested elsewhere. | |
813 if (fmt::detail::const_check(true)) return; | |
814 auto s = adl_test::string(); | |
815 char buf[10]; | |
816 (void)fmt::format("{}", s); | |
817 fmt::format_to(buf, "{}", s); | |
818 fmt::format_to_n(buf, 10, "{}", s); | |
819 (void)fmt::formatted_size("{}", s); | |
820 fmt::print("{}", s); | |
821 fmt::print(stdout, "{}", s); | |
822 } | |
823 | |
824 TEST(core_test, has_const_formatter) { | |
825 EXPECT_TRUE((fmt::detail::has_const_formatter<const_formattable, | |
826 fmt::format_context>())); | |
827 EXPECT_FALSE((fmt::detail::has_const_formatter<nonconst_formattable, | |
828 fmt::format_context>())); | |
829 } | |
830 | |
831 TEST(core_test, format_nonconst) { | |
832 EXPECT_EQ(fmt::format("{}", nonconst_formattable()), "test"); | |
833 } | |
834 | |
835 struct its_a_trap { | |
836 template <typename T> operator T() const { | |
837 auto v = T(); | |
838 v.x = 42; | |
839 return v; | |
840 } | |
841 }; | |
842 | |
843 FMT_BEGIN_NAMESPACE | |
844 template <> struct formatter<its_a_trap> { | |
845 FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { | |
846 return ctx.begin(); | |
847 } | |
848 | |
849 auto format(its_a_trap, format_context& ctx) const -> decltype(ctx.out()) { | |
850 auto s = string_view("42"); | |
851 return std::copy(s.begin(), s.end(), ctx.out()); | |
852 } | |
853 }; | |
854 FMT_END_NAMESPACE | |
855 | |
856 TEST(core_test, trappy_conversion) { | |
857 EXPECT_EQ(fmt::format("{}", its_a_trap()), "42"); | |
858 } |