Merge commit '2abfcd02f1868bc12b5b0934101d2845c41e3cf5' into dev

This commit is contained in:
SimoneN64
2024-09-22 15:18:54 +02:00
166 changed files with 14259 additions and 22061 deletions

View File

@@ -8,22 +8,6 @@ target_include_directories(test-main PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
target_link_libraries(test-main gtest fmt)
function(add_fmt_executable name)
add_executable(${name} ${ARGN})
# (Wstringop-overflow) - [meta-bug] bogus/missing -Wstringop-overflow warnings
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88443
# Bogus -Wstringop-overflow warning
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100395
# [10 Regression] spurious -Wstringop-overflow writing to a trailing array plus offset
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95353
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND
NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
target_compile_options(${name} PRIVATE -Wno-stringop-overflow)
# The linker flag is needed for LTO.
target_link_libraries(${name} -Wno-stringop-overflow)
endif ()
endfunction()
# Adds a test.
# Usage: add_fmt_test(name srcs...)
function(add_fmt_test name)
@@ -42,9 +26,13 @@ function(add_fmt_test name)
else ()
set(libs test-main fmt)
endif ()
add_fmt_executable(${name} ${sources})
add_executable(${name} ${sources})
target_link_libraries(${name} ${libs})
if (ADD_FMT_TEST_HEADER_ONLY AND NOT FMT_UNICODE)
target_compile_definitions(${name} PUBLIC FMT_UNICODE=0)
endif ()
# Define if certain C++ features can be used.
if (FMT_PEDANTIC)
target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS})
@@ -106,6 +94,9 @@ add_fmt_test(enforce-checks-test)
target_compile_definitions(enforce-checks-test PRIVATE
-DFMT_ENFORCE_COMPILE_STRING)
add_executable(perf-sanity perf-sanity.cc)
target_link_libraries(perf-sanity fmt::fmt)
if (FMT_MODULE)
# The tests need {fmt} to be compiled as traditional library
# because of visibility of implementation details.
@@ -138,7 +129,7 @@ if (NOT DEFINED MSVC_STATIC_RUNTIME AND MSVC)
endif()
if (NOT MSVC_STATIC_RUNTIME)
add_fmt_executable(posix-mock-test
add_executable(posix-mock-test
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
target_include_directories(
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
@@ -146,6 +137,9 @@ if (NOT MSVC_STATIC_RUNTIME)
if (FMT_PEDANTIC)
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
if (MSVC)
target_compile_options(posix-mock-test PRIVATE /utf-8)
endif ()
add_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(os-test)
endif ()
@@ -226,7 +220,7 @@ if (FMT_PEDANTIC AND NOT WIN32 AND NOT (
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif ()
# This test are disabled on Windows because it is only *NIX issue.
# This test is disabled on Windows because it is POSIX-specific.
if (FMT_PEDANTIC AND NOT WIN32)
add_test(static-export-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE}

View File

@@ -92,7 +92,11 @@ TEST(string_view_test, compare) {
check_op<std::greater_equal>();
}
TEST(core_test, is_output_iterator) {
TEST(base_test, is_locking) {
EXPECT_FALSE(fmt::detail::is_locking<const char(&)[3]>());
}
TEST(base_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));
@@ -105,19 +109,13 @@ TEST(core_test, is_output_iterator) {
char>::value));
}
TEST(core_test, is_back_insert_iterator) {
TEST(base_test, is_back_insert_iterator) {
EXPECT_TRUE(fmt::detail::is_back_insert_iterator<
std::back_insert_iterator<std::string>>::value);
EXPECT_FALSE(fmt::detail::is_back_insert_iterator<
std::front_insert_iterator<std::string>>::value);
}
TEST(core_test, buffer_appender) {
#ifdef __cpp_lib_ranges
static_assert(std::output_iterator<fmt::appender, char>);
#endif
}
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(buffer_test, noncopyable) {
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
@@ -268,26 +266,15 @@ TEST(buffer_test, append_allocates_enough_storage) {
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 {
FMT_CONSTEXPR auto parse(fmt::format_parse_context& ctx)
-> decltype(ctx.begin()) {
return ctx.begin();
}
const char* format(const T&, custom_context& ctx) const {
ctx.called = true;
return nullptr;
}
};
void advance_to(const char*) {}
};
TEST(base_test, get_buffer) {
mock_buffer<char> buffer;
void* buffer_ptr = &buffer;
auto&& appender_result = fmt::detail::get_buffer<char>(fmt::appender(buffer));
EXPECT_EQ(&appender_result, buffer_ptr);
auto&& back_inserter_result =
fmt::detail::get_buffer<char>(std::back_inserter(buffer));
EXPECT_EQ(&back_inserter_result, buffer_ptr);
}
struct test_struct {};
@@ -308,16 +295,6 @@ TEST(arg_test, format_args) {
EXPECT_FALSE(args.get(1));
}
TEST(arg_test, make_value_with_custom_context) {
auto t = test_struct();
auto arg = fmt::detail::value<custom_context>(
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 {};
@@ -364,29 +341,35 @@ 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 = fmt::basic_appender<Char>; \
auto var = value; \
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>(var) \
.visit(visitor); \
}
#if FMT_BUILTIN_TYPES
# define CHECK_ARG(expected, value) \
{ \
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
EXPECT_CALL(visitor, visit(expected)); \
auto var = value; \
fmt::basic_format_arg<fmt::format_context>(var).visit(visitor); \
}
#else
# define CHECK_ARG(expected, value)
#endif
#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(expected, value) \
}
template <typename T> class numeric_arg_test : public testing::Test {};
#if FMT_BUILTIN_TYPES
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>;
#else
using test_types = testing::Types<int>;
#endif
TYPED_TEST_SUITE(numeric_arg_test, test_types);
template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
@@ -406,25 +389,33 @@ TYPED_TEST(numeric_arg_test, make_and_visit) {
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
}
TEST(arg_test, char_arg) { CHECK_ARG(char, 'a', 'a'); }
TEST(arg_test, char_arg) { CHECK_ARG('a', 'a'); }
TEST(arg_test, string_arg) {
char str_data[] = "test";
char* str = str_data;
const char* cstr = str;
CHECK_ARG(char, cstr, str);
CHECK_ARG(cstr, str);
auto sv = fmt::string_view(str);
CHECK_ARG(char, sv, std::string(str));
CHECK_ARG(sv, std::string(str));
}
TEST(arg_test, pointer_arg) {
void* p = nullptr;
const void* cp = nullptr;
CHECK_ARG(char, cp, p);
CHECK_ARG(cp, p);
CHECK_ARG_SIMPLE(cp);
}
TEST(arg_test, volatile_pointer_arg) {
const void* p = nullptr;
volatile int* vip = nullptr;
const volatile int* cvip = nullptr;
CHECK_ARG(p, static_cast<volatile void*>(vip));
CHECK_ARG(p, static_cast<const volatile void*>(cvip));
}
struct check_custom {
auto operator()(fmt::basic_format_arg<fmt::format_context>::handle h) const
-> test_result {
@@ -448,7 +439,7 @@ TEST(arg_test, custom_arg) {
mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>;
auto&& v = testing::StrictMock<visitor>();
EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom()));
fmt::detail::make_arg<fmt::format_context>(test).visit(v);
fmt::basic_format_arg<fmt::format_context>(test).visit(v);
}
TEST(arg_test, visit_invalid_arg) {
@@ -459,14 +450,12 @@ TEST(arg_test, visit_invalid_arg) {
#if FMT_USE_CONSTEXPR
enum class arg_id_result { none, empty, index, name };
enum class arg_id_result { none, index, name };
struct test_arg_id_handler {
arg_id_result res = arg_id_result::none;
int index = 0;
string_view name;
constexpr void on_auto() { res = arg_id_result::empty; }
constexpr void on_index(int i) {
res = arg_id_result::index;
index = i;
@@ -485,9 +474,7 @@ constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) {
return h;
}
TEST(core_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, "");
TEST(base_test, constexpr_parse_arg_id) {
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, "");
@@ -503,21 +490,21 @@ template <size_t N> constexpr auto parse_test_specs(const char (&s)[N]) {
return specs;
}
TEST(core_test, constexpr_parse_format_specs) {
static_assert(parse_test_specs("<").align == fmt::align::left, "");
static_assert(parse_test_specs("*^").fill.get<char>() == '*', "");
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("#").alt, "");
static_assert(parse_test_specs("0").align == fmt::align::numeric, "");
static_assert(parse_test_specs("L").localized, "");
TEST(base_test, constexpr_parse_format_specs) {
static_assert(parse_test_specs("<").align() == fmt::align::left, "");
static_assert(parse_test_specs("*^").fill_unit<char>() == '*', "");
static_assert(parse_test_specs("+").sign() == fmt::sign::plus, "");
static_assert(parse_test_specs("-").sign() == fmt::sign::none, "");
static_assert(parse_test_specs(" ").sign() == fmt::sign::space, "");
static_assert(parse_test_specs("#").alt(), "");
static_assert(parse_test_specs("0").align() == fmt::align::numeric, "");
static_assert(parse_test_specs("L").localized(), "");
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}").width_ref.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("f").type == fmt::presentation_type::fixed, "");
static_assert(parse_test_specs(".{42}").precision_ref.index == 42, "");
static_assert(parse_test_specs("f").type() == fmt::presentation_type::fixed,
"");
}
struct test_format_string_handler {
@@ -541,11 +528,11 @@ struct test_format_string_handler {
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);
fmt::detail::parse_format_string(fmt::string_view(s, N - 1), h);
return !h.error;
}
TEST(core_test, constexpr_parse_format_string) {
TEST(base_test, constexpr_parse_format_string) {
static_assert(parse_string("foo"), "");
static_assert(!parse_string("}"), "");
static_assert(parse_string("{}"), "");
@@ -584,15 +571,6 @@ template <> struct formatter<enabled_ptr_formatter*> {
};
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 {};
@@ -643,67 +621,83 @@ FMT_END_NAMESPACE
enum class unformattable_scoped_enum {};
TEST(core_test, is_formattable) {
static_assert(!fmt::is_formattable<wchar_t>::value, "");
TEST(base_test, is_formattable) {
EXPECT_FALSE(fmt::is_formattable<void>::value);
EXPECT_FALSE(fmt::is_formattable<wchar_t>::value);
#ifdef __cpp_char8_t
static_assert(!fmt::is_formattable<char8_t>::value, "");
EXPECT_FALSE(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<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, "");
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,
"");
EXPECT_FALSE(fmt::is_formattable<char16_t>::value);
EXPECT_FALSE(fmt::is_formattable<char32_t>::value);
EXPECT_FALSE(fmt::is_formattable<signed char*>::value);
EXPECT_FALSE(fmt::is_formattable<unsigned char*>::value);
EXPECT_FALSE(fmt::is_formattable<const signed char*>::value);
EXPECT_FALSE(fmt::is_formattable<const unsigned char*>::value);
EXPECT_FALSE(fmt::is_formattable<const wchar_t*>::value);
EXPECT_FALSE(fmt::is_formattable<const wchar_t[3]>::value);
EXPECT_FALSE(fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value);
EXPECT_FALSE(fmt::is_formattable<enabled_ptr_formatter*>::value);
EXPECT_FALSE(fmt::is_formattable<disabled_formatter>::value);
EXPECT_FALSE(fmt::is_formattable<disabled_formatter_convertible>::value);
static_assert(fmt::is_formattable<const_formattable&>::value, "");
static_assert(fmt::is_formattable<const const_formattable&>::value, "");
EXPECT_TRUE(fmt::is_formattable<enabled_formatter>::value);
EXPECT_TRUE(fmt::is_formattable<const_formattable&>::value);
EXPECT_TRUE(fmt::is_formattable<const const_formattable&>::value);
static_assert(fmt::is_formattable<nonconst_formattable&>::value, "");
EXPECT_TRUE(fmt::is_formattable<nonconst_formattable&>::value);
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, "");
EXPECT_FALSE(fmt::is_formattable<const nonconst_formattable&>::value);
#endif
static_assert(!fmt::is_formattable<convertible_to_pointer>::value, "");
EXPECT_FALSE(fmt::is_formattable<convertible_to_pointer>::value);
const auto f = convertible_to_pointer_formattable();
auto str = std::string();
fmt::format_to(std::back_inserter(str), "{}", f);
EXPECT_EQ(str, "test");
static_assert(!fmt::is_formattable<void (*)()>::value, "");
EXPECT_FALSE(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<unformattable_scoped_enum>::value, "");
EXPECT_FALSE(fmt::is_formattable<int(s::*)>::value);
EXPECT_FALSE(fmt::is_formattable<int (s::*)()>::value);
EXPECT_FALSE(fmt::is_formattable<unformattable_scoped_enum>::value);
EXPECT_FALSE(fmt::is_formattable<unformattable_scoped_enum>::value);
}
TEST(core_test, format_to) {
#ifdef __cpp_concepts
TEST(base_test, formattable_concept) {
static_assert(fmt::formattable<char>);
static_assert(fmt::formattable<char&>);
static_assert(fmt::formattable<char&&>);
static_assert(fmt::formattable<const char>);
static_assert(fmt::formattable<const char&>);
static_assert(fmt::formattable<const char&&>);
static_assert(fmt::formattable<int>);
static_assert(!fmt::formattable<wchar_t>);
}
#endif
TEST(base_test, format_to) {
auto s = std::string();
fmt::format_to(std::back_inserter(s), "{}", 42);
EXPECT_EQ(s, "42");
}
TEST(core_test, format_to_c_array) {
TEST(base_test, format_to_array) {
char buffer[4];
auto result = fmt::format_to(buffer, "{}", 12345);
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
EXPECT_EQ(0, std::distance(result.out, result.out_last));
EXPECT_TRUE(result.truncated);
EXPECT_EQ(buffer + 4, result.out);
EXPECT_EQ("1234", fmt::string_view(buffer, 4));
char* out = nullptr;
EXPECT_THROW(out = result, std::runtime_error);
(void)out;
result = fmt::format_to(buffer, "{:s}", "foobar");
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
EXPECT_EQ(0, std::distance(result.out, result.out_last));
EXPECT_TRUE(result.truncated);
EXPECT_EQ(buffer + 4, result.out);
EXPECT_EQ("foob", fmt::string_view(buffer, 4));
@@ -713,38 +707,30 @@ TEST(core_test, format_to_c_array) {
buffer[3] = 'x';
result = fmt::format_to(buffer, "{}", 'A');
EXPECT_EQ(1, std::distance(&buffer[0], result.out));
EXPECT_EQ(3, std::distance(result.out, result.out_last));
EXPECT_FALSE(result.truncated);
EXPECT_EQ(buffer + 1, result.out);
EXPECT_EQ("Axxx", fmt::string_view(buffer, 4));
result = fmt::format_to(buffer, "{}{} ", 'B', 'C');
EXPECT_EQ(3, std::distance(&buffer[0], result.out));
EXPECT_EQ(1, std::distance(result.out, result.out_last));
EXPECT_FALSE(result.truncated);
EXPECT_EQ(buffer + 3, result.out);
EXPECT_EQ("BC x", fmt::string_view(buffer, 4));
result = fmt::format_to(buffer, "{}", "ABCDE");
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
EXPECT_EQ(0, std::distance(result.out, result.out_last));
EXPECT_TRUE(result.truncated);
EXPECT_EQ("ABCD", fmt::string_view(buffer, 4));
result = fmt::format_to(buffer, "{}", std::string(1000, '*'));
result = fmt::format_to(buffer, "{}", std::string(1000, '*').c_str());
EXPECT_EQ(4, std::distance(&buffer[0], result.out));
EXPECT_EQ(0, std::distance(result.out, result.out_last));
EXPECT_TRUE(result.truncated);
EXPECT_EQ("****", fmt::string_view(buffer, 4));
}
#ifdef __cpp_lib_byte
TEST(core_test, format_byte) {
auto s = std::string();
fmt::format_to(std::back_inserter(s), "{}", std::byte(42));
EXPECT_EQ(s, "42");
}
#endif
// Test that check is not found by ADL.
template <typename T> void check(T);
TEST(core_test, adl_check) {
TEST(base_test, adl_check) {
auto s = std::string();
fmt::format_to(std::back_inserter(s), "{}", test_struct());
EXPECT_EQ(s, "test");
@@ -754,7 +740,7 @@ struct implicitly_convertible_to_string_view {
operator fmt::string_view() const { return "foo"; }
};
TEST(core_test, no_implicit_conversion_to_string_view) {
TEST(base_test, no_implicit_conversion_to_string_view) {
EXPECT_FALSE(
fmt::is_formattable<implicitly_convertible_to_string_view>::value);
}
@@ -764,7 +750,7 @@ struct implicitly_convertible_to_std_string_view {
operator std::string_view() const { return "foo"; }
};
TEST(core_test, no_implicit_conversion_to_std_string_view) {
TEST(base_test, no_implicit_conversion_to_std_string_view) {
EXPECT_FALSE(
fmt::is_formattable<implicitly_convertible_to_std_string_view>::value);
}
@@ -776,7 +762,7 @@ struct explicitly_convertible_to_string_view {
explicit operator fmt::string_view() const { return "foo"; }
};
TEST(core_test, format_explicitly_convertible_to_string_view) {
TEST(base_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(
@@ -788,7 +774,7 @@ 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) {
TEST(base_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(
@@ -798,20 +784,19 @@ TEST(core_test, format_explicitly_convertible_to_std_string_view) {
# endif
#endif
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(base_test, has_formatter) {
EXPECT_TRUE((fmt::detail::has_formatter<const const_formattable, char>()));
EXPECT_FALSE(
(fmt::detail::has_formatter<const nonconst_formattable, char>()));
}
TEST(core_test, format_nonconst) {
TEST(base_test, format_nonconst) {
auto s = std::string();
fmt::format_to(std::back_inserter(s), "{}", nonconst_formattable());
EXPECT_EQ(s, "test");
}
TEST(core_test, throw_in_buffer_dtor) {
TEST(base_test, throw_in_buffer_dtor) {
enum { buffer_size = 256 };
struct throwing_iterator {
@@ -857,8 +842,29 @@ template <> struct formatter<its_a_trap> {
};
FMT_END_NAMESPACE
TEST(format_test, trappy_conversion) {
TEST(base_test, trappy_conversion) {
auto s = std::string();
fmt::format_to(std::back_inserter(s), "{}", its_a_trap());
EXPECT_EQ(s, "x");
}
struct custom_container {
char data;
using value_type = char;
size_t size() const { return 0; }
void resize(size_t) {}
void push_back(char) {}
char& operator[](size_t) { return data; }
};
FMT_BEGIN_NAMESPACE
template <> struct is_contiguous<custom_container> : std::true_type {};
FMT_END_NAMESPACE
TEST(base_test, format_to_custom_container) {
auto c = custom_container();
fmt::format_to(std::back_inserter(c), "");
}

View File

@@ -539,6 +539,7 @@ TEST(chrono_test, format_specs) {
EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(24)), "12");
EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(4)), "04");
EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(14)), "02");
EXPECT_EQ(fmt::format("{:%j}", days(12)), "12");
EXPECT_EQ(fmt::format("{:%j}", days(12345)), "12345");
EXPECT_EQ(fmt::format("{:%j}", std::chrono::hours(12345 * 24 + 12)), "12345");
EXPECT_EQ(fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)),
@@ -751,17 +752,21 @@ TEST(chrono_test, unsigned_duration) {
TEST(chrono_test, weekday) {
auto loc = get_locale("es_ES.UTF-8");
std::locale::global(loc);
auto sat = fmt::weekday(6);
auto tm = std::tm();
tm.tm_wday = static_cast<int>(sat.c_encoding());
EXPECT_EQ(fmt::format("{}", sat), "Sat");
EXPECT_EQ(fmt::format("{:%a}", sat), "Sat");
EXPECT_EQ(fmt::format("{:%A}", sat), "Saturday");
EXPECT_EQ(fmt::format("{:%a}", tm), "Sat");
if (loc != std::locale::classic()) {
auto saturdays = std::vector<std::string>{"sáb", "sá."};
auto saturdays = std::vector<std::string>{"sáb", "sá.", "sáb."};
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:L}", sat)));
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:%a}", sat)));
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:%a}", tm)));
}
}
@@ -791,6 +796,14 @@ TEST(chrono_test, cpp20_duration_subsecond_support) {
"01.234000");
EXPECT_EQ(fmt::format("{:.6%S}", std::chrono::milliseconds{-1234}),
"-01.234000");
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{12345}), "12.34");
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{12375}), "12.37");
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{-12375}),
"-12.37");
EXPECT_EQ(fmt::format("{:.0%S}", std::chrono::milliseconds{12054}), "12");
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{99999}), "39.99");
EXPECT_EQ(fmt::format("{:.2%S}", std::chrono::milliseconds{1000}), "01.00");
EXPECT_EQ(fmt::format("{:.3%S}", std::chrono::milliseconds{1}), "00.001");
EXPECT_EQ(fmt::format("{:.3%S}", std::chrono::seconds{1234}), "34.000");
EXPECT_EQ(fmt::format("{:.3%S}", std::chrono::hours{1234}), "00.000");
EXPECT_EQ(fmt::format("{:.5%S}", dms(1.234)), "00.00123");
@@ -953,13 +966,11 @@ TEST(chrono_test, glibc_extensions) {
std::chrono::seconds(3);
EXPECT_EQ(fmt::format("{:%I,%H,%M,%S}", d), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%0I,%0H,%0M,%0S}", d), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%_I,%_H,%_M,%_S}", d), " 1, 1, 2, 3");
EXPECT_EQ(fmt::format("{:%-I,%-H,%-M,%-S}", d), "1,1,2,3");
EXPECT_EQ(fmt::format("{:%-I,%H,%M,%S}", d), "1,01,02,03");
EXPECT_EQ(fmt::format("{:%OI,%OH,%OM,%OS}", d), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%0OI,%0OH,%0OM,%0OS}", d), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%_OI,%_OH,%_OM,%_OS}", d), " 1, 1, 2, 3");
EXPECT_EQ(fmt::format("{:%-OI,%-OH,%-OM,%-OS}", d), "1,1,2,3");
}
@@ -967,12 +978,10 @@ TEST(chrono_test, glibc_extensions) {
{
const auto tm = make_tm(1970, 1, 1, 1, 2, 3);
EXPECT_EQ(fmt::format("{:%I,%H,%M,%S}", tm), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%0I,%0H,%0M,%0S}", tm), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%_I,%_H,%_M,%_S}", tm), " 1, 1, 2, 3");
EXPECT_EQ(fmt::format("{:%-I,%-H,%-M,%-S}", tm), "1,1,2,3");
EXPECT_EQ(fmt::format("{:%OI,%OH,%OM,%OS}", tm), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%0OI,%0OH,%0OM,%0OS}", tm), "01,01,02,03");
EXPECT_EQ(fmt::format("{:%_OI,%_OH,%_OM,%_OS}", tm), " 1, 1, 2, 3");
EXPECT_EQ(fmt::format("{:%-OI,%-OH,%-OM,%-OS}", tm), "1,1,2,3");
}
@@ -980,21 +989,100 @@ TEST(chrono_test, glibc_extensions) {
{
const auto d = std::chrono::seconds(3) + std::chrono::milliseconds(140);
EXPECT_EQ(fmt::format("{:%S}", d), "03.140");
EXPECT_EQ(fmt::format("{:%0S}", d), "03.140");
EXPECT_EQ(fmt::format("{:%_S}", d), " 3.140");
EXPECT_EQ(fmt::format("{:%-S}", d), "3.140");
}
{
const auto d = std::chrono::duration<double>(3.14);
auto d = std::chrono::duration<double>(3.14);
EXPECT_EQ(fmt::format("{:%S}", d), "03.140000");
EXPECT_EQ(fmt::format("{:%0S}", d), "03.140000");
EXPECT_EQ(fmt::format("{:%_S}", d), " 3.140000");
EXPECT_EQ(fmt::format("{:%-S}", d), "3.140000");
}
{
auto t = std::tm();
t.tm_yday = 7;
EXPECT_EQ(fmt::format("{:%U,%W,%V}", t), "02,01,01");
EXPECT_EQ(fmt::format("{:%_U,%_W,%_V}", t), " 2, 1, 1");
EXPECT_EQ(fmt::format("{:%-U,%-W,%-V}", t), "2,1,1");
EXPECT_EQ(fmt::format("{:%j}", t), "008");
EXPECT_EQ(fmt::format("{:%_j}", t), " 8");
EXPECT_EQ(fmt::format("{:%-j}", t), "8");
}
{
auto t = std::tm();
t.tm_mday = 7;
EXPECT_EQ(fmt::format("{:%d}", t), "07");
EXPECT_EQ(fmt::format("{:%_d}", t), " 7");
EXPECT_EQ(fmt::format("{:%-d}", t), "7");
EXPECT_EQ(fmt::format("{:%e}", t), " 7");
}
{
auto t = std::tm();
t.tm_year = 7 - 1900;
EXPECT_EQ(fmt::format("{:%Y}", t), "0007");
EXPECT_EQ(fmt::format("{:%_Y}", t), " 7");
EXPECT_EQ(fmt::format("{:%-Y}", t), "7");
}
{
auto t = std::tm();
t.tm_year = -5 - 1900;
EXPECT_EQ(fmt::format( "{:%Y}", t), "-005");
EXPECT_EQ(fmt::format("{:%_Y}", t), " -5");
EXPECT_EQ(fmt::format("{:%-Y}", t), "-5");
}
{
auto t = std::tm();
t.tm_mon = 7 - 1;
EXPECT_EQ(fmt::format("{:%m}", t), "07");
EXPECT_EQ(fmt::format("{:%_m}", t), " 7");
EXPECT_EQ(fmt::format("{:%-m}", t), "7");
}
}
TEST(chrono_test, out_of_range) {
auto d = std::chrono::duration<unsigned long, std::giga>(538976288);
EXPECT_THROW((void)fmt::format("{:%j}", d), fmt::format_error);
}
}
TEST(chrono_test, year_month_day) {
auto loc = get_locale("es_ES.UTF-8");
std::locale::global(loc);
auto year = fmt::year(2024);
auto month = fmt::month(1);
auto day = fmt::day(1);
auto ymd = fmt::year_month_day(year, month, day);
EXPECT_EQ(fmt::format("{}", year), "2024");
EXPECT_EQ(fmt::format("{:%Y}", year), "2024");
EXPECT_EQ(fmt::format("{:%y}", year), "24");
EXPECT_EQ(fmt::format("{}", month), "Jan");
EXPECT_EQ(fmt::format("{:%m}", month), "01");
EXPECT_EQ(fmt::format("{:%b}", month), "Jan");
EXPECT_EQ(fmt::format("{:%B}", month), "January");
EXPECT_EQ(fmt::format("{}", day), "01");
EXPECT_EQ(fmt::format("{:%d}", day), "01");
EXPECT_EQ(fmt::format("{}", ymd), "2024-01-01");
EXPECT_EQ(fmt::format("{:%Y-%m-%d}", ymd), "2024-01-01");
EXPECT_EQ(fmt::format("{:%Y-%b-%d}", ymd), "2024-Jan-01");
EXPECT_EQ(fmt::format("{:%Y-%B-%d}", ymd), "2024-January-01");
if (loc != std::locale::classic()) {
auto months = std::vector<std::string>{"ene.", "ene"};
EXPECT_THAT(months, Contains(fmt::format(loc, "{:L}", month)));
EXPECT_THAT(months, Contains(fmt::format(loc, "{:%b}", month)));
}
}

View File

@@ -115,8 +115,7 @@ function (run_tests)
endif ()
endfunction ()
# check if the source file skeleton compiles
# Check if the source file skeleton compiles.
expect_compile(check "")
expect_compile(check-error "compilation_error" ERROR)
@@ -166,31 +165,6 @@ expect_compile(format-lots-of-arguments-with-function "
fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, f);
" ERROR)
# Check if user-defined literals are available
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
check_cxx_source_compiles("
void operator\"\" _udl(long double);
int main() {}"
SUPPORTS_USER_DEFINED_LITERALS)
set(CMAKE_REQUIRED_FLAGS )
if (NOT SUPPORTS_USER_DEFINED_LITERALS)
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
endif ()
# Make sure that compiler features detected in the header
# match the features detected in CMake.
if (SUPPORTS_USER_DEFINED_LITERALS)
set(supports_udl 1)
else ()
set(supports_udl 0)
endif ()
expect_compile(udl-check "
#if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl}
# error
#endif
")
if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
# Compile-time argument type check
expect_compile(format-string-number-spec "

View File

@@ -14,14 +14,6 @@
#include "gmock/gmock.h"
#include "gtest-extra.h"
TEST(iterator_test, counting_iterator) {
auto it = fmt::detail::counting_iterator();
auto prev = it++;
EXPECT_EQ(prev.count(), 0);
EXPECT_EQ(it.count(), 1);
EXPECT_EQ((it + 41).count(), 42);
}
TEST(compile_test, compile_fallback) {
// FMT_COMPILE should fallback on runtime formatting when `if constexpr` is
// not available.
@@ -206,7 +198,7 @@ TEST(compile_test, format_to_n) {
EXPECT_STREQ("2a", buffer);
}
# ifdef __cpp_lib_bit_cast
# if FMT_USE_CONSTEVAL && (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940)
TEST(compile_test, constexpr_formatted_size) {
FMT_CONSTEXPR20 size_t size = fmt::formatted_size(FMT_COMPILE("{}"), 42);
EXPECT_EQ(size, 2);
@@ -342,6 +334,7 @@ TEST(compile_time_formatting_test, integer) {
EXPECT_EQ("0X4A", test_format<5>(FMT_COMPILE("{:#X}"), 0x4a));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42l));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42ll));
EXPECT_EQ(" 42", test_format<6>(FMT_COMPILE("{:5}"), 42ull));

View File

@@ -344,7 +344,7 @@ TEST(format_impl_test, write_dragon_even) {
if (!FMT_MSC_VERSION) EXPECT_EQ(s, "33554450");
}
#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR)
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
# include <windows.h>
TEST(format_impl_test, write_console_signature) {

View File

@@ -15,17 +15,28 @@
#include <stdint.h> // uint32_t
#include <climits> // INT_MAX
#include <cmath> // std::signbit
#include <cstring> // std::strlen
#include <iterator> // std::back_inserter
#include <list> // std::list
#include <type_traits> // std::is_default_constructible
#include <cfenv> // fegetexceptflag and FE_ALL_EXCEPT
#include <climits> // INT_MAX
#include <cmath> // std::signbit
#include <condition_variable> // std::condition_variable
#include <cstring> // std::strlen
#include <iterator> // std::back_inserter
#include <list> // std::list
#include <mutex> // std::mutex
#include <string> // std::string
#include <thread> // std::thread
#include <type_traits> // std::is_default_constructible
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
#include <limits.h>
#include <limits>
#include "gtest-extra.h"
#include "mock-allocator.h"
#include "util.h"
using fmt::basic_memory_buffer;
using fmt::format_error;
using fmt::memory_buffer;
@@ -37,6 +48,10 @@ using fmt::detail::uint128_fallback;
using testing::Return;
using testing::StrictMock;
#ifdef __cpp_lib_concepts
static_assert(std::output_iterator<fmt::appender, char>);
#endif
enum { buffer_size = 256 };
TEST(uint128_test, ctor) {
@@ -105,6 +120,14 @@ TEST(float_test, isfinite) {
#endif
}
void check_no_fp_exception() {
fexcept_t fe;
fegetexceptflag(&fe, FE_ALL_EXCEPT);
// No exception flags should have been set
EXPECT_TRUE(fe == 0);
}
template <typename Float> void check_isnan() {
using fmt::detail::isnan;
EXPECT_FALSE(isnan(Float(0.0)));
@@ -117,6 +140,17 @@ template <typename Float> void check_isnan() {
EXPECT_FALSE(isnan(Float(-limits::infinity())));
EXPECT_TRUE(isnan(Float(limits::quiet_NaN())));
EXPECT_TRUE(isnan(Float(-limits::quiet_NaN())));
// Sanity check: make sure no error has occurred before we start
check_no_fp_exception();
// Check that no exception is raised for the non-NaN case
isnan(Float(42.0));
check_no_fp_exception();
// Check that no exception is raised for the NaN case
isnan(Float(limits::quiet_NaN()));
check_no_fp_exception();
}
TEST(float_test, isnan) {
@@ -239,7 +273,8 @@ TEST(util_test, format_system_error) {
throws_on_alloc = true;
}
if (!throws_on_alloc) {
fmt::print("warning: std::allocator allocates {} chars\n", max_size);
fmt::print(stderr, "warning: std::allocator allocates {} chars\n",
max_size);
return;
}
}
@@ -446,6 +481,12 @@ TEST(memory_buffer_test, max_size_allocator_overflow) {
EXPECT_THROW(buffer.resize(161), std::exception);
}
TEST(format_test, digits2_alignment) {
auto p =
fmt::detail::bit_cast<fmt::detail::uintptr_t>(fmt::detail::digits2(0));
EXPECT_EQ(p % 2, 0);
}
TEST(format_test, exception_from_lib) {
EXPECT_THROW_MSG(fmt::report_error("test"), format_error, "test");
}
@@ -769,7 +810,7 @@ TEST(format_test, hash_flag) {
EXPECT_EQ(fmt::format("{:#.2g}", 0.5), "0.50");
EXPECT_EQ(fmt::format("{:#.0f}", 0.5), "0.");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#"), 'c'), format_error,
"missing '}' in format string");
"invalid format specifier for char");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), "abc"), format_error,
@@ -790,7 +831,7 @@ TEST(format_test, zero_flag) {
EXPECT_EQ(fmt::format("{0:07}", -42.0), "-000042");
EXPECT_EQ(fmt::format("{0:07}", -42.0l), "-000042");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:0"), 'c'), format_error,
"missing '}' in format string");
"invalid format specifier for char");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), "abc"), format_error,
@@ -837,6 +878,10 @@ TEST(format_test, width) {
EXPECT_EQ(fmt::format("{:>06.0f}", 0.00884311), " 0");
}
auto bad_dynamic_spec_msg = FMT_BUILTIN_TYPES
? "width/precision is out of range"
: "width/precision is not integer";
TEST(format_test, runtime_width) {
auto int_maxer = std::to_string(INT_MAX + 1u);
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{" + int_maxer), 0),
@@ -859,23 +904,23 @@ TEST(format_test, runtime_width) {
"invalid format string");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1), format_error,
"negative width");
"width/precision is out of range");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1u)),
format_error, "number is too big");
format_error, bad_dynamic_spec_msg);
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1l), format_error,
"negative width");
bad_dynamic_spec_msg);
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (value + 1)),
format_error, "number is too big");
format_error, bad_dynamic_spec_msg);
}
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1ul)),
format_error, "number is too big");
format_error, bad_dynamic_spec_msg);
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, '0'), format_error,
"width is not integer");
"width/precision is not integer");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error,
"width is not integer");
"width/precision is not integer");
EXPECT_EQ(fmt::format("{0:{1}}", -42, 4), " -42");
EXPECT_EQ(fmt::format("{0:{1}}", 42u, 5), " 42");
@@ -892,6 +937,10 @@ TEST(format_test, runtime_width) {
EXPECT_EQ(fmt::format("{:{}}", 42, short(4)), " 42");
}
TEST(format_test, exponent_range) {
for (int e = -1074; e <= 1023; ++e) (void)fmt::format("{}", std::ldexp(1, e));
}
TEST(format_test, precision) {
char format_str[buffer_size];
safe_sprintf(format_str, "{0:.%u", UINT_MAX);
@@ -914,7 +963,7 @@ TEST(format_test, precision) {
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:."), 0.0), format_error,
"invalid precision");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.}"), 0.0), format_error,
"invalid precision");
"invalid format string");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2"), 0), format_error,
"invalid format specifier");
@@ -950,6 +999,9 @@ TEST(format_test, precision) {
EXPECT_EQ(fmt::format("{0:.3}", 1.1), "1.1");
EXPECT_EQ(fmt::format("{:.0e}", 1.0L), "1e+00");
EXPECT_EQ(fmt::format("{:9.1e}", 0.0), " 0.0e+00");
EXPECT_EQ(fmt::format("{:.7f}", 0.0000000000000071054273576010018587L),
"0.0000000");
EXPECT_EQ(
fmt::format("{:.494}", 4.9406564584124654E-324),
"4.9406564584124654417656879286822137236505980261432476442558568250067550"
@@ -1032,13 +1084,15 @@ TEST(format_test, precision) {
EXPECT_THROW_MSG(
(void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304),
format_error, "number is too big");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.f}"), 42.0), format_error,
"invalid format string");
EXPECT_EQ(fmt::format("{0:.2}", "str"), "st");
EXPECT_EQ(fmt::format("{0:.5}", "вожыкі"), "вожык");
EXPECT_EQ(fmt::format("{0:.6}", "123456\xad"), "123456");
}
TEST(xchar_test, utf8_precision) {
TEST(format_test, utf8_precision) {
auto result = fmt::format("{:.4}", "caf\u00e9s"); // cafés
EXPECT_EQ(fmt::detail::compute_width(result), 4);
EXPECT_EQ(result, "caf\u00e9");
@@ -1075,23 +1129,23 @@ TEST(format_test, runtime_precision) {
"invalid format string");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, -1),
format_error, "negative precision");
format_error, "width/precision is out of range");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (INT_MAX + 1u)),
format_error, "number is too big");
format_error, bad_dynamic_spec_msg);
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, -1l),
format_error, "negative precision");
format_error, bad_dynamic_spec_msg);
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (value + 1)),
format_error, "number is too big");
format_error, bad_dynamic_spec_msg);
}
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, (INT_MAX + 1ul)),
format_error, "number is too big");
format_error, bad_dynamic_spec_msg);
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, '0'),
format_error, "precision is not integer");
format_error, "width/precision is not integer");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0.0, 0.0),
format_error, "precision is not integer");
format_error, "width/precision is not integer");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42, 2), format_error,
"invalid format specifier");
@@ -1622,6 +1676,20 @@ TEST(format_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("'foo'",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
struct convertible_to_std_string_view {
operator std::string_view() const noexcept { return "Hi there"; }
};
FMT_BEGIN_NAMESPACE
template <>
class formatter<convertible_to_std_string_view>
: public formatter<std::string_view> {};
FMT_END_NAMESPACE
TEST(format_test, format_implicitly_convertible_and_inherits_string_view) {
static_assert(fmt::is_formattable<convertible_to_std_string_view>{}, "");
EXPECT_EQ("Hi there", fmt::format("{}", convertible_to_std_string_view{}));
}
#endif
class Answer {};
@@ -1752,6 +1820,71 @@ TEST(format_test, big_print) {
EXPECT_WRITE(stdout, big_print(), std::string(count, 'x'));
}
// Windows CRT implements _IOLBF incorrectly (full buffering).
#if FMT_USE_FCNTL && !defined(_WIN32)
TEST(format_test, line_buffering) {
auto pipe = fmt::pipe();
int write_fd = pipe.write_end.descriptor();
auto write_end = pipe.write_end.fdopen("w");
setvbuf(write_end.get(), nullptr, _IOLBF, 4096);
write_end.print("42\n");
close(write_fd);
try {
write_end.close();
} catch (const std::system_error&) {
}
auto read_end = pipe.read_end.fdopen("r");
std::thread reader([&]() {
int n = 0;
int result = fscanf(read_end.get(), "%d", &n);
(void)result;
EXPECT_EQ(n, 42);
});
reader.join();
}
#endif
struct deadlockable {
int value = 0;
mutable std::mutex mutex;
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<deadlockable> {
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(const deadlockable& d, format_context& ctx) const
-> decltype(ctx.out()) {
std::lock_guard<std::mutex> lock(d.mutex);
return format_to(ctx.out(), "{}", d.value);
}
};
FMT_END_NAMESPACE
TEST(format_test, locking_formatter) {
auto f = fmt::buffered_file();
try {
f = fmt::buffered_file("/dev/null", "w");
} catch (const std::system_error&) {
fmt::print(stderr, "warning: /dev/null is not supported\n");
return;
}
deadlockable d;
auto t = std::thread([&]() {
fmt::print(f.get(), "start t\n");
std::lock_guard<std::mutex> lock(d.mutex);
for (int i = 0; i < 1000000; ++i) d.value += 10;
fmt::print(f.get(), "done\n");
});
for (int i = 0; i < 100; ++i) fmt::print(f.get(), "{}", d);
t.join();
}
TEST(format_test, variadic) {
EXPECT_EQ(fmt::format("{}c{}", "ab", 1), "abc1");
}
@@ -1765,6 +1898,9 @@ TEST(format_test, bytes) {
TEST(format_test, group_digits_view) {
EXPECT_EQ(fmt::format("{}", fmt::group_digits(10000000)), "10,000,000");
EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(1000)), " 1,000");
EXPECT_EQ(fmt::format("{}", fmt::group_digits(-10000000)), "-10,000,000");
EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(-1000)), " -1,000");
EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(-100)), " -100");
}
#ifdef __cpp_generic_lambdas
@@ -1869,7 +2005,6 @@ TEST(format_test, custom_format_compile_time_string) {
EXPECT_EQ(fmt::format(FMT_STRING("{}"), const_answer), "42");
}
#if FMT_USE_USER_DEFINED_LITERALS
TEST(format_test, named_arg_udl) {
using namespace fmt::literals;
auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra",
@@ -1881,13 +2016,12 @@ TEST(format_test, named_arg_udl) {
EXPECT_EQ(fmt::format("{answer}", "answer"_a = Answer()), "42");
}
#endif // FMT_USE_USER_DEFINED_LITERALS
TEST(format_test, enum) { EXPECT_EQ(fmt::format("{}", foo), "0"); }
TEST(format_test, formatter_not_specialized) {
static_assert(!fmt::has_formatter<fmt::formatter<test_enum>,
fmt::format_context>::value,
static_assert(!fmt::is_formattable<fmt::formatter<test_enum>,
fmt::format_context>::value,
"");
}
@@ -1896,6 +2030,8 @@ enum big_enum : unsigned long long { big_enum_value = 5000000000ULL };
auto format_as(big_enum e) -> unsigned long long { return e; }
TEST(format_test, strong_enum) {
auto arg = fmt::basic_format_arg<fmt::context>(big_enum_value);
EXPECT_EQ(arg.type(), fmt::detail::type::ulong_long_type);
EXPECT_EQ(fmt::format("{}", big_enum_value), "5000000000");
}
#endif
@@ -1947,6 +2083,13 @@ TEST(format_test, output_iterators) {
EXPECT_EQ("42", s.str());
}
TEST(format_test, fill_via_appender) {
fmt::memory_buffer buf;
auto it = fmt::appender(buf);
std::fill_n(it, 3, '~');
EXPECT_EQ(fmt::to_string(buf), "~~~");
}
TEST(format_test, formatted_size) {
EXPECT_EQ(2u, fmt::formatted_size("{}", 42));
EXPECT_EQ(2u, fmt::formatted_size(std::locale(), "{}", 42));
@@ -2060,7 +2203,7 @@ TEST(format_test, vformat_to) {
fmt::vformat_to(std::back_inserter(s), "{}", args);
EXPECT_EQ(s, "42");
s.clear();
fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args);
fmt::vformat_to(std::back_inserter(s), "{}", args);
EXPECT_EQ(s, "42");
}
@@ -2147,16 +2290,21 @@ template <typename Char, typename... T> void check_enabled_formatters() {
}
TEST(format_test, test_formatters_enabled) {
using custom_string =
std::basic_string<char, std::char_traits<char>, mock_allocator<char>>;
using custom_wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>,
mock_allocator<wchar_t>>;
check_enabled_formatters<char, bool, char, signed char, unsigned char, short,
unsigned short, int, unsigned, long, unsigned long,
long long, unsigned long long, float, double,
long double, void*, const void*, char*, const char*,
std::string, std::nullptr_t>();
check_enabled_formatters<wchar_t, bool, wchar_t, signed char, unsigned char,
short, unsigned short, int, unsigned, long,
unsigned long, long long, unsigned long long, float,
double, long double, void*, const void*, wchar_t*,
const wchar_t*, std::wstring, std::nullptr_t>();
std::string, custom_string, std::nullptr_t>();
check_enabled_formatters<
wchar_t, bool, wchar_t, signed char, unsigned char, short, unsigned short,
int, unsigned, long, unsigned long, long long, unsigned long long, float,
double, long double, void*, const void*, wchar_t*, const wchar_t*,
std::wstring, custom_wstring, std::nullptr_t>();
}
TEST(format_int_test, data) {
@@ -2264,6 +2412,7 @@ namespace adl_test {
template <typename... T> void make_format_args(const T&...) = delete;
struct string : std::string {};
auto format_as(const string& s) -> std::string { return s; }
} // namespace adl_test
// Test that formatting functions compile when make_format_args is found by ADL.
@@ -2319,3 +2468,93 @@ TEST(format_test, formatter_overrides_implicit_conversion) {
EXPECT_EQ(fmt::format("{}", convertible_to_int()), "x");
EXPECT_EQ(fmt::format("{}", convertible_to_cstring()), "y");
}
struct ustring {
using value_type = unsigned;
auto find_first_of(value_type, size_t) const -> size_t;
auto data() const -> const char*;
auto size() const -> size_t;
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<ustring> : formatter<std::string> {
auto format(const ustring&, format_context& ctx) const
-> decltype(ctx.out()) {
return formatter<std::string>::format("ustring", ctx);
}
};
FMT_END_NAMESPACE
TEST(format_test, ustring) {
EXPECT_EQ(fmt::format("{}", ustring()), "ustring");
}
TEST(format_test, writer) {
auto write_to_stdout = []() {
auto w = fmt::writer(stdout);
w.print("{}", 42);
};
EXPECT_WRITE(stdout, write_to_stdout(), "42");
#if FMT_USE_FCNTL
auto pipe = fmt::pipe();
auto write_end = pipe.write_end.fdopen("w");
fmt::writer(write_end.get()).print("42");
write_end.close();
auto read_end = pipe.read_end.fdopen("r");
int n = 0;
int result = fscanf(read_end.get(), "%d", &n);
(void)result;
EXPECT_EQ(n, 42);
#endif
auto s = fmt::string_buffer();
fmt::writer(s).print("foo");
EXPECT_EQ(s.str(), "foo");
}
#if FMT_USE_BITINT
FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")
TEST(format_test, bitint) {
using fmt::detail::bitint;
using fmt::detail::ubitint;
EXPECT_EQ(fmt::format("{}", ubitint<3>(7)), "7");
EXPECT_EQ(fmt::format("{}", bitint<7>()), "0");
EXPECT_EQ(fmt::format("{}", ubitint<15>(31000)), "31000");
EXPECT_EQ(fmt::format("{}", bitint<16>(INT16_MIN)), "-32768");
EXPECT_EQ(fmt::format("{}", bitint<16>(INT16_MAX)), "32767");
EXPECT_EQ(fmt::format("{}", ubitint<32>(4294967295)), "4294967295");
EXPECT_EQ(fmt::format("{}", ubitint<47>(140737488355327ULL)),
"140737488355327");
EXPECT_EQ(fmt::format("{}", bitint<47>(-40737488355327LL)),
"-40737488355327");
// Check lvalues and const
auto a = bitint<8>(0);
auto b = ubitint<32>(4294967295);
const auto c = bitint<7>(0);
const auto d = ubitint<32>(4294967295);
EXPECT_EQ(fmt::format("{}", a), "0");
EXPECT_EQ(fmt::format("{}", b), "4294967295");
EXPECT_EQ(fmt::format("{}", c), "0");
EXPECT_EQ(fmt::format("{}", d), "4294967295");
static_assert(fmt::is_formattable<bitint<64>, char>{}, "");
static_assert(fmt::is_formattable<ubitint<64>, char>{}, "");
}
#endif
#ifdef __cpp_lib_byte
TEST(base_test, format_byte) {
auto s = std::string();
fmt::format_to(std::back_inserter(s), "{}", std::byte(42));
EXPECT_EQ(s, "42");
}
#endif

View File

@@ -13360,6 +13360,7 @@ bool UnorderedElementsAreMatcherImplBase::FindPairing(
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
@@ -13983,46 +13984,50 @@ MockObjectRegistry g_mock_object_registry;
// Maps a mock object to the reaction Google Mock should have when an
// uninteresting method is called. Protected by g_gmock_mutex.
std::map<const void*, internal::CallReaction> g_uninteresting_call_reaction;
std::unordered_map<uintptr_t, internal::CallReaction>&
UninterestingCallReactionMap() {
static auto* map = new std::unordered_map<uintptr_t, internal::CallReaction>;
return *map;
}
// Sets the reaction Google Mock should have when an uninteresting
// method of the given mock object is called.
void SetReactionOnUninterestingCalls(const void* mock_obj,
void SetReactionOnUninterestingCalls(uintptr_t mock_obj,
internal::CallReaction reaction)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
internal::MutexLock l(&internal::g_gmock_mutex);
g_uninteresting_call_reaction[mock_obj] = reaction;
UninterestingCallReactionMap()[mock_obj] = reaction;
}
} // namespace
// Tells Google Mock to allow uninteresting calls on the given mock
// object.
void Mock::AllowUninterestingCalls(const void* mock_obj)
void Mock::AllowUninterestingCalls(uintptr_t mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
SetReactionOnUninterestingCalls(mock_obj, internal::kAllow);
}
// Tells Google Mock to warn the user about uninteresting calls on the
// given mock object.
void Mock::WarnUninterestingCalls(const void* mock_obj)
void Mock::WarnUninterestingCalls(uintptr_t mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
SetReactionOnUninterestingCalls(mock_obj, internal::kWarn);
}
// Tells Google Mock to fail uninteresting calls on the given mock
// object.
void Mock::FailUninterestingCalls(const void* mock_obj)
void Mock::FailUninterestingCalls(uintptr_t mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
SetReactionOnUninterestingCalls(mock_obj, internal::kFail);
}
// Tells Google Mock the given mock object is being destroyed and its
// entry in the call-reaction table should be removed.
void Mock::UnregisterCallReaction(const void* mock_obj)
void Mock::UnregisterCallReaction(uintptr_t mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
internal::MutexLock l(&internal::g_gmock_mutex);
g_uninteresting_call_reaction.erase(mock_obj);
UninterestingCallReactionMap().erase(static_cast<uintptr_t>(mock_obj));
}
// Returns the reaction Google Mock will have on uninteresting calls
@@ -14031,9 +14036,12 @@ internal::CallReaction Mock::GetReactionOnUninterestingCalls(
const void* mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex) {
internal::MutexLock l(&internal::g_gmock_mutex);
return (g_uninteresting_call_reaction.count(mock_obj) == 0) ?
internal::intToCallReaction(GMOCK_FLAG(default_mock_behavior)) :
g_uninteresting_call_reaction[mock_obj];
return (UninterestingCallReactionMap().count(
reinterpret_cast<uintptr_t>(mock_obj)) == 0)
? internal::intToCallReaction(
GMOCK_FLAG(default_mock_behavior))
: UninterestingCallReactionMap()[reinterpret_cast<uintptr_t>(
mock_obj)];
}
// Tells Google Mock to ignore mock_obj when checking for leaked mock

View File

@@ -2860,6 +2860,7 @@ GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
@@ -8646,22 +8647,22 @@ class GTEST_API_ Mock {
// Tells Google Mock to allow uninteresting calls on the given mock
// object.
static void AllowUninterestingCalls(const void* mock_obj)
static void AllowUninterestingCalls(uintptr_t mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
// Tells Google Mock to warn the user about uninteresting calls on
// the given mock object.
static void WarnUninterestingCalls(const void* mock_obj)
static void WarnUninterestingCalls(uintptr_t mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
// Tells Google Mock to fail uninteresting calls on the given mock
// object.
static void FailUninterestingCalls(const void* mock_obj)
static void FailUninterestingCalls(uintptr_t mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
// Tells Google Mock the given mock object is being destroyed and
// its entry in the call-reaction table should be removed.
static void UnregisterCallReaction(const void* mock_obj)
static void UnregisterCallReaction(uintptr_t mock_obj)
GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
// Returns the reaction Google Mock will have on uninteresting calls
@@ -11417,6 +11418,7 @@ MATCHER(IsFalse, negation ? "is true" : "is false") {
#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
#include <cstdint>
#include <type_traits>
@@ -11461,25 +11463,37 @@ constexpr bool HasStrictnessModifier() {
template <typename Base>
class NiceMockImpl {
public:
NiceMockImpl() { ::testing::Mock::AllowUninterestingCalls(this); }
NiceMockImpl() {
::testing::Mock::AllowUninterestingCalls(reinterpret_cast<uintptr_t>(this));
}
~NiceMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
~NiceMockImpl() {
::testing::Mock::UnregisterCallReaction(reinterpret_cast<uintptr_t>(this));
}
};
template <typename Base>
class NaggyMockImpl {
public:
NaggyMockImpl() { ::testing::Mock::WarnUninterestingCalls(this); }
NaggyMockImpl() {
::testing::Mock::WarnUninterestingCalls(reinterpret_cast<uintptr_t>(this));
}
~NaggyMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
~NaggyMockImpl() {
::testing::Mock::UnregisterCallReaction(reinterpret_cast<uintptr_t>(this));
}
};
template <typename Base>
class StrictMockImpl {
public:
StrictMockImpl() { ::testing::Mock::FailUninterestingCalls(this); }
StrictMockImpl() {
::testing::Mock::FailUninterestingCalls(reinterpret_cast<uintptr_t>(this));
}
~StrictMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
~StrictMockImpl() {
::testing::Mock::UnregisterCallReaction(reinterpret_cast<uintptr_t>(this));
}
};
} // namespace internal

View File

@@ -6390,17 +6390,18 @@ class MatcherBase : private MatcherDescriberInterface {
}
protected:
MatcherBase() : vtable_(nullptr) {}
MatcherBase() : vtable_(nullptr), buffer_() {}
// Constructs a matcher from its implementation.
template <typename U>
explicit MatcherBase(const MatcherInterface<U>* impl) {
explicit MatcherBase(const MatcherInterface<U>* impl)
: vtable_(nullptr), buffer_() {
Init(impl);
}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
MatcherBase(M&& m) { // NOLINT
MatcherBase(M&& m) : vtable_(nullptr), buffer_() { // NOLINT
Init(std::forward<M>(m));
}

View File

@@ -20,6 +20,16 @@ template <typename T> class mock_allocator {
using value_type = T;
using size_type = size_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using difference_type = ptrdiff_t;
template <typename U> struct rebind {
using other = mock_allocator<U>;
};
mock_allocator() {}
mock_allocator(const mock_allocator&) {}

View File

@@ -18,6 +18,10 @@ using fmt::buffered_file;
using testing::HasSubstr;
using wstring_view = fmt::basic_string_view<wchar_t>;
static std::string uniq_file_name(unsigned line_number) {
return "test-file" + std::to_string(line_number);
}
#ifdef _WIN32
# include <windows.h>
@@ -232,68 +236,75 @@ TEST(buffered_file_test, descriptor) {
}
TEST(ostream_test, move) {
fmt::ostream out = fmt::output_file("test-file");
auto test_file = uniq_file_name(__LINE__);
fmt::ostream out = fmt::output_file(test_file);
fmt::ostream moved(std::move(out));
moved.print("hello");
}
TEST(ostream_test, move_while_holding_data) {
auto test_file = uniq_file_name(__LINE__);
{
fmt::ostream out = fmt::output_file("test-file");
fmt::ostream out = fmt::output_file(test_file);
out.print("Hello, ");
fmt::ostream moved(std::move(out));
moved.print("world!\n");
}
{
file in("test-file", file::RDONLY);
file in(test_file, file::RDONLY);
EXPECT_READ(in, "Hello, world!\n");
}
}
TEST(ostream_test, print) {
fmt::ostream out = fmt::output_file("test-file");
auto test_file = uniq_file_name(__LINE__);
fmt::ostream out = fmt::output_file(test_file);
out.print("The answer is {}.\n", 42);
out.close();
file in("test-file", file::RDONLY);
file in(test_file, file::RDONLY);
EXPECT_READ(in, "The answer is 42.\n");
}
TEST(ostream_test, buffer_boundary) {
auto str = std::string(4096, 'x');
fmt::ostream out = fmt::output_file("test-file");
auto test_file = uniq_file_name(__LINE__);
fmt::ostream out = fmt::output_file(test_file);
out.print("{}", str);
out.print("{}", str);
out.close();
file in("test-file", file::RDONLY);
file in(test_file, file::RDONLY);
EXPECT_READ(in, str + str);
}
TEST(ostream_test, buffer_size) {
fmt::ostream out = fmt::output_file("test-file", fmt::buffer_size = 1);
auto test_file = uniq_file_name(__LINE__);
fmt::ostream out = fmt::output_file(test_file, fmt::buffer_size = 1);
out.print("{}", "foo");
out.close();
file in("test-file", file::RDONLY);
file in(test_file, file::RDONLY);
EXPECT_READ(in, "foo");
}
TEST(ostream_test, truncate) {
auto test_file = uniq_file_name(__LINE__);
{
fmt::ostream out = fmt::output_file("test-file");
fmt::ostream out = fmt::output_file(test_file);
out.print("0123456789");
}
{
fmt::ostream out = fmt::output_file("test-file");
fmt::ostream out = fmt::output_file(test_file);
out.print("foo");
}
file in("test-file", file::RDONLY);
file in(test_file, file::RDONLY);
EXPECT_EQ("foo", read(in, 4));
}
TEST(ostream_test, flush) {
auto out = fmt::output_file("test-file");
auto test_file = uniq_file_name(__LINE__);
auto out = fmt::output_file(test_file);
out.print("x");
out.flush();
auto in = fmt::file("test-file", file::RDONLY);
auto in = fmt::file(test_file, file::RDONLY);
EXPECT_READ(in, "x");
}
@@ -303,10 +314,11 @@ TEST(file_test, default_ctor) {
}
TEST(file_test, open_buffered_file_in_ctor) {
FILE* fp = safe_fopen("test-file", "w");
auto test_file = uniq_file_name(__LINE__);
FILE* fp = safe_fopen(test_file.c_str(), "w");
std::fputs(file_content, fp);
std::fclose(fp);
file f("test-file", file::RDONLY);
file f(test_file.c_str(), file::RDONLY);
// Check if the file is open by reading one character from it.
char buffer;
bool isopen = FMT_POSIX(read(f.descriptor(), &buffer, 1)) == 1;
@@ -417,7 +429,8 @@ TEST(file_test, read) {
}
TEST(file_test, read_error) {
file f("test-file", file::WRONLY);
auto test_file = uniq_file_name(__LINE__);
file f(test_file, file::WRONLY | file::CREATE);
char buf;
// We intentionally read from a file opened in the write-only mode to
// cause error.
@@ -426,13 +439,15 @@ TEST(file_test, read_error) {
TEST(file_test, write) {
auto pipe = fmt::pipe();
write(pipe.write_end, "test");
auto test_file = uniq_file_name(__LINE__);
write(pipe.write_end, test_file);
pipe.write_end.close();
EXPECT_READ(pipe.read_end, "test");
EXPECT_READ(pipe.read_end, test_file);
}
TEST(file_test, write_error) {
file f("test-file", file::RDONLY);
auto test_file = uniq_file_name(__LINE__);
file f(test_file, file::RDONLY | file::CREATE);
// We intentionally write to a file opened in the read-only mode to
// cause error.
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");

25
external/fmt/test/perf-sanity.cc vendored Normal file
View File

@@ -0,0 +1,25 @@
// A quick and dirty performance test.
// For actual benchmarks see https://github.com/fmtlib/format-benchmark.
#include <atomic>
#include <chrono>
#include <iterator>
#include "fmt/format.h"
int main() {
const int n = 10000000;
auto start = std::chrono::steady_clock::now();
for (int iteration = 0; iteration < n; ++iteration) {
auto buf = fmt::memory_buffer();
fmt::format_to(std::back_inserter(buf),
"Hello, {}. The answer is {} and {}.", 1, 2345, 6789);
}
std::atomic_signal_fence(std::memory_order_acq_rel); // Clobber memory.
auto end = std::chrono::steady_clock::now();
// Print time in milliseconds.
std::chrono::duration<double> duration = end - start;
fmt::print("{:.1f}\n", duration.count() * 1000);
}

View File

@@ -6,6 +6,10 @@
// For the license information refer to format.h.
#include "fmt/printf.h"
// include <format> if possible for https://github.com/fmtlib/fmt/pull/4042
#if FMT_HAS_INCLUDE(<format>) && FMT_CPLUSPLUS > 201703L
# include <format>
#endif
#include <cctype>
#include <climits>

View File

@@ -7,6 +7,7 @@
#include "fmt/ranges.h"
#include <array>
#include <list>
#include <map>
#include <numeric>
@@ -16,7 +17,7 @@
#include <utility>
#include <vector>
#if FMT_HAS_INCLUDE(<ranges>)
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<ranges>)
# include <ranges>
#endif
@@ -58,8 +59,13 @@ TEST(ranges_test, format_vector) {
EXPECT_EQ(fmt::format("{:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb");
auto vc = std::vector<char>{'a', 'b', 'c'};
auto vec = std::vector<char>{'a', '\n', '\t'};
auto vvc = std::vector<std::vector<char>>{vc, vc};
EXPECT_EQ(fmt::format("{}", vc), "['a', 'b', 'c']");
EXPECT_EQ(fmt::format("{:s}", vc), "\"abc\"");
EXPECT_EQ(fmt::format("{:?s}", vec), "\"a\\n\\t\"");
EXPECT_EQ(fmt::format("{:s}", vec), "\"a\n\t\"");
EXPECT_EQ(fmt::format("{::s}", vvc), "[\"abc\", \"abc\"]");
EXPECT_EQ(fmt::format("{}", vvc), "[['a', 'b', 'c'], ['a', 'b', 'c']]");
EXPECT_EQ(fmt::format("{:n}", vvc), "['a', 'b', 'c'], ['a', 'b', 'c']");
EXPECT_EQ(fmt::format("{:n:n}", vvc), "'a', 'b', 'c', 'a', 'b', 'c'");
@@ -84,6 +90,35 @@ TEST(ranges_test, format_map) {
EXPECT_EQ(fmt::format("{:n}", m), "\"one\": 1, \"two\": 2");
}
struct test_map_value {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<test_map_value> : formatter<string_view> {
auto format(test_map_value, format_context& ctx) const
-> format_context::iterator {
return formatter<string_view>::format("foo", ctx);
}
};
template <typename K>
struct formatter<std::pair<K, test_map_value>> : formatter<string_view> {
auto format(std::pair<K, test_map_value>, format_context& ctx) const
-> format_context::iterator {
return ctx.out();
}
};
template <typename K>
struct is_tuple_formattable<std::pair<K, test_map_value>, char>
: std::false_type {};
FMT_END_NAMESPACE
TEST(ranges_test, format_map_custom_pair) {
EXPECT_EQ(fmt::format("{}", std::map<int, test_map_value>{{42, {}}}),
"{42: \"foo\"}");
}
TEST(ranges_test, format_set) {
EXPECT_EQ(fmt::format("{}", std::set<std::string>{"one", "two"}),
"{\"one\", \"two\"}");
@@ -135,6 +170,8 @@ TEST(ranges_test, format_adl_begin_end) {
TEST(ranges_test, format_pair) {
auto p = std::pair<int, float>(42, 1.5f);
EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)");
EXPECT_EQ(fmt::format("{:}", p), "(42, 1.5)");
EXPECT_EQ(fmt::format("{:n}", p), "421.5");
}
struct unformattable {};
@@ -143,6 +180,7 @@ TEST(ranges_test, format_tuple) {
auto t =
std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i');
EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')");
EXPECT_EQ(fmt::format("{:n}", t), "421.5\"this is tuple\"'i'");
EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()");
@@ -233,6 +271,33 @@ TEST(ranges_test, disabled_range_formatting_of_path) {
fmt::range_format::disabled);
}
struct vector_string : std::vector<char> {
using base = std::vector<char>;
using base::base;
};
struct vector_debug_string : std::vector<char> {
using base = std::vector<char>;
using base::base;
};
FMT_BEGIN_NAMESPACE
template <>
struct range_format_kind<vector_string, char>
: std::integral_constant<range_format, range_format::string> {};
template <>
struct range_format_kind<vector_debug_string, char>
: std::integral_constant<range_format, range_format::debug_string> {};
FMT_END_NAMESPACE
TEST(ranges_test, range_format_string) {
const vector_string v{'f', 'o', 'o'};
EXPECT_EQ(fmt::format("{}", v), "foo");
}
TEST(ranges_test, range_format_debug_string) {
const vector_debug_string v{'f', 'o', 'o'};
EXPECT_EQ(fmt::format("{}", v), "\"foo\"");
}
// A range that provides non-const only begin()/end() to test fmt::join
// handles that.
//
@@ -298,8 +363,7 @@ TEST(ranges_test, enum_range) {
#if !FMT_MSC_VERSION
TEST(ranges_test, unformattable_range) {
EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,
fmt::format_context>::value));
EXPECT_FALSE((fmt::is_formattable<std::vector<unformattable>, char>::value));
}
#endif
@@ -457,23 +521,21 @@ TEST(ranges_test, join_range) {
}
namespace adl {
struct vec : std::vector<int> {
using std::vector<int>::vector; // inherit all constructors
struct vec {
int n[2] = {42, 43};
};
// ADL-found begin() and end() skip the first and last element
auto begin(vec& v) -> typename vec::iterator { return v.begin() + 1; }
auto end(vec& v) -> typename vec::iterator { return v.end() - 1; }
}
auto begin(const vec& v) -> const int* { return v.n; }
auto end(const vec& v) -> const int* { return v.n + 2; }
} // namespace adl
TEST(ranges_test, format_join_adl_begin_end) {
auto v = adl::vec{41, 42, 43, 44};
EXPECT_EQ(fmt::format("{}", fmt::join(v, "/")), "42/43");
EXPECT_EQ(fmt::format("{}", fmt::join(adl::vec(), "/")), "42/43");
}
#endif // FMT_RANGES_TEST_ENABLE_JOIN
#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 202302L
#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 202207L
TEST(ranges_test, nested_ranges) {
auto l = std::list{1, 2, 3};
auto r = std::views::iota(0, 3) | std::views::transform([&l](auto i) {
@@ -498,7 +560,7 @@ TEST(ranges_test, escape) {
EXPECT_EQ(fmt::format("{}", vec{"\x7f"}), "[\"\\x7f\"]");
EXPECT_EQ(fmt::format("{}", vec{"n\xcc\x83"}), "[\"n\xcc\x83\"]");
if (fmt::detail::is_utf8()) {
if (fmt::detail::use_utf8) {
EXPECT_EQ(fmt::format("{}", vec{"\xcd\xb8"}), "[\"\\u0378\"]");
// Unassigned Unicode code points.
EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
@@ -516,7 +578,11 @@ TEST(ranges_test, escape) {
EXPECT_EQ(fmt::format("{}", std::vector<std::vector<char>>{{'x'}}),
"[['x']]");
// Disabled due to a clang 17 bug: https://github.com/fmtlib/fmt/issues/4144.
#if FMT_CLANG_VERSION >= 1800
EXPECT_EQ(fmt::format("{}", std::tuple<std::vector<char>>{{'x'}}), "(['x'])");
#endif
}
template <typename R> struct fmt_ref_view {
@@ -620,3 +686,110 @@ struct lvalue_qualified_begin_end {
TEST(ranges_test, lvalue_qualified_begin_end) {
EXPECT_EQ(fmt::format("{}", lvalue_qualified_begin_end{}), "[1, 2, 3, 4, 5]");
}
#if !defined(__cpp_lib_ranges) || __cpp_lib_ranges <= 202106L
# define ENABLE_STD_VIEWS_TESTS 0
#elif FMT_CLANG_VERSION
# if FMT_CLANG_VERSION > 1500
# define ENABLE_STD_VIEWS_TESTS 1
# else
# define ENABLE_STD_VIEWS_TESTS 0
# endif
#else
# define ENABLE_STD_VIEWS_TESTS 1
#endif
#if ENABLE_STD_VIEWS_TESTS
TEST(ranges_test, input_range_join) {
auto iss = std::istringstream("1 2 3 4 5");
auto view = std::views::istream<std::string>(iss);
EXPECT_EQ("1, 2, 3, 4, 5",
fmt::format("{}", fmt::join(view.begin(), view.end(), ", ")));
}
TEST(ranges_test, input_range_join_overload) {
auto iss = std::istringstream("1 2 3 4 5");
EXPECT_EQ(
"1.2.3.4.5",
fmt::format("{}", fmt::join(std::views::istream<std::string>(iss), ".")));
}
namespace views_filter_view_test {
struct codec_mask {
static constexpr auto codecs = std::array{0, 1, 2, 3};
int except = 0;
};
auto format_as(codec_mask mask) {
// Careful not to capture param by reference here, it will dangle.
return codec_mask::codecs |
std::views::filter([mask](auto c) { return c != mask.except; });
}
} // namespace views_filter_view_test
TEST(ranges_test, format_as_with_ranges_mutable_begin_end) {
using namespace views_filter_view_test;
{
auto make_filter_view = []() {
return codec_mask::codecs |
std::views::filter([](auto c) { return c != 2; });
};
auto r = make_filter_view();
EXPECT_EQ("[0, 1, 3]", fmt::format("{}", r));
EXPECT_EQ("[0, 1, 3]", fmt::format("{}", make_filter_view()));
}
{
auto mask = codec_mask{2};
const auto const_mask = codec_mask{2};
EXPECT_EQ("[0, 1, 3]", fmt::format("{}", mask));
EXPECT_EQ("[0, 1, 3]", fmt::format("{}", const_mask));
EXPECT_EQ("[0, 1, 3]", fmt::format("{}", codec_mask{2}));
}
}
#endif
TEST(ranges_test, std_istream_iterator_join) {
auto&& iss = std::istringstream("1 2 3 4 5");
auto first = std::istream_iterator<int>(iss);
auto last = std::istream_iterator<int>();
EXPECT_EQ("1, 2, 3, 4, 5", fmt::format("{}", fmt::join(first, last, ", ")));
}
// Mirrors C++20 std::ranges::basic_istream_view::iterator.
struct noncopyable_istream_iterator : std::istream_iterator<int> {
using base = std::istream_iterator<int>;
explicit noncopyable_istream_iterator(std::istringstream& iss) : base{iss} {}
noncopyable_istream_iterator(const noncopyable_istream_iterator&) = delete;
noncopyable_istream_iterator(noncopyable_istream_iterator&&) = default;
};
static_assert(!std::is_copy_constructible<noncopyable_istream_iterator>::value,
"");
TEST(ranges_test, movable_only_istream_iter_join) {
auto&& iss = std::istringstream("1 2 3 4 5");
auto first = noncopyable_istream_iterator(iss);
auto last = std::istream_iterator<int>();
EXPECT_EQ("1, 2, 3, 4, 5",
fmt::format("{}", fmt::join(std::move(first), last, ", ")));
}
struct movable_iter_range {
std::istringstream iss{"1 2 3 4 5"};
noncopyable_istream_iterator begin() {
return noncopyable_istream_iterator{iss};
}
std::istream_iterator<int> end() { return {}; }
};
TEST(ranges_test, movable_only_istream_iter_join2) {
EXPECT_EQ("[1, 2, 3, 4, 5]", fmt::format("{}", movable_iter_range{}));
}
struct not_range {
void begin() const {}
void end() const {}
};
static_assert(!fmt::is_formattable<not_range>{}, "");

View File

@@ -110,12 +110,27 @@ TEST(scan_test, invalid_format) {
"invalid format string");
}
namespace std {
using fmt::scan;
using fmt::scan_error;
} // namespace std
TEST(scan_test, example) {
std::string key;
int value = 0;
fmt::scan_to("answer = 42", "{} = {}", key, value);
EXPECT_EQ(key, "answer");
EXPECT_EQ(value, 42);
// Example from https://wg21.link/p1729r3.
if (auto result = std::scan<std::string, int>("answer = 42", "{} = {}")) {
auto range = result->range();
EXPECT_EQ(range.begin(), range.end());
EXPECT_EQ(result->begin(), result->end());
#ifdef __cpp_structured_bindings
const auto& [key, value] = result->values();
EXPECT_EQ(key, "answer");
EXPECT_EQ(value, 42);
#endif
} else {
std::scan_error error = result.error();
(void)error;
FAIL();
}
}
TEST(scan_test, end_of_input) { fmt::scan<int>("", "{}"); }

View File

@@ -10,7 +10,7 @@
#include <climits>
#include <tuple>
#include "fmt/format.h"
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
namespace detail {
@@ -142,7 +142,7 @@ class scan_buffer {
using scan_iterator = scan_buffer::iterator;
using scan_sentinel = scan_buffer::sentinel;
class string_scan_buffer : public scan_buffer {
class string_scan_buffer final : public scan_buffer {
private:
void consume() override {}
@@ -151,13 +151,15 @@ class string_scan_buffer : public scan_buffer {
: scan_buffer(s.begin(), s.end(), true) {}
};
class file_scan_buffer : public scan_buffer {
class file_scan_buffer final : public scan_buffer {
private:
template <typename F, FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0)>
template <typename F, FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0 &&
!FMT_USE_FALLBACK_FILE)>
static auto get_file(F* f, int) -> glibc_file<F> {
return f;
}
template <typename F, FMT_ENABLE_IF(sizeof(F::_p) != 0)>
template <typename F,
FMT_ENABLE_IF(sizeof(F::_p) != 0 && !FMT_USE_FALLBACK_FILE)>
static auto get_file(F* f, int) -> apple_file<F> {
return f;
}
@@ -190,7 +192,10 @@ class file_scan_buffer : public scan_buffer {
flockfile(f);
fill();
}
~file_scan_buffer() { funlockfile(file_); }
~file_scan_buffer() {
FILE* f = file_;
funlockfile(f);
}
};
} // namespace detail
@@ -363,7 +368,7 @@ const char* parse_scan_specs(const char* begin, const char* end,
switch (to_ascii(*begin)) {
// TODO: parse more scan format specifiers
case 'x':
specs.type = presentation_type::hex;
specs.set_type(presentation_type::hex);
++begin;
break;
case '}':
@@ -432,7 +437,7 @@ auto read_hex(scan_iterator it, T& value) -> scan_iterator {
template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
auto read(scan_iterator it, T& value, const format_specs& specs)
-> scan_iterator {
if (specs.type == presentation_type::hex) return read_hex(it, value);
if (specs.type() == presentation_type::hex) return read_hex(it, value);
return read(it, value);
}
@@ -548,47 +553,80 @@ struct scan_handler {
return begin;
}
void on_error(const char* message) { report_error(message); }
FMT_NORETURN void on_error(const char* message) { report_error(message); }
};
void vscan(detail::scan_buffer& buf, string_view fmt, scan_args args) {
auto h = detail::scan_handler(fmt, buf, args);
detail::parse_format_string<false>(fmt, h);
detail::parse_format_string(fmt, h);
}
template <size_t I, typename... T, FMT_ENABLE_IF(I == sizeof...(T))>
void make_args(std::array<scan_arg, sizeof...(T)>&, std::tuple<T...>&) {}
template <size_t I, typename... T, FMT_ENABLE_IF(I < sizeof...(T))>
void make_args(std::array<scan_arg, sizeof...(T)>& args,
std::tuple<T...>& values) {
using element_type = typename std::tuple_element<I, std::tuple<T...>>::type;
static_assert(std::is_same<remove_cvref_t<element_type>, element_type>::value,
"");
args[I] = std::get<I>(values);
make_args<I + 1>(args, values);
}
} // namespace detail
template <typename... T>
auto make_scan_args(T&... args) -> std::array<scan_arg, sizeof...(T)> {
return {{args...}};
}
template <typename... T> class scan_data {
template <typename Range, typename... T> class scan_data {
private:
std::tuple<T...> values_;
Range range_;
public:
scan_data() = default;
scan_data(T... values) : values_(std::move(values)...) {}
auto value() const -> decltype(std::get<0>(values_)) {
return std::get<0>(values_);
}
auto values() const -> const std::tuple<T...>& { return values_; }
auto make_args() -> std::array<scan_arg, sizeof...(T)> {
auto args = std::array<scan_arg, sizeof...(T)>();
detail::make_args<0>(args, values_);
return args;
}
auto range() const -> Range { return range_; }
auto begin() const -> decltype(range_.begin()) { return range_.begin(); }
auto end() const -> decltype(range_.end()) { return range_.end(); }
};
template <typename... T>
auto make_scan_args(T&... args) -> std::array<scan_arg, sizeof...(T)> {
return {{args...}};
}
class scan_error {};
// A rudimentary version of std::expected for testing the API shape.
template <typename T, typename E> class expected {
private:
T value_;
bool has_value_ = true;
public:
expected(T value) : value_(std::move(value)) {}
explicit operator bool() const { return has_value_; }
auto operator->() const -> const T* { return &value_; }
auto error() -> E const { return E(); }
};
template <typename... T>
using scan_result = expected<scan_data<T...>, scan_error>;
template <typename Range, typename... T>
using scan_result = expected<scan_data<Range, T...>, scan_error>;
auto vscan(string_view input, string_view fmt, scan_args args)
-> string_view::iterator {
@@ -604,12 +642,12 @@ auto scan_to(string_view input, string_view fmt, T&... args)
return vscan(input, fmt, make_scan_args(args...));
}
template <typename T>
auto scan(string_view input, string_view fmt) -> scan_result<T> {
static_assert(std::is_same<remove_cvref_t<T>, T>::value, "");
auto value = T();
scan_to(input, fmt, value);
return scan_data<T>(std::move(value));
template <typename... T>
auto scan(string_view input, string_view fmt)
-> scan_result<string_view, T...> {
auto data = scan_data<string_view, T...>();
vscan(input, fmt, data.make_args());
return data;
}
template <typename Range, typename... T,

View File

@@ -35,6 +35,11 @@ TEST(std_test, path) {
L"\x0447\x044B\x043D\x0430")),
"Шчучыншчына");
EXPECT_EQ(fmt::format("{}", path(L"\xd800")), "<EFBFBD>");
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xd800 TAIL")), "HEAD <20> TAIL");
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xDE00 TAIL")),
"HEAD \xF0\x9F\x98\x80 TAIL");
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xD83D\xDE00 TAIL")),
"HEAD <20>\xF0\x9F\x98\x80 TAIL");
EXPECT_EQ(fmt::format("{:?}", path(L"\xd800")), "\"\\ud800\"");
# endif
}
@@ -65,6 +70,37 @@ TEST(std_test, thread_id) {
EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty());
}
TEST(std_test, complex) {
using limits = std::numeric_limits<double>;
EXPECT_EQ(fmt::format("{}", std::complex<double>(1, limits::quiet_NaN())),
"(1+nan i)");
EXPECT_EQ(fmt::format("{}", std::complex<double>(1, -limits::infinity())),
"(1-inf i)");
EXPECT_EQ(fmt::format("{}", std::complex<int>(1, 2)), "(1+2i)");
EXPECT_EQ(fmt::format("{}", std::complex<double>(1, 2.2)), "(1+2.2i)");
EXPECT_EQ(fmt::format("{}", std::complex<double>(1, -2.2)), "(1-2.2i)");
EXPECT_EQ(fmt::format("{}", std::complex<double>(0, 2.2)), "2.2i");
EXPECT_EQ(fmt::format("{}", std::complex<double>(0, -2.2)), "-2.2i");
EXPECT_EQ(fmt::format("{:+}", std::complex<double>(0, 2.2)), "+2.2i");
EXPECT_EQ(fmt::format("{:+}", std::complex<double>(0, -2.2)), "-2.2i");
EXPECT_EQ(fmt::format("{:+}", std::complex<double>(1, -2.2)), "(+1-2.2i)");
EXPECT_EQ(fmt::format("{:+}", std::complex<double>(1, 2.2)), "(+1+2.2i)");
EXPECT_EQ(fmt::format("{: }", std::complex<double>(1, 2.2)), "( 1+2.2i)");
EXPECT_EQ(fmt::format("{: }", std::complex<double>(1, -2.2)), "( 1-2.2i)");
EXPECT_EQ(fmt::format("{:>20.2f}", std::complex<double>(1, 2.2)),
" (1.00+2.20i)");
EXPECT_EQ(fmt::format("{:<20.2f}", std::complex<double>(1, 2.2)),
"(1.00+2.20i) ");
EXPECT_EQ(fmt::format("{:<20.2f}", std::complex<double>(1, -2.2)),
"(1.00-2.20i) ");
EXPECT_EQ(fmt::format("{:<{}.{}f}", std::complex<double>(1, -2.2), 20, 2),
"(1.00-2.20i) ");
}
#ifdef __cpp_lib_source_location
TEST(std_test, source_location) {
std::source_location loc = std::source_location::current();
@@ -102,6 +138,36 @@ TEST(std_test, optional) {
#endif
}
TEST(std_test, expected) {
#ifdef __cpp_lib_expected
EXPECT_EQ(fmt::format("{}", std::expected<void, int>{}), "expected()");
EXPECT_EQ(fmt::format("{}", std::expected<int, int>{1}), "expected(1)");
EXPECT_EQ(fmt::format("{}", std::expected<int, int>{std::unexpected(1)}),
"unexpected(1)");
EXPECT_EQ(fmt::format("{}", std::expected<std::string, int>{"test"}),
"expected(\"test\")");
EXPECT_EQ(fmt::format(
"{}", std::expected<int, std::string>{std::unexpected("test")}),
"unexpected(\"test\")");
EXPECT_EQ(fmt::format("{}", std::expected<char, int>{'a'}), "expected('a')");
EXPECT_EQ(fmt::format("{}", std::expected<int, char>{std::unexpected('a')}),
"unexpected('a')");
struct unformattable1 {};
struct unformattable2 {};
EXPECT_FALSE((fmt::is_formattable<unformattable1>::value));
EXPECT_FALSE((fmt::is_formattable<unformattable2>::value));
EXPECT_FALSE((fmt::is_formattable<
std::expected<unformattable1, unformattable2>>::value));
EXPECT_FALSE(
(fmt::is_formattable<std::expected<unformattable1, int>>::value));
EXPECT_FALSE(
(fmt::is_formattable<std::expected<int, unformattable2>>::value));
EXPECT_TRUE((fmt::is_formattable<std::expected<int, int>>::value));
EXPECT_TRUE((fmt::is_formattable<std::expected<void, int>>::value));
#endif
}
namespace my_nso {
enum class my_number {
one,
@@ -199,9 +265,13 @@ TEST(std_test, variant) {
}
TEST(std_test, error_code) {
auto& generic = std::generic_category();
EXPECT_EQ("generic:42",
fmt::format(FMT_STRING("{0}"),
std::error_code(42, std::generic_category())));
fmt::format(FMT_STRING("{0}"), std::error_code(42, generic)));
EXPECT_EQ(" generic:42",
fmt::format(FMT_STRING("{:>12}"), std::error_code(42, generic)));
EXPECT_EQ("generic:42 ",
fmt::format(FMT_STRING("{:12}"), std::error_code(42, generic)));
EXPECT_EQ("system:42",
fmt::format(FMT_STRING("{0}"),
std::error_code(42, fmt::system_category())));
@@ -265,6 +335,13 @@ TEST(std_test, exception) {
#endif
}
#if FMT_USE_RTTI
TEST(std_test, type_info) {
EXPECT_EQ(fmt::format("{}", typeid(std::runtime_error)),
"std::runtime_error");
}
#endif
TEST(std_test, format_bit_reference) {
std::bitset<2> bs(1);
EXPECT_EQ(fmt::format("{} {}", bs[0], bs[1]), "true false");
@@ -322,3 +399,8 @@ TEST(std_test, format_shared_ptr) {
EXPECT_EQ(fmt::format("{}", fmt::ptr(sp.get())),
fmt::format("{}", fmt::ptr(sp)));
}
TEST(std_test, format_reference_wrapper) {
int num = 35;
EXPECT_EQ("35", fmt::to_string(std::cref(num)));
}

View File

@@ -15,7 +15,7 @@
using testing::Contains;
TEST(unicode_test, is_utf8) { EXPECT_TRUE(fmt::detail::is_utf8()); }
TEST(unicode_test, use_utf8) { EXPECT_TRUE(fmt::detail::use_utf8); }
TEST(unicode_test, legacy_locale) {
auto loc = get_locale("be_BY.CP1251", "Belarusian_Belarus.1251");

View File

@@ -36,12 +36,11 @@ std::locale do_get_locale(const char* name) {
std::locale get_locale(const char* name, const char* alt_name) {
auto loc = do_get_locale(name);
if (loc == std::locale::classic() && alt_name)
loc = do_get_locale(alt_name);
if (loc == std::locale::classic() && alt_name) loc = do_get_locale(alt_name);
#ifdef __OpenBSD__
// Locales are not working in OpenBSD:
// https://github.com/fmtlib/fmt/issues/3670.
loc = std::locale::classic();
// Locales are not working in OpenBSD:
// https://github.com/fmtlib/fmt/issues/3670.
loc = std::locale::classic();
#endif
if (loc == std::locale::classic())
fmt::print(stderr, "{} locale is missing.\n", name);

View File

@@ -78,7 +78,6 @@ TEST(xchar_test, format) {
EXPECT_EQ(L"z", fmt::format(L"{}", L'z'));
EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error);
EXPECT_EQ(L"true", fmt::format(L"{}", true));
EXPECT_EQ(L"a", fmt::format(L"{0}", 'a'));
EXPECT_EQ(L"a", fmt::format(L"{0}", L'a'));
EXPECT_EQ(L"Cyrillic letter \x42e",
fmt::format(L"Cyrillic letter {}", L'\x42e'));
@@ -96,105 +95,18 @@ TEST(xchar_test, compile_time_string) {
#endif
}
#if FMT_CPLUSPLUS > 201103L
struct custom_char {
int value;
custom_char() = default;
template <typename T>
constexpr custom_char(T val) : value(static_cast<int>(val)) {}
constexpr operator char() const {
return value <= 0xff ? static_cast<char>(value) : '\0';
}
constexpr bool operator<(custom_char c) const { return value < c.value; }
};
namespace std {
template <> struct char_traits<custom_char> {
using char_type = custom_char;
using int_type = int;
using off_type = streamoff;
using pos_type = streampos;
using state_type = mbstate_t;
static constexpr void assign(char_type& r, const char_type& a) { r = a; }
static constexpr bool eq(char_type a, char_type b) { return a == b; }
static constexpr bool lt(char_type a, char_type b) { return a < b; }
static FMT_CONSTEXPR int compare(const char_type* s1, const char_type* s2,
size_t count) {
for (; count; count--, s1++, s2++) {
if (lt(*s1, *s2)) return -1;
if (lt(*s2, *s1)) return 1;
}
return 0;
}
static FMT_CONSTEXPR size_t length(const char_type* s) {
size_t count = 0;
while (!eq(*s++, custom_char(0))) count++;
return count;
}
static const char_type* find(const char_type*, size_t, const char_type&);
static FMT_CONSTEXPR char_type* move(char_type* dest, const char_type* src,
size_t count) {
if (count == 0) return dest;
char_type* ret = dest;
if (src < dest) {
dest += count;
src += count;
for (; count; count--) assign(*--dest, *--src);
} else if (src > dest)
copy(dest, src, count);
return ret;
}
static FMT_CONSTEXPR char_type* copy(char_type* dest, const char_type* src,
size_t count) {
char_type* ret = dest;
for (; count; count--) assign(*dest++, *src++);
return ret;
}
static FMT_CONSTEXPR char_type* assign(char_type* dest, std::size_t count,
char_type a) {
char_type* ret = dest;
for (; count; count--) assign(*dest++, a);
return ret;
}
static int_type not_eof(int_type);
static char_type to_char_type(int_type);
static int_type to_int_type(char_type);
static bool eq_int_type(int_type, int_type);
static int_type eof();
};
} // namespace std
auto to_ascii(custom_char c) -> char { return c; }
FMT_BEGIN_NAMESPACE
template <> struct is_char<custom_char> : std::true_type {};
FMT_END_NAMESPACE
TEST(xchar_test, format_custom_char) {
const custom_char format[] = {'{', '}', 0};
auto result = fmt::format(format, custom_char('x'));
EXPECT_EQ(result.size(), 1);
EXPECT_EQ(result[0], custom_char('x'));
}
#endif
// Convert a char8_t string to std::string. Otherwise GTest will insist on
// inserting `char8_t` NTBS into a `char` stream which is disabled by P1423.
template <typename S> std::string from_u8str(const S& str) {
return std::string(str.begin(), str.end());
}
TEST(xchar_test, format_to) {
auto buf = std::vector<wchar_t>();
fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0');
EXPECT_STREQ(buf.data(), L"42");
}
TEST(xchar_test, compile_time_string_format_to) {
std::wstring ws;
fmt::format_to(std::back_inserter(ws), FMT_STRING(L"{}"), 42);
EXPECT_EQ(L"42", ws);
}
TEST(xchar_test, vformat_to) {
int n = 42;
auto args = fmt::make_wformat_args(n);
@@ -232,7 +144,6 @@ TEST(format_test, wide_format_to_n) {
EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4));
}
#if FMT_USE_USER_DEFINED_LITERALS
TEST(xchar_test, named_arg_udl) {
using namespace fmt::literals;
auto udl_a =
@@ -243,7 +154,6 @@ TEST(xchar_test, named_arg_udl) {
fmt::arg(L"second", L"cad"), fmt::arg(L"third", 99)),
udl_a);
}
#endif // FMT_USE_USER_DEFINED_LITERALS
TEST(xchar_test, print) {
// Check that the wide print overload compiles.
@@ -517,7 +427,7 @@ TEST(locale_test, format) {
fmt::format(small_grouping_loc, "{:L}", max_value<uint32_t>()));
}
TEST(locale_test, format_detault_align) {
TEST(locale_test, format_default_align) {
auto loc = std::locale({}, new special_grouping<char>());
EXPECT_EQ(" 12,345", fmt::format(loc, "{:8L}", 12345));
}
@@ -562,60 +472,15 @@ TEST(locale_test, int_formatter) {
EXPECT_EQ(fmt::to_string(buf), "12,345");
}
FMT_BEGIN_NAMESPACE
template <class charT> struct formatter<std::complex<double>, charT> {
private:
detail::dynamic_format_specs<char> specs_;
public:
FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse(
basic_format_parse_context<charT>& ctx) {
auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type::float_type);
detail::parse_float_type_spec(specs_);
return end;
}
template <class FormatContext>
typename FormatContext::iterator format(const std::complex<double>& c,
FormatContext& ctx) const {
auto specs = specs_;
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
auto fspecs = std::string();
if (specs.precision > 0) fspecs = fmt::format(".{}", specs.precision);
if (specs.type == presentation_type::fixed) fspecs += 'f';
auto real = fmt::format(ctx.locale().template get<std::locale>(),
fmt::runtime("{:" + fspecs + "}"), c.real());
auto imag = fmt::format(ctx.locale().template get<std::locale>(),
fmt::runtime("{:" + fspecs + "}"), c.imag());
auto fill_align_width = std::string();
if (specs.width > 0) fill_align_width = fmt::format(">{}", specs.width);
return fmt::format_to(ctx.out(), runtime("{:" + fill_align_width + "}"),
c.real() != 0 ? fmt::format("({}+{}i)", real, imag)
: fmt::format("{}i", imag));
}
};
FMT_END_NAMESPACE
TEST(locale_test, complex) {
std::string s = fmt::format("{}", std::complex<double>(1, 2));
EXPECT_EQ(s, "(1+2i)");
EXPECT_EQ(fmt::format("{:.2f}", std::complex<double>(1, 2)), "(1.00+2.00i)");
EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)");
}
TEST(locale_test, chrono_weekday) {
auto loc = get_locale("es_ES.UTF-8", "Spanish_Spain.1252");
auto loc_old = std::locale::global(loc);
auto sat = fmt::weekday(6);
EXPECT_EQ(fmt::format(L"{}", sat), L"Sat");
if (loc != std::locale::classic()) {
// L'\xE1' is 'á'.
auto saturdays = std::vector<std::wstring>{
L"s\xE1"
"b",
L"s\xE1."};
// L'\341' is 'á'.
auto saturdays =
std::vector<std::wstring>{L"s\341b", L"s\341.", L"s\341b."};
EXPECT_THAT(saturdays, Contains(fmt::format(loc, L"{:L}", sat)));
}
std::locale::global(loc_old);
@@ -625,6 +490,14 @@ TEST(locale_test, sign) {
EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50");
}
TEST(std_test_xchar, complex) {
auto s = fmt::format(L"{}", std::complex<double>(1, 2));
EXPECT_EQ(s, L"(1+2i)");
EXPECT_EQ(fmt::format(L"{:.2f}", std::complex<double>(1, 2)),
L"(1.00+2.00i)");
EXPECT_EQ(fmt::format(L"{:8}", std::complex<double>(1, 2)), L"(1+2i) ");
}
TEST(std_test_xchar, optional) {
# ifdef __cpp_lib_optional
EXPECT_EQ(fmt::format(L"{}", std::optional{L'C'}), L"optional(\'C\')");