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