a7f9129f18
c4ee72653 Update version fa2eb2d2e Bump version 35f72bf21 Bump version d22f00d7e Update changelog 4e8d21560 Update changelog 84eecb656 Prune CI configs 55727e3b2 More compile-time checks 1010b7f14 Update docs 2ac51fc44 Update changelog 831132293 Workaround for Microsoft Visual Studio 2022 Internal compiler error. 115e00e0b Replace __cplusplus with FMT_CPLUSPLUS. 94114b05c New CI: Microsoft Visual Studio 2022. d2a232082 Fix partial specialization problem for filesystem for Visual Studio (#2957) 0c06c81da Deprecated implicit conversion of enums to ints for consistency with scoped enums c12b4c0cf New CI: GCC-8 C++17, Clang-8 C++17. 99bb5b1d1 Fix std::variant, std::filesystem::path tests on GCC-8, Clang-7,8. e29c2bc60 Update docs c65e4286b Update changelog 69c24e47e Update changelog 6a775e956 Add support for 'std::variant' in C++17 (#2941) 51535866d Update docs 3ef5caa9f Update docs dccd3e674 Fix docs 9cb02aaaa Fix UDLs e6d478f8e Update changelog and docs 2d931b149 Add fmt::streamed 0506a5733 Update changelog e8bd2a804 Fix enable_ifs for map formatter (#2944) 7c56e11ec Update changelog 69a20db08 Update changelog and fix an apidoc comment 7a2a97c88 Update changelog 568233889 Fix is_formattable for tuple-like types. (#2940) f0de12844 Remove /source-charset:utf-8 compile option. eaa8efb95 Fix ofstream handling in msvc fb991e9d3 Update changelog 8e47cfd1c fix -Wsign-conversion warning 247187586 Make the tests pass on a CHERI system. b135f1c01 Refactor handling of argument types f61a1e813 Add format_arg_types 48b7e3daf Added a FMT_STRING wrapper for system_error() call. 4bb3af7a6 Improve compile-time checks d02c582b9 Fix 'duplicate symbol' error. b59d8c3a2 Make std::filesystem::path formatter utf-8 compatible. 232e21d51 Add utf-8 test for std::filesystem::path formatter. 864465419 Docs: add comment about empty format context range ba50c19e8 use qualified call to avoid ADL conflict with std::format_to 9d6039595 Fix compilation on ppc64 a2681aabc Debug ppc failure bfc576736 Add support for std.h in Bazel build 798d09bb7 Debug ppc failure 8c7cf5139 Cleanup cdfacb434 Cleanup parse_format_string 926ddd063 Move compile string to detail cb682f36f Move to_string_view to detail 156744ad4 Simplify fmt::runtime d9c7166cf bi_iterator -> base 11316b29a chore: Set permissions for GitHub actions fe6eb792d Cleanup check_format_string 054b1d980 Remove unused include e927149f8 Cleanup macros 1761e2666 Remove FMT_CONSTEXPR_DECL d6b568a6c Cleanup string_view checks c83a5d42b FMT_MSC_VER -> FMT_MSC_VERSION 27cd68c30 Cleanup macros 08be4abb3 Remove FMT_NVCOMPILER_VERSION 661b19254 Remove FMT_HEADER_ONLY_CONSTEXPR20 d1026fa5d Remove extern format_float 7e63b600b Make to_string work with __float128 b2ea212cd Update README.rst c2fcdc54e Move format_float to format.h for __float128 2b9037a19 Move basic_fp to format.h for compile-time formatting 542785ccb Get rid of detail::bits 65dd2ea52 Use write_escaped_string to std::filesystem::path. 9860f67cd Improve xchar support for std formatters. 03b1b2838 Improve std::filesystem::path formatter. 4f9311e68 Fix definition of error_handler::on_error 652fea45a Visual Studio 2022: fmt/format.h(1526,27): warning C4127: conditional expression is constant #2908 1f9eae7e3 Add xchar support for write_escaped_string. 90b68783f Skip cmake targets inclusion if fmt::fmt already exists (#2907) ce246aaf7 Remove deprecated APIs edeb3d809 Remove deprecated APIs 496aff7c3 Remove deprecated APIs f5cdf7cb0 Simplify snprintf_float 440512f08 Remove deprecated APIs 621eb80bb Remove deprecated APIs 5c7d315de Remove locale.h c6324009b Add initial double-double support 147e8ca58 Fix Windows max mix-up (#2903) 6bf039d75 Add std:🧵:id formatter 9730fb015 Fix path formatter f0903ad9d Add a path formatter 8833f386e Merge branch 'master' of github.com:fmtlib/fmt 5ab9d3925 Namespace-qualify format_to to avoid conflict with std::format_to af5644c27 Update README.rst 3e28dc021 VS2022 17.2: C4189: 'zero': local variable is initialized but not referenced #2891 (#2892) f6f920a1a Tweak a comment and apply clang-format ae963e444 Implement constexpr isfinite to avoid producing NaN 358f5a7e5 Make precision computation consistent with width f63afd161 Fixed all clang -Wsigned-enum-bitfield warnings (#2882) 7e4ad4017 Add initial support for double-double ffb5e6a73 Suppress a -Wliteral-range warning on Apple M1 (#2861) 5d804ee7f Fix handling of subnormals in exotic FP 86e27ccb4 Suppress a warning 192f79aaa Fix handling of locale separators in FP formatting 395cf0f03 Fix detection of unformattable pointers fc429d18b Avoid overhead on sensible platforms ce7ecdb7a Replace conditional compilation with SFINAE 8751a03a0 Fix Unicode handling when writing to an ostream c55175a58 Add an issue template a935ac3e6 MSVC CMake generation optimization (#2852) 22d31b31f Add a __float128 test f607e3e97 Add __float128 support 686de5888 Implement 128-bit constant mul in bigint 02eb215f2 Replace uint128_wrapper with uint128_fallback b4dc7a1d3 Add 128-bit operations to bigint ef54f9aa3 Suppress -Wfloat-equal 288c3b928 Remove dead code in ostream.h format_value 96930161f Implement 128-bit operator+= for uint128_fallback b41890c1e Make arg_mapper SFINAE-friendly again e2408f37c Check if formatter is not defined if there is format_as db5b8993a Fix formatting of std::byte via format_as 1c83eaf75 Fix incompatible between Jinja2 >= 3.1 and sphinx 3.3.0 5379063b5 Fixed clang -Wreserved-identifier warings b591fc87d Fixed all clang -Wreserved-id-macro warnings (on macOS at least) 17dda5839 constexpr -> const for portability 7ffe87c0b Fix docs 3c4273dd0 Simplify UDL 36d95c9fc Fix docs 44abd1f48 Update signatures in docs and ostream.h db745986f Workaround broken std::numeric_limits 8271e43e5 Improve __float128 support and use constexpr 3f9b7433a Improve __float128 support 71778e8b9 Specialize float_info for __float128 f024565c3 Improve exponent handling in Dragon e7f31f5cd Cleanup format_dragon 3c61799fb Cleanup fuzzing mode 4e39e1308 Remove xchar.h include from ostream.h ac0d9d5fe Issue #2816: also strip named-arg for the fallback formatter 4ad90578f Fix #2818: diagnose unformattable arguments in unpacked case 17ba99c1d Fix #2817: add compile-time checking to ostream overloads of fmt::print 3d19be282 Fix #2816: strip named argument wrappers for compile-time checking c076a54a4 Move snprintf_float to format.h 0419d2388 Add FMT_USE_FLOAT128 69396347a Update color.h (#2815) c51604a0e Reduce the number of configs 587dc9946 Remove windows-2016 env no longer suppported by GA 1f3d44b85 Update std::tm/chrono docs bc654faf8 Add is_floating_point that works with __float128 26bffce66 Simplify basic_memory_buffer ed18ca3ea Implement isnan a204b8dde Add initial __float128 support b6b003b07 Cleanup test f2543b0a9 Add initial support for 128-bit floats 72f487562 Simplify float_info f91f61cd1 Reuse num_significand_bits 9a1beab57 Workaround Windows API garbage a8fe8becf Fix compilation error for ranges with ADL `begin`/`end` (#2807) f6bcb25e1 Remove extra dot b4a4189d0 Fix handling of implicit bit 32d477e5f Add `styled` in documentation (#2805) 0b7c045a2 Simplify _cf c10fffecd Make _cf visible in the doc build dcfbe4a77 Document output_file default behavior correctly (#2803) 8c9bc070f Implement styled arguments (#2793) 5bc39d363 Eliminate intel compiler warnings (#2802) e3d688e79 Fix warning C4251: class fmt::v8::file needs to have dll-interface (#2797) 8d4f3e91b Update docs 0cef1f819 Fixing formatting of certain kinds of ranges of ranges. (#2787) 5c0d65640 Fix apt install d416a995e Update README.rst 3f67a1247 Update README.rst cc57e3597 Update godbolt link in the readme (#2789) 86477f7ec Fix size computation 0742606f1 Fix Conversion Warning (#2782) 1ba69fb5a Remove snprintf FP fallback ea6f0bf0e Minor cleanup 1a18a2f3d Fixing "C4127: conditional expression is constant" Visual Studio 2022 warning in pedantic mode (#2783) 4fcacea35 Parameterized fp on significand type cf940ae82 Simplify to_decimal 70dc3de05 Update format.h cbc59ca89 Clear moved from memory buffer ea3d326c6 Fix clang -Wliteral-range warning (#2779) aad44f283 Add fmt::enums::format_as 1319719a5 Add underlying_t af5d8004f Limit Dragonbox to supported FP formats 7b9642096 Remove unused include a0b43bfae Add support for 96-bit long double 2c8cd2db3 Fix handling of zero precision b6d56170f Remove unnecessary inline 05432e570 Use consistent indentation 47da218cc Remove uintptr_fallback 4ddab8901 Merge accumulator into int128_fallback d38f72aff Refactor fallback ints 15c2a3bac int128_t -> int128_opt 532a69a63 Fix handling of 96-bit long double with -m32 d8e1dd4ab improve installing headers ae25f7968 add ability to build Apple framework using CMAKE_FRAMEWORK ce93a66df Implement a fallback uint128_t 6a1346405 Include 128-bit with other signed integers in specifier check 70de324aa Apply 2746 fix for NVidia compiler also (#2770) a1ea3e015 Move built-in formatter specialization to core 161059dd9 Add support for extended precision FP c4c6b42de Bump version 21785040c Fix markup 2b6f7fc7a Add partial support for extended precision FP 0a24a0714 Clz builtin may be not constexpr (Issue #2761) (#2762) ba6f89c76 Update .bazelversion (#2766) 5594edaf6 Address https://github.com/fmtlib/fmt/issues/2763 (#2765) 10e3b83a7 Replace ``make_args_checked`` with ``make_format_args`` (#2760) c48353cb7 Update docs 083510f0f Add FMT_CONSTEXPR to rotr instead dba99bc86 Revert adding constexpr to rotr to satisfy C++11 compilers c04af4bfc Simplify remove_trailing_zeros b348caa9e Remove some C-style casts for consistency c8bd1e646 Simplify remove_trailing_zeros 9b23e9dcb Fix wrong comment/refer to a correct reference 69f2c550a Remove std:: infront of uint32_t/64_t & add constexpr to rotr 9b62310f0 Fix some conversion issues 08d12f31d Fix typo dbddb1d06 Remove literal separator to satisfy some compilers 7dbe3dcde Recover log10_2_significand 10642e608 Optimize remove_trailing_zeros 7b4323e1e Add rotr f1bd6f773 Check r < deltai first, because that is the major branch chosen for short inputs 5d8eb6a1a Reflect the new paper - Change constants appearing in log & division computations - Rename beta_minus_1 to beta 8e2e4d403 Suppress a gcc warning a44716f58 Workaround to Intel compiler (#2758) c71b07016 Add missing const qualifier (#2755) ecd6022c2 Update docs afbcf1e8e Remove legacy C locale wrapper 90325d097 Fix stored type detection e2ba01fcb Fix overload ambiguity in print 17b362f78 Simplify ostream opt-in API a5a7e3a26 Update docs f055ebbd2 Make ostream operators opt in to reduce the risk of ODR violations 8a21e328b Remove problematic constructibility check 31e743d06 Don't use ostream for types convertible to string_view 35c0286cd Simplify byte handling c7173a36a Drop :: and fix formatting 3e8372b96 qualify unqualified calls to format in compile.h (#2742) a34a97cc1 Supporting ? as a string presentation type (#2674) ae1aaaee5 Fix access mode of files created (#2530) (#2733) 1557ab764 Add format_as for enums b00a1eac7 Fixes NVIDIA HPC compiler and Intel ICC compatibility (#2732) a7aecbfca Remove an old mingw workaround dfcc730cb Making target_compile_options PRIVATE, fix #2726, fix #2507 f7a809be6 Clarify the choice of magic numbers and compute the most magic one 09fde7f4b Add fmt::underlying for enum classes 0014024a2 Don't rely on transitive includes c28500556 FMT_NOEXCEPT -> noexcept 6240d0201 Improve comments 925b744ae Improve comments 22b14ff25 Simplify cache recovery 3dc26b44d Make a fallback path more compiler-friendly 2e4038bf5 Simplify lines with __builtin_addcll and friends 76336b4f6 Replace noexcept with FMT_NOEXCEPT 918198348 Fix syntax errors 74097a149 Remove now-unused stuffs 21a1c5338 Fix typo 04eea0f0a Remove now-unused stuffs 35a468ed3 Simplify integer checks 1882a7a2c Replace Dragonbox cache which allows simpler cache recovery & integer checks f4dd1b1b8 Simplify Dragonbox Step 3. 70561ed13 Minimize the usage of built-in 128-bit ints It usually generates slower code than manual handling. cdf1a3b53 Fix codecvt warning (#2408) (#2725) b8b037e93 Fix -Wconversion warning (#2724) 5985f0a7d Fix overflow for chrono durations (#2722) 8f8a1a02d Fix handling of formattable types implicitly convertible to pointers b02e5af52 fmt::join support FMT_COMPILE (#2720) 58fb78239 Improve docs 4fe6129d6 Fix FMT_NOEXCEPT definition c056a009d Docs: Fix link to "Compile-time Format String Checks" section (#2712) 7c12118c1 Deprecate buffered_file::fileno 2a09d468d Use noexcept unconditionally a126b4d88 Check if right shift is arithmetic 9ff91b18c Simplify write_fractional_seconds d9f045fba Fix a UB in chrono c06bef727 Adding comments for range formatting. (#2706) 3c98f1a4c Comment style 6e0f1399d Supporting nested format specs for ranges. (#2673) 0102101ac Make colored print handle UTF-8 (#2701) 4ac5269b4 Update ChangeLog.rst git-subtree-dir: externals/fmt git-subtree-split: c4ee726532178e556d923372f29163bd206d7732
975 lines
31 KiB
C++
975 lines
31 KiB
C++
// Formatting library for C++ - core tests
|
|
//
|
|
// Copyright (c) 2012 - present, Victor Zverovich
|
|
// All rights reserved.
|
|
//
|
|
// For the license information refer to format.h.
|
|
|
|
// clang-format off
|
|
#include "test-assert.h"
|
|
// clang-format on
|
|
|
|
#include "fmt/core.h"
|
|
|
|
#include <algorithm> // std::copy_n
|
|
#include <climits> // INT_MAX
|
|
#include <cstring> // std::strlen
|
|
#include <functional> // std::equal_to
|
|
#include <iterator> // std::back_insert_iterator
|
|
#include <limits> // std::numeric_limits
|
|
#include <string> // std::string
|
|
#include <type_traits> // std::is_same
|
|
|
|
#include "gmock/gmock.h"
|
|
|
|
using fmt::string_view;
|
|
using fmt::detail::buffer;
|
|
|
|
using testing::_;
|
|
using testing::Invoke;
|
|
using testing::Return;
|
|
|
|
#ifdef FMT_FORMAT_H_
|
|
# error core-test includes format.h
|
|
#endif
|
|
|
|
TEST(string_view_test, value_type) {
|
|
static_assert(std::is_same<string_view::value_type, char>::value, "");
|
|
}
|
|
|
|
TEST(string_view_test, ctor) {
|
|
EXPECT_STREQ("abc", fmt::string_view("abc").data());
|
|
EXPECT_EQ(3u, fmt::string_view("abc").size());
|
|
|
|
EXPECT_STREQ("defg", fmt::string_view(std::string("defg")).data());
|
|
EXPECT_EQ(4u, fmt::string_view(std::string("defg")).size());
|
|
}
|
|
|
|
TEST(string_view_test, length) {
|
|
// Test that string_view::size() returns string length, not buffer size.
|
|
char str[100] = "some string";
|
|
EXPECT_EQ(std::strlen(str), string_view(str).size());
|
|
EXPECT_LT(std::strlen(str), sizeof(str));
|
|
}
|
|
|
|
// Check string_view's comparison operator.
|
|
template <template <typename> class Op> void check_op() {
|
|
const char* inputs[] = {"foo", "fop", "fo"};
|
|
size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
|
|
for (size_t i = 0; i < num_inputs; ++i) {
|
|
for (size_t j = 0; j < num_inputs; ++j) {
|
|
string_view lhs(inputs[i]), rhs(inputs[j]);
|
|
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(string_view_test, compare) {
|
|
EXPECT_EQ(string_view("foo").compare(string_view("foo")), 0);
|
|
EXPECT_GT(string_view("fop").compare(string_view("foo")), 0);
|
|
EXPECT_LT(string_view("foo").compare(string_view("fop")), 0);
|
|
EXPECT_GT(string_view("foo").compare(string_view("fo")), 0);
|
|
EXPECT_LT(string_view("fo").compare(string_view("foo")), 0);
|
|
check_op<std::equal_to>();
|
|
check_op<std::not_equal_to>();
|
|
check_op<std::less>();
|
|
check_op<std::less_equal>();
|
|
check_op<std::greater>();
|
|
check_op<std::greater_equal>();
|
|
}
|
|
|
|
namespace test_ns {
|
|
template <typename Char> class test_string {
|
|
private:
|
|
std::basic_string<Char> s_;
|
|
|
|
public:
|
|
test_string(const Char* s) : s_(s) {}
|
|
const Char* data() const { return s_.data(); }
|
|
size_t length() const { return s_.size(); }
|
|
operator const Char*() const { return s_.c_str(); }
|
|
};
|
|
|
|
template <typename Char>
|
|
fmt::basic_string_view<Char> to_string_view(const test_string<Char>& s) {
|
|
return {s.data(), s.length()};
|
|
}
|
|
} // namespace test_ns
|
|
|
|
TEST(core_test, is_output_iterator) {
|
|
EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value));
|
|
EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value));
|
|
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string, char>::value));
|
|
EXPECT_TRUE(
|
|
(fmt::detail::is_output_iterator<std::back_insert_iterator<std::string>,
|
|
char>::value));
|
|
EXPECT_TRUE(
|
|
(fmt::detail::is_output_iterator<std::string::iterator, char>::value));
|
|
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string::const_iterator,
|
|
char>::value));
|
|
}
|
|
|
|
TEST(core_test, buffer_appender) {
|
|
// back_insert_iterator is not default-constructible before C++20, so
|
|
// buffer_appender can only be default-constructible when back_insert_iterator
|
|
// is.
|
|
static_assert(
|
|
std::is_default_constructible<
|
|
std::back_insert_iterator<fmt::detail::buffer<char>>>::value ==
|
|
std::is_default_constructible<
|
|
fmt::detail::buffer_appender<char>>::value,
|
|
"");
|
|
|
|
#ifdef __cpp_lib_ranges
|
|
static_assert(std::output_iterator<fmt::detail::buffer_appender<char>, char>);
|
|
#endif
|
|
}
|
|
|
|
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
|
|
TEST(buffer_test, noncopyable) {
|
|
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
|
|
# if !FMT_MSC_VERSION
|
|
// std::is_copy_assignable is broken in MSVC2013.
|
|
EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
|
|
# endif
|
|
}
|
|
|
|
TEST(buffer_test, nonmoveable) {
|
|
EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
|
|
# if !FMT_MSC_VERSION
|
|
// std::is_move_assignable is broken in MSVC2013.
|
|
EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
TEST(buffer_test, indestructible) {
|
|
static_assert(!std::is_destructible<fmt::detail::buffer<int>>(),
|
|
"buffer's destructor is protected");
|
|
}
|
|
|
|
template <typename T> struct mock_buffer final : buffer<T> {
|
|
MOCK_METHOD1(do_grow, size_t(size_t capacity));
|
|
|
|
void grow(size_t capacity) override {
|
|
this->set(this->data(), do_grow(capacity));
|
|
}
|
|
|
|
mock_buffer(T* data = nullptr, size_t buf_capacity = 0) {
|
|
this->set(data, buf_capacity);
|
|
ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) {
|
|
return capacity;
|
|
}));
|
|
}
|
|
};
|
|
|
|
TEST(buffer_test, ctor) {
|
|
{
|
|
mock_buffer<int> buffer;
|
|
EXPECT_EQ(nullptr, buffer.data());
|
|
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
|
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
|
|
}
|
|
{
|
|
int dummy;
|
|
mock_buffer<int> buffer(&dummy);
|
|
EXPECT_EQ(&dummy, &buffer[0]);
|
|
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
|
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
|
|
}
|
|
{
|
|
int dummy;
|
|
size_t capacity = std::numeric_limits<size_t>::max();
|
|
mock_buffer<int> buffer(&dummy, capacity);
|
|
EXPECT_EQ(&dummy, &buffer[0]);
|
|
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
|
EXPECT_EQ(capacity, buffer.capacity());
|
|
}
|
|
}
|
|
|
|
TEST(buffer_test, access) {
|
|
char data[10];
|
|
mock_buffer<char> buffer(data, sizeof(data));
|
|
buffer[0] = 11;
|
|
EXPECT_EQ(11, buffer[0]);
|
|
buffer[3] = 42;
|
|
EXPECT_EQ(42, *(&buffer[0] + 3));
|
|
const fmt::detail::buffer<char>& const_buffer = buffer;
|
|
EXPECT_EQ(42, const_buffer[3]);
|
|
}
|
|
|
|
TEST(buffer_test, try_resize) {
|
|
char data[123];
|
|
mock_buffer<char> buffer(data, sizeof(data));
|
|
buffer[10] = 42;
|
|
EXPECT_EQ(42, buffer[10]);
|
|
buffer.try_resize(20);
|
|
EXPECT_EQ(20u, buffer.size());
|
|
EXPECT_EQ(123u, buffer.capacity());
|
|
EXPECT_EQ(42, buffer[10]);
|
|
buffer.try_resize(5);
|
|
EXPECT_EQ(5u, buffer.size());
|
|
EXPECT_EQ(123u, buffer.capacity());
|
|
EXPECT_EQ(42, buffer[10]);
|
|
// Check if try_resize calls grow.
|
|
EXPECT_CALL(buffer, do_grow(124));
|
|
buffer.try_resize(124);
|
|
EXPECT_CALL(buffer, do_grow(200));
|
|
buffer.try_resize(200);
|
|
}
|
|
|
|
TEST(buffer_test, try_resize_partial) {
|
|
char data[10];
|
|
mock_buffer<char> buffer(data, sizeof(data));
|
|
EXPECT_CALL(buffer, do_grow(20)).WillOnce(Return(15));
|
|
buffer.try_resize(20);
|
|
EXPECT_EQ(buffer.capacity(), 15);
|
|
EXPECT_EQ(buffer.size(), 15);
|
|
}
|
|
|
|
TEST(buffer_test, clear) {
|
|
mock_buffer<char> buffer;
|
|
EXPECT_CALL(buffer, do_grow(20));
|
|
buffer.try_resize(20);
|
|
buffer.try_resize(0);
|
|
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
|
EXPECT_EQ(20u, buffer.capacity());
|
|
}
|
|
|
|
TEST(buffer_test, append) {
|
|
char data[15];
|
|
mock_buffer<char> buffer(data, 10);
|
|
auto test = "test";
|
|
buffer.append(test, test + 5);
|
|
EXPECT_STREQ(test, &buffer[0]);
|
|
EXPECT_EQ(5u, buffer.size());
|
|
buffer.try_resize(10);
|
|
EXPECT_CALL(buffer, do_grow(12));
|
|
buffer.append(test, test + 2);
|
|
EXPECT_EQ('t', buffer[10]);
|
|
EXPECT_EQ('e', buffer[11]);
|
|
EXPECT_EQ(12u, buffer.size());
|
|
}
|
|
|
|
TEST(buffer_test, append_partial) {
|
|
char data[10];
|
|
mock_buffer<char> buffer(data, sizeof(data));
|
|
testing::InSequence seq;
|
|
EXPECT_CALL(buffer, do_grow(15)).WillOnce(Return(10));
|
|
EXPECT_CALL(buffer, do_grow(15)).WillOnce(Invoke([&buffer](size_t) {
|
|
EXPECT_EQ(fmt::string_view(buffer.data(), buffer.size()), "0123456789");
|
|
buffer.clear();
|
|
return 10;
|
|
}));
|
|
auto test = "0123456789abcde";
|
|
buffer.append(test, test + 15);
|
|
}
|
|
|
|
TEST(buffer_test, append_allocates_enough_storage) {
|
|
char data[19];
|
|
mock_buffer<char> buffer(data, 10);
|
|
auto test = "abcdefgh";
|
|
buffer.try_resize(10);
|
|
EXPECT_CALL(buffer, do_grow(19));
|
|
buffer.append(test, test + 9);
|
|
}
|
|
|
|
struct custom_context {
|
|
using char_type = char;
|
|
using parse_context_type = fmt::format_parse_context;
|
|
|
|
bool called = false;
|
|
|
|
template <typename T> struct formatter_type {
|
|
auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin()) {
|
|
return ctx.begin();
|
|
}
|
|
|
|
const char* format(const T&, custom_context& ctx) {
|
|
ctx.called = true;
|
|
return nullptr;
|
|
}
|
|
};
|
|
|
|
void advance_to(const char*) {}
|
|
};
|
|
|
|
struct test_struct {};
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
template <typename Char> struct formatter<test_struct, Char> {
|
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
|
return ctx.begin();
|
|
}
|
|
|
|
auto format(test_struct, format_context& ctx) -> decltype(ctx.out()) {
|
|
auto test = string_view("test");
|
|
return std::copy_n(test.data(), test.size(), ctx.out());
|
|
}
|
|
};
|
|
FMT_END_NAMESPACE
|
|
|
|
TEST(arg_test, format_args) {
|
|
auto args = fmt::format_args();
|
|
EXPECT_FALSE(args.get(1));
|
|
}
|
|
|
|
TEST(arg_test, make_value_with_custom_context) {
|
|
auto t = test_struct();
|
|
fmt::detail::value<custom_context> arg(
|
|
fmt::detail::arg_mapper<custom_context>().map(t));
|
|
auto ctx = custom_context();
|
|
auto parse_ctx = fmt::format_parse_context("");
|
|
arg.custom.format(&t, parse_ctx, ctx);
|
|
EXPECT_TRUE(ctx.called);
|
|
}
|
|
|
|
// Use a unique result type to make sure that there are no undesirable
|
|
// conversions.
|
|
struct test_result {};
|
|
|
|
template <typename T> struct mock_visitor {
|
|
template <typename U> struct result { using type = test_result; };
|
|
|
|
mock_visitor() {
|
|
ON_CALL(*this, visit(_)).WillByDefault(Return(test_result()));
|
|
}
|
|
|
|
MOCK_METHOD1_T(visit, test_result(T value));
|
|
MOCK_METHOD0_T(unexpected, void());
|
|
|
|
test_result operator()(T value) { return visit(value); }
|
|
|
|
template <typename U> test_result operator()(U) {
|
|
unexpected();
|
|
return test_result();
|
|
}
|
|
};
|
|
|
|
template <typename T> struct visit_type { using type = T; };
|
|
|
|
#define VISIT_TYPE(type_, visit_type_) \
|
|
template <> struct visit_type<type_> { using type = visit_type_; }
|
|
|
|
VISIT_TYPE(signed char, int);
|
|
VISIT_TYPE(unsigned char, unsigned);
|
|
VISIT_TYPE(short, int);
|
|
VISIT_TYPE(unsigned short, unsigned);
|
|
|
|
#if LONG_MAX == INT_MAX
|
|
VISIT_TYPE(long, int);
|
|
VISIT_TYPE(unsigned long, unsigned);
|
|
#else
|
|
VISIT_TYPE(long, long long);
|
|
VISIT_TYPE(unsigned long, unsigned long long);
|
|
#endif
|
|
|
|
#define CHECK_ARG(Char, expected, value) \
|
|
{ \
|
|
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
|
|
EXPECT_CALL(visitor, visit(expected)); \
|
|
using iterator = std::back_insert_iterator<buffer<Char>>; \
|
|
fmt::visit_format_arg( \
|
|
visitor, \
|
|
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>( \
|
|
value)); \
|
|
}
|
|
|
|
#define CHECK_ARG_SIMPLE(value) \
|
|
{ \
|
|
using value_type = decltype(value); \
|
|
typename visit_type<value_type>::type expected = value; \
|
|
CHECK_ARG(char, expected, value) \
|
|
CHECK_ARG(wchar_t, expected, value) \
|
|
}
|
|
|
|
template <typename T> class numeric_arg_test : public testing::Test {};
|
|
|
|
using test_types =
|
|
testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
|
|
unsigned, long, unsigned long, long long, unsigned long long,
|
|
float, double, long double>;
|
|
TYPED_TEST_SUITE(numeric_arg_test, test_types);
|
|
|
|
template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
|
|
T test_value() {
|
|
return static_cast<T>(42);
|
|
}
|
|
|
|
template <typename T,
|
|
fmt::enable_if_t<std::is_floating_point<T>::value, int> = 0>
|
|
T test_value() {
|
|
return static_cast<T>(4.2);
|
|
}
|
|
|
|
TYPED_TEST(numeric_arg_test, make_and_visit) {
|
|
CHECK_ARG_SIMPLE(test_value<TypeParam>());
|
|
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::min());
|
|
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
|
|
}
|
|
|
|
TEST(arg_test, char_arg) { CHECK_ARG(char, 'a', 'a'); }
|
|
|
|
TEST(arg_test, string_arg) {
|
|
char str_data[] = "test";
|
|
char* str = str_data;
|
|
const char* cstr = str;
|
|
CHECK_ARG(char, cstr, str);
|
|
|
|
auto sv = fmt::string_view(str);
|
|
CHECK_ARG(char, sv, std::string(str));
|
|
}
|
|
|
|
TEST(arg_test, wstring_arg) {
|
|
wchar_t str_data[] = L"test";
|
|
wchar_t* str = str_data;
|
|
const wchar_t* cstr = str;
|
|
|
|
auto sv = fmt::basic_string_view<wchar_t>(str);
|
|
CHECK_ARG(wchar_t, cstr, str);
|
|
CHECK_ARG(wchar_t, cstr, cstr);
|
|
CHECK_ARG(wchar_t, sv, std::wstring(str));
|
|
CHECK_ARG(wchar_t, sv, fmt::basic_string_view<wchar_t>(str));
|
|
}
|
|
|
|
TEST(arg_test, pointer_arg) {
|
|
void* p = nullptr;
|
|
const void* cp = nullptr;
|
|
CHECK_ARG(char, cp, p);
|
|
CHECK_ARG(wchar_t, cp, p);
|
|
CHECK_ARG_SIMPLE(cp);
|
|
}
|
|
|
|
struct check_custom {
|
|
test_result operator()(
|
|
fmt::basic_format_arg<fmt::format_context>::handle h) const {
|
|
struct test_buffer final : fmt::detail::buffer<char> {
|
|
char data[10];
|
|
test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
|
|
void grow(size_t) override {}
|
|
} buffer;
|
|
auto parse_ctx = fmt::format_parse_context("");
|
|
auto ctx = fmt::format_context(fmt::detail::buffer_appender<char>(buffer),
|
|
fmt::format_args());
|
|
h.format(parse_ctx, ctx);
|
|
EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
|
|
return test_result();
|
|
}
|
|
};
|
|
|
|
TEST(arg_test, custom_arg) {
|
|
auto test = test_struct();
|
|
using visitor =
|
|
mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>;
|
|
testing::StrictMock<visitor> v;
|
|
EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom()));
|
|
fmt::visit_format_arg(v, fmt::detail::make_arg<fmt::format_context>(test));
|
|
}
|
|
|
|
TEST(arg_test, visit_invalid_arg) {
|
|
testing::StrictMock<mock_visitor<fmt::monostate>> visitor;
|
|
EXPECT_CALL(visitor, visit(_));
|
|
auto arg = fmt::basic_format_arg<fmt::format_context>();
|
|
fmt::visit_format_arg(visitor, arg);
|
|
}
|
|
|
|
#if FMT_USE_CONSTEXPR
|
|
|
|
enum class arg_id_result { none, empty, index, name, error };
|
|
struct test_arg_id_handler {
|
|
arg_id_result res = arg_id_result::none;
|
|
int index = 0;
|
|
string_view name;
|
|
|
|
constexpr void operator()() { res = arg_id_result::empty; }
|
|
|
|
constexpr void operator()(int i) {
|
|
res = arg_id_result::index;
|
|
index = i;
|
|
}
|
|
|
|
constexpr void operator()(string_view n) {
|
|
res = arg_id_result::name;
|
|
name = n;
|
|
}
|
|
|
|
constexpr void on_error(const char*) { res = arg_id_result::error; }
|
|
};
|
|
|
|
template <size_t N>
|
|
constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) {
|
|
test_arg_id_handler h;
|
|
fmt::detail::parse_arg_id(s, s + N, h);
|
|
return h;
|
|
}
|
|
|
|
TEST(format_test, constexpr_parse_arg_id) {
|
|
static_assert(parse_arg_id(":").res == arg_id_result::empty, "");
|
|
static_assert(parse_arg_id("}").res == arg_id_result::empty, "");
|
|
static_assert(parse_arg_id("42:").res == arg_id_result::index, "");
|
|
static_assert(parse_arg_id("42:").index == 42, "");
|
|
static_assert(parse_arg_id("foo:").res == arg_id_result::name, "");
|
|
static_assert(parse_arg_id("foo:").name.size() == 3, "");
|
|
static_assert(parse_arg_id("!").res == arg_id_result::error, "");
|
|
}
|
|
|
|
struct test_format_specs_handler {
|
|
enum result { none, hash, zero, loc, error };
|
|
result res = none;
|
|
|
|
fmt::align_t alignment = fmt::align::none;
|
|
fmt::sign_t sign = fmt::sign::none;
|
|
char fill = 0;
|
|
int width = 0;
|
|
fmt::detail::arg_ref<char> width_ref;
|
|
int precision = 0;
|
|
fmt::detail::arg_ref<char> precision_ref;
|
|
fmt::presentation_type type = fmt::presentation_type::none;
|
|
|
|
// Workaround for MSVC2017 bug that results in "expression did not evaluate
|
|
// to a constant" with compiler-generated copy ctor.
|
|
constexpr test_format_specs_handler() {}
|
|
constexpr test_format_specs_handler(const test_format_specs_handler& other) =
|
|
default;
|
|
|
|
constexpr void on_align(fmt::align_t a) { alignment = a; }
|
|
constexpr void on_fill(fmt::string_view f) { fill = f[0]; }
|
|
constexpr void on_sign(fmt::sign_t s) { sign = s; }
|
|
constexpr void on_hash() { res = hash; }
|
|
constexpr void on_zero() { res = zero; }
|
|
constexpr void on_localized() { res = loc; }
|
|
|
|
constexpr void on_width(int w) { width = w; }
|
|
constexpr void on_dynamic_width(fmt::detail::auto_id) {}
|
|
constexpr void on_dynamic_width(int index) { width_ref = index; }
|
|
constexpr void on_dynamic_width(string_view) {}
|
|
|
|
constexpr void on_precision(int p) { precision = p; }
|
|
constexpr void on_dynamic_precision(fmt::detail::auto_id) {}
|
|
constexpr void on_dynamic_precision(int index) { precision_ref = index; }
|
|
constexpr void on_dynamic_precision(string_view) {}
|
|
|
|
constexpr void end_precision() {}
|
|
constexpr void on_type(fmt::presentation_type t) { type = t; }
|
|
constexpr void on_error(const char*) { res = error; }
|
|
};
|
|
|
|
template <size_t N>
|
|
constexpr test_format_specs_handler parse_test_specs(const char (&s)[N]) {
|
|
auto h = test_format_specs_handler();
|
|
fmt::detail::parse_format_specs(s, s + N - 1, h);
|
|
return h;
|
|
}
|
|
|
|
TEST(core_test, constexpr_parse_format_specs) {
|
|
using handler = test_format_specs_handler;
|
|
static_assert(parse_test_specs("<").alignment == fmt::align::left, "");
|
|
static_assert(parse_test_specs("*^").fill == '*', "");
|
|
static_assert(parse_test_specs("+").sign == fmt::sign::plus, "");
|
|
static_assert(parse_test_specs("-").sign == fmt::sign::minus, "");
|
|
static_assert(parse_test_specs(" ").sign == fmt::sign::space, "");
|
|
static_assert(parse_test_specs("#").res == handler::hash, "");
|
|
static_assert(parse_test_specs("0").res == handler::zero, "");
|
|
static_assert(parse_test_specs("L").res == handler::loc, "");
|
|
static_assert(parse_test_specs("42").width == 42, "");
|
|
static_assert(parse_test_specs("{42}").width_ref.val.index == 42, "");
|
|
static_assert(parse_test_specs(".42").precision == 42, "");
|
|
static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, "");
|
|
static_assert(parse_test_specs("d").type == fmt::presentation_type::dec, "");
|
|
static_assert(parse_test_specs("{<").res == handler::error, "");
|
|
}
|
|
|
|
struct test_parse_context {
|
|
using char_type = char;
|
|
|
|
constexpr int next_arg_id() { return 11; }
|
|
template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {}
|
|
|
|
constexpr const char* begin() { return nullptr; }
|
|
constexpr const char* end() { return nullptr; }
|
|
|
|
void on_error(const char*) {}
|
|
};
|
|
|
|
template <size_t N>
|
|
constexpr fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
|
|
const char (&s)[N]) {
|
|
auto specs = fmt::detail::dynamic_format_specs<char>();
|
|
auto ctx = test_parse_context();
|
|
auto h = fmt::detail::dynamic_specs_handler<test_parse_context>(specs, ctx);
|
|
parse_format_specs(s, s + N - 1, h);
|
|
return specs;
|
|
}
|
|
|
|
TEST(format_test, constexpr_dynamic_specs_handler) {
|
|
static_assert(parse_dynamic_specs("<").align == fmt::align::left, "");
|
|
static_assert(parse_dynamic_specs("*^").fill[0] == '*', "");
|
|
static_assert(parse_dynamic_specs("+").sign == fmt::sign::plus, "");
|
|
static_assert(parse_dynamic_specs("-").sign == fmt::sign::minus, "");
|
|
static_assert(parse_dynamic_specs(" ").sign == fmt::sign::space, "");
|
|
static_assert(parse_dynamic_specs("#").alt, "");
|
|
static_assert(parse_dynamic_specs("0").align == fmt::align::numeric, "");
|
|
static_assert(parse_dynamic_specs("42").width == 42, "");
|
|
static_assert(parse_dynamic_specs("{}").width_ref.val.index == 11, "");
|
|
static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, "");
|
|
static_assert(parse_dynamic_specs(".42").precision == 42, "");
|
|
static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 11, "");
|
|
static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, "");
|
|
static_assert(parse_dynamic_specs("d").type == fmt::presentation_type::dec,
|
|
"");
|
|
}
|
|
|
|
template <size_t N>
|
|
constexpr test_format_specs_handler check_specs(const char (&s)[N]) {
|
|
fmt::detail::specs_checker<test_format_specs_handler> checker(
|
|
test_format_specs_handler(), fmt::detail::type::double_type);
|
|
parse_format_specs(s, s + N - 1, checker);
|
|
return checker;
|
|
}
|
|
|
|
TEST(format_test, constexpr_specs_checker) {
|
|
using handler = test_format_specs_handler;
|
|
static_assert(check_specs("<").alignment == fmt::align::left, "");
|
|
static_assert(check_specs("*^").fill == '*', "");
|
|
static_assert(check_specs("+").sign == fmt::sign::plus, "");
|
|
static_assert(check_specs("-").sign == fmt::sign::minus, "");
|
|
static_assert(check_specs(" ").sign == fmt::sign::space, "");
|
|
static_assert(check_specs("#").res == handler::hash, "");
|
|
static_assert(check_specs("0").res == handler::zero, "");
|
|
static_assert(check_specs("42").width == 42, "");
|
|
static_assert(check_specs("{42}").width_ref.val.index == 42, "");
|
|
static_assert(check_specs(".42").precision == 42, "");
|
|
static_assert(check_specs(".{42}").precision_ref.val.index == 42, "");
|
|
static_assert(check_specs("d").type == fmt::presentation_type::dec, "");
|
|
static_assert(check_specs("{<").res == handler::error, "");
|
|
}
|
|
|
|
struct test_format_string_handler {
|
|
constexpr void on_text(const char*, const char*) {}
|
|
|
|
constexpr int on_arg_id() { return 0; }
|
|
|
|
template <typename T> constexpr int on_arg_id(T) { return 0; }
|
|
|
|
constexpr void on_replacement_field(int, const char*) {}
|
|
|
|
constexpr const char* on_format_specs(int, const char* begin, const char*) {
|
|
return begin;
|
|
}
|
|
|
|
constexpr void on_error(const char*) { error = true; }
|
|
|
|
bool error = false;
|
|
};
|
|
|
|
template <size_t N> constexpr bool parse_string(const char (&s)[N]) {
|
|
auto h = test_format_string_handler();
|
|
fmt::detail::parse_format_string<true>(fmt::string_view(s, N - 1), h);
|
|
return !h.error;
|
|
}
|
|
|
|
TEST(format_test, constexpr_parse_format_string) {
|
|
static_assert(parse_string("foo"), "");
|
|
static_assert(!parse_string("}"), "");
|
|
static_assert(parse_string("{}"), "");
|
|
static_assert(parse_string("{42}"), "");
|
|
static_assert(parse_string("{foo}"), "");
|
|
static_assert(parse_string("{:}"), "");
|
|
}
|
|
#endif // FMT_USE_CONSTEXPR
|
|
|
|
struct enabled_formatter {};
|
|
struct enabled_ptr_formatter {};
|
|
struct disabled_formatter {};
|
|
struct disabled_formatter_convertible {
|
|
operator int() const { return 42; }
|
|
};
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
template <> struct formatter<enabled_formatter> {
|
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
|
return ctx.begin();
|
|
}
|
|
auto format(enabled_formatter, format_context& ctx) -> decltype(ctx.out()) {
|
|
return ctx.out();
|
|
}
|
|
};
|
|
|
|
template <> struct formatter<enabled_ptr_formatter*> {
|
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
|
return ctx.begin();
|
|
}
|
|
auto format(enabled_ptr_formatter*, format_context& ctx)
|
|
-> decltype(ctx.out()) {
|
|
return ctx.out();
|
|
}
|
|
};
|
|
FMT_END_NAMESPACE
|
|
|
|
TEST(core_test, has_formatter) {
|
|
using fmt::has_formatter;
|
|
using context = fmt::format_context;
|
|
static_assert(has_formatter<enabled_formatter, context>::value, "");
|
|
static_assert(!has_formatter<disabled_formatter, context>::value, "");
|
|
static_assert(!has_formatter<disabled_formatter_convertible, context>::value,
|
|
"");
|
|
}
|
|
|
|
struct const_formattable {};
|
|
struct nonconst_formattable {};
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
template <> struct formatter<const_formattable> {
|
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
|
return ctx.begin();
|
|
}
|
|
|
|
auto format(const const_formattable&, format_context& ctx)
|
|
-> decltype(ctx.out()) {
|
|
auto test = string_view("test");
|
|
return std::copy_n(test.data(), test.size(), ctx.out());
|
|
}
|
|
};
|
|
|
|
template <> struct formatter<nonconst_formattable> {
|
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
|
return ctx.begin();
|
|
}
|
|
|
|
auto format(nonconst_formattable&, format_context& ctx)
|
|
-> decltype(ctx.out()) {
|
|
auto test = string_view("test");
|
|
return std::copy_n(test.data(), test.size(), ctx.out());
|
|
}
|
|
};
|
|
FMT_END_NAMESPACE
|
|
|
|
struct convertible_to_pointer {
|
|
operator const int*() const { return nullptr; }
|
|
};
|
|
|
|
struct convertible_to_pointer_formattable {
|
|
operator const int*() const { return nullptr; }
|
|
};
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
template <> struct formatter<convertible_to_pointer_formattable> {
|
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
|
return ctx.begin();
|
|
}
|
|
|
|
auto format(convertible_to_pointer_formattable, format_context& ctx) const
|
|
-> decltype(ctx.out()) {
|
|
auto test = string_view("test");
|
|
return std::copy_n(test.data(), test.size(), ctx.out());
|
|
}
|
|
};
|
|
FMT_END_NAMESPACE
|
|
|
|
enum class unformattable_scoped_enum {};
|
|
|
|
namespace test {
|
|
enum class formattable_scoped_enum {};
|
|
auto format_as(formattable_scoped_enum) -> int { return 42; }
|
|
|
|
struct convertible_to_enum {
|
|
operator formattable_scoped_enum() const { return {}; }
|
|
};
|
|
} // namespace test
|
|
|
|
TEST(core_test, is_formattable) {
|
|
#if 0
|
|
// This should be enabled once corresponding map overloads are gone.
|
|
static_assert(fmt::is_formattable<signed char*>::value, "");
|
|
static_assert(fmt::is_formattable<unsigned char*>::value, "");
|
|
static_assert(fmt::is_formattable<const signed char*>::value, "");
|
|
static_assert(fmt::is_formattable<const unsigned char*>::value, "");
|
|
#endif
|
|
static_assert(!fmt::is_formattable<wchar_t>::value, "");
|
|
#ifdef __cpp_char8_t
|
|
static_assert(!fmt::is_formattable<char8_t>::value, "");
|
|
#endif
|
|
static_assert(!fmt::is_formattable<char16_t>::value, "");
|
|
static_assert(!fmt::is_formattable<char32_t>::value, "");
|
|
static_assert(!fmt::is_formattable<const wchar_t*>::value, "");
|
|
static_assert(!fmt::is_formattable<const wchar_t[3]>::value, "");
|
|
static_assert(!fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value,
|
|
"");
|
|
static_assert(fmt::is_formattable<enabled_formatter>::value, "");
|
|
static_assert(!fmt::is_formattable<enabled_ptr_formatter*>::value, "");
|
|
static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
|
|
static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
|
|
|
|
static_assert(fmt::is_formattable<const_formattable&>::value, "");
|
|
static_assert(fmt::is_formattable<const const_formattable&>::value, "");
|
|
|
|
static_assert(fmt::is_formattable<nonconst_formattable&>::value, "");
|
|
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
|
static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, "");
|
|
#endif
|
|
|
|
static_assert(!fmt::is_formattable<convertible_to_pointer>::value, "");
|
|
const auto f = convertible_to_pointer_formattable();
|
|
EXPECT_EQ(fmt::format("{}", f), "test");
|
|
|
|
static_assert(!fmt::is_formattable<void (*)()>::value, "");
|
|
|
|
struct s;
|
|
static_assert(!fmt::is_formattable<int(s::*)>::value, "");
|
|
static_assert(!fmt::is_formattable<int (s::*)()>::value, "");
|
|
static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, "");
|
|
static_assert(fmt::is_formattable<test::formattable_scoped_enum>::value, "");
|
|
static_assert(!fmt::is_formattable<test::convertible_to_enum>::value, "");
|
|
}
|
|
|
|
TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); }
|
|
|
|
TEST(core_test, format_to) {
|
|
std::string s;
|
|
fmt::format_to(std::back_inserter(s), "{}", 42);
|
|
EXPECT_EQ(s, "42");
|
|
}
|
|
|
|
TEST(core_test, format_as) {
|
|
EXPECT_EQ(fmt::format("{}", test::formattable_scoped_enum()), "42");
|
|
}
|
|
|
|
struct convertible_to_int {
|
|
operator int() const { return 42; }
|
|
};
|
|
|
|
struct convertible_to_c_string {
|
|
operator const char*() const { return "foo"; }
|
|
};
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
template <> struct formatter<convertible_to_int> {
|
|
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
|
return ctx.begin();
|
|
}
|
|
auto format(convertible_to_int, format_context& ctx) -> decltype(ctx.out()) {
|
|
return std::copy_n("foo", 3, ctx.out());
|
|
}
|
|
};
|
|
|
|
template <> struct formatter<convertible_to_c_string> {
|
|
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
|
return ctx.begin();
|
|
}
|
|
auto format(convertible_to_c_string, format_context& ctx)
|
|
-> decltype(ctx.out()) {
|
|
return std::copy_n("bar", 3, ctx.out());
|
|
}
|
|
};
|
|
FMT_END_NAMESPACE
|
|
|
|
TEST(core_test, formatter_overrides_implicit_conversion) {
|
|
EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo");
|
|
EXPECT_EQ(fmt::format("{}", convertible_to_c_string()), "bar");
|
|
}
|
|
|
|
// Test that check is not found by ADL.
|
|
template <typename T> void check(T);
|
|
TEST(core_test, adl_check) {
|
|
EXPECT_EQ(fmt::format("{}", test_struct()), "test");
|
|
}
|
|
|
|
TEST(core_test, to_string_view_foreign_strings) {
|
|
using namespace test_ns;
|
|
EXPECT_EQ(to_string_view(test_string<char>("42")), "42");
|
|
fmt::detail::type type =
|
|
fmt::detail::mapped_type_constant<test_string<char>,
|
|
fmt::format_context>::value;
|
|
EXPECT_EQ(type, fmt::detail::type::string_type);
|
|
}
|
|
|
|
struct implicitly_convertible_to_string {
|
|
operator std::string() const { return "foo"; }
|
|
};
|
|
|
|
struct implicitly_convertible_to_string_view {
|
|
operator fmt::string_view() const { return "foo"; }
|
|
};
|
|
|
|
TEST(core_test, format_implicitly_convertible_to_string_view) {
|
|
EXPECT_EQ("foo", fmt::format("{}", implicitly_convertible_to_string_view()));
|
|
}
|
|
|
|
// std::is_constructible is broken in MSVC until version 2015.
|
|
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900
|
|
struct explicitly_convertible_to_string_view {
|
|
explicit operator fmt::string_view() const { return "foo"; }
|
|
};
|
|
|
|
TEST(core_test, format_explicitly_convertible_to_string_view) {
|
|
// Types explicitly convertible to string_view are not formattable by
|
|
// default because it may introduce ODR violations.
|
|
static_assert(
|
|
!fmt::is_formattable<explicitly_convertible_to_string_view>::value, "");
|
|
}
|
|
|
|
# ifdef FMT_USE_STRING_VIEW
|
|
struct explicitly_convertible_to_std_string_view {
|
|
explicit operator std::string_view() const { return "foo"; }
|
|
};
|
|
|
|
TEST(core_test, format_explicitly_convertible_to_std_string_view) {
|
|
// Types explicitly convertible to string_view are not formattable by
|
|
// default because it may introduce ODR violations.
|
|
static_assert(
|
|
!fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
|
|
"");
|
|
}
|
|
# endif
|
|
#endif
|
|
|
|
struct convertible_to_long_long {
|
|
operator long long() const { return 1LL << 32; }
|
|
};
|
|
|
|
TEST(format_test, format_convertible_to_long_long) {
|
|
EXPECT_EQ("100000000", fmt::format("{:x}", convertible_to_long_long()));
|
|
}
|
|
|
|
struct disabled_rvalue_conversion {
|
|
operator const char*() const& { return "foo"; }
|
|
operator const char*() & { return "foo"; }
|
|
operator const char*() const&& = delete;
|
|
operator const char*() && = delete;
|
|
};
|
|
|
|
TEST(core_test, disabled_rvalue_conversion) {
|
|
EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
|
|
}
|
|
|
|
namespace adl_test {
|
|
template <typename... T> void make_format_args(const T&...) = delete;
|
|
|
|
struct string : std::string {};
|
|
} // namespace adl_test
|
|
|
|
// Test that formatting functions compile when make_format_args is found by ADL.
|
|
TEST(core_test, adl) {
|
|
// Only check compilation and don't run the code to avoid polluting the output
|
|
// and since the output is tested elsewhere.
|
|
if (fmt::detail::const_check(true)) return;
|
|
auto s = adl_test::string();
|
|
char buf[10];
|
|
(void)fmt::format("{}", s);
|
|
fmt::format_to(buf, "{}", s);
|
|
fmt::format_to_n(buf, 10, "{}", s);
|
|
(void)fmt::formatted_size("{}", s);
|
|
fmt::print("{}", s);
|
|
fmt::print(stdout, "{}", s);
|
|
}
|
|
|
|
TEST(core_test, has_const_formatter) {
|
|
EXPECT_TRUE((fmt::detail::has_const_formatter<const_formattable,
|
|
fmt::format_context>()));
|
|
EXPECT_FALSE((fmt::detail::has_const_formatter<nonconst_formattable,
|
|
fmt::format_context>()));
|
|
}
|
|
|
|
TEST(core_test, format_nonconst) {
|
|
EXPECT_EQ(fmt::format("{}", nonconst_formattable()), "test");
|
|
}
|