dynarmic/test/core-test.cc
Merry a7f9129f18 Squashed 'externals/fmt/' changes from b6f4ceaed..c4ee72653
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
2022-07-26 10:51:24 +01:00

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");
}