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 } |
