mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2026-11-15 00:07:08 +01:00
Update WIP discord and some vendors.
CPR has features disabled and PCRE is fully disabled until updated to new code.
This commit is contained in:
1 parent
e0761bf3b9
commit
9298065cef
1562 files changed
+55070
-76299
No files matched your search
Vendored
+29
-13
@@ -1,18 +1,15 @@
|
||||
add_subdirectory(gtest)
|
||||
|
||||
include(CheckSymbolExists)
|
||||
|
||||
set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
|
||||
add_library(test-main STATIC ${TEST_MAIN_SRC})
|
||||
target_include_directories(test-main PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
|
||||
target_link_libraries(test-main gtest fmt)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
function(add_fmt_executable name)
|
||||
add_executable(${name} ${ARGN})
|
||||
if (MINGW)
|
||||
target_link_libraries(${name} -static-libgcc -static-libstdc++)
|
||||
endif ()
|
||||
# (Wstringop-overflow) - [meta-bug] bogus/missing -Wstringop-overflow warnings
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88443
|
||||
# Bogus -Wstringop-overflow warning
|
||||
@@ -21,6 +18,8 @@ function(add_fmt_executable name)
|
||||
# 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()
|
||||
@@ -38,7 +37,7 @@ function(add_fmt_test name)
|
||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-weak-vtables)
|
||||
endif ()
|
||||
elseif (ADD_FMT_TEST_MODULE)
|
||||
set(libs gtest test-module)
|
||||
set(libs test-main test-module)
|
||||
set_source_files_properties(${name}.cc PROPERTIES OBJECT_DEPENDS test-module)
|
||||
else ()
|
||||
set(libs test-main fmt)
|
||||
@@ -56,6 +55,10 @@ function(add_fmt_test name)
|
||||
add_test(NAME ${name} COMMAND ${name})
|
||||
endfunction()
|
||||
|
||||
if (FMT_MODULE)
|
||||
return ()
|
||||
endif ()
|
||||
|
||||
add_fmt_test(args-test)
|
||||
add_fmt_test(assert-test)
|
||||
add_fmt_test(chrono-test)
|
||||
@@ -78,7 +81,22 @@ if (MSVC)
|
||||
endif()
|
||||
add_fmt_test(printf-test)
|
||||
add_fmt_test(ranges-test ranges-odr-test.cc)
|
||||
|
||||
add_fmt_test(scan-test)
|
||||
check_symbol_exists(strptime "time.h" HAVE_STRPTIME)
|
||||
if (HAVE_STRPTIME)
|
||||
target_compile_definitions(scan-test PRIVATE FMT_HAVE_STRPTIME)
|
||||
endif ()
|
||||
|
||||
add_fmt_test(std-test)
|
||||
try_compile(compile_result_unused
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
SOURCES ${CMAKE_CURRENT_LIST_DIR}/detect-stdfs.cc
|
||||
OUTPUT_VARIABLE RAWOUTPUT)
|
||||
string(REGEX REPLACE ".*libfound \"([^\"]*)\".*" "\\1" STDLIBFS "${RAWOUTPUT}")
|
||||
if (STDLIBFS)
|
||||
target_link_libraries(std-test ${STDLIBFS})
|
||||
endif ()
|
||||
add_fmt_test(unicode-test HEADER_ONLY)
|
||||
if (MSVC)
|
||||
target_compile_options(unicode-test PRIVATE /utf-8)
|
||||
@@ -88,13 +106,13 @@ add_fmt_test(enforce-checks-test)
|
||||
target_compile_definitions(enforce-checks-test PRIVATE
|
||||
-DFMT_ENFORCE_COMPILE_STRING)
|
||||
|
||||
if (FMT_CAN_MODULE)
|
||||
if (FMT_MODULE)
|
||||
# The tests need {fmt} to be compiled as traditional library
|
||||
# because of visibility of implementation details.
|
||||
# If module support is present the module tests require a
|
||||
# test-only module to be built from {fmt}
|
||||
add_library(test-module OBJECT ${CMAKE_SOURCE_DIR}/src/fmt.cc)
|
||||
target_compile_features(test-module PUBLIC ${FMT_REQUIRED_FEATURES})
|
||||
target_compile_features(test-module PUBLIC cxx_std_11)
|
||||
target_include_directories(test-module PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
|
||||
enable_module(test-module)
|
||||
@@ -145,9 +163,7 @@ if (FMT_PEDANTIC)
|
||||
target_include_directories(
|
||||
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_options(noexception-test PRIVATE -fno-exceptions)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
|
||||
# Test that the library compiles without locale.
|
||||
@@ -171,8 +187,8 @@ if (FMT_PEDANTIC AND NOT WIN32)
|
||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
|
||||
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
|
||||
"-DFMT_DIR=${CMAKE_SOURCE_DIR}"
|
||||
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
|
||||
"-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}"
|
||||
"-DFMT_DIR=${CMAKE_SOURCE_DIR}")
|
||||
|
||||
# Test if the targets are found from the build directory.
|
||||
add_test(find-package-test ${CMAKE_CTEST_COMMAND}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
cmake_minimum_required(VERSION 3.8...3.25)
|
||||
|
||||
project(fmt-test CXX)
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -44,7 +44,7 @@ template <> struct formatter<custom_type> {
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const custom_type& p, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
return format_to(ctx.out(), "cust={}", p.i);
|
||||
return fmt::format_to(ctx.out(), "cust={}", p.i);
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
Vendored
+371
-24
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "fmt/chrono.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
|
||||
@@ -14,9 +15,15 @@
|
||||
#include "util.h" // get_locale
|
||||
|
||||
using fmt::runtime;
|
||||
|
||||
using testing::Contains;
|
||||
|
||||
#if defined(__MINGW32__) && !defined(_UCRT)
|
||||
// Only C89 conversion specifiers when using MSVCRT instead of UCRT
|
||||
# define FMT_HAS_C99_STRFTIME 0
|
||||
#else
|
||||
# define FMT_HAS_C99_STRFTIME 1
|
||||
#endif
|
||||
|
||||
auto make_tm() -> std::tm {
|
||||
auto time = std::tm();
|
||||
time.tm_mday = 1;
|
||||
@@ -121,6 +128,13 @@ TEST(chrono_test, format_tm) {
|
||||
make_tm(2000, 1, 2, 12, 14, 16), // W52
|
||||
make_tm(2000, 1, 3, 12, 14, 16) // W1
|
||||
};
|
||||
|
||||
#if !FMT_HAS_C99_STRFTIME
|
||||
GTEST_SKIP() << "Skip the rest of this test because it relies on strftime() "
|
||||
"conforming to C99, but on this platform, MINGW + MSVCRT, "
|
||||
"the function conforms only to C89.";
|
||||
#endif
|
||||
|
||||
const std::string iso_week_spec = "%Y-%m-%d: %G %g %V";
|
||||
for (auto ctm : tm_list) {
|
||||
// Calculate tm_yday, tm_wday, etc.
|
||||
@@ -227,32 +241,135 @@ auto equal(const std::tm& lhs, const std::tm& rhs) -> bool {
|
||||
lhs.tm_isdst == rhs.tm_isdst;
|
||||
}
|
||||
|
||||
TEST(chrono_test, localtime) {
|
||||
auto t = std::time(nullptr);
|
||||
auto tm = *std::localtime(&t);
|
||||
EXPECT_TRUE(equal(tm, fmt::localtime(t)));
|
||||
}
|
||||
|
||||
TEST(chrono_test, gmtime) {
|
||||
auto t = std::time(nullptr);
|
||||
auto tm = *std::gmtime(&t);
|
||||
EXPECT_TRUE(equal(tm, fmt::gmtime(t)));
|
||||
}
|
||||
|
||||
template <typename TimePoint> auto strftime_full(TimePoint tp) -> std::string {
|
||||
template <typename TimePoint>
|
||||
auto strftime_full_utc(TimePoint tp) -> std::string {
|
||||
auto t = std::chrono::system_clock::to_time_t(tp);
|
||||
auto tm = *std::gmtime(&t);
|
||||
return system_strftime("%Y-%m-%d %H:%M:%S", &tm);
|
||||
}
|
||||
|
||||
TEST(chrono_test, system_clock_time_point) {
|
||||
auto t1 = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now());
|
||||
EXPECT_EQ(strftime_full_utc(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
|
||||
EXPECT_EQ(strftime_full_utc(t1), fmt::format("{}", t1));
|
||||
EXPECT_EQ(strftime_full_utc(t1), fmt::format("{:}", t1));
|
||||
using time_point =
|
||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
|
||||
auto t2 = time_point(std::chrono::seconds(42));
|
||||
EXPECT_EQ(strftime_full_utc(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2));
|
||||
|
||||
std::vector<std::string> spec_list = {
|
||||
"%%", "%n", "%t", "%Y", "%EY", "%y", "%Oy", "%Ey", "%C",
|
||||
"%EC", "%G", "%g", "%b", "%h", "%B", "%m", "%Om", "%U",
|
||||
"%OU", "%W", "%OW", "%V", "%OV", "%j", "%d", "%Od", "%e",
|
||||
"%Oe", "%a", "%A", "%w", "%Ow", "%u", "%Ou", "%H", "%OH",
|
||||
"%I", "%OI", "%M", "%OM", "%S", "%OS", "%x", "%Ex", "%X",
|
||||
"%EX", "%D", "%F", "%R", "%T", "%p"};
|
||||
#ifndef _WIN32
|
||||
// Disabled on Windows because these formats are not consistent among
|
||||
// platforms.
|
||||
spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"});
|
||||
#elif !FMT_HAS_C99_STRFTIME
|
||||
// Only C89 conversion specifiers when using MSVCRT instead of UCRT
|
||||
spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d",
|
||||
"%a", "%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p"};
|
||||
#endif
|
||||
spec_list.push_back("%Y-%m-%d %H:%M:%S");
|
||||
|
||||
for (const auto& spec : spec_list) {
|
||||
auto t = std::chrono::system_clock::to_time_t(t1);
|
||||
auto tm = *std::gmtime(&t);
|
||||
|
||||
auto sys_output = system_strftime(spec, &tm);
|
||||
|
||||
auto fmt_spec = fmt::format("{{:{}}}", spec);
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1));
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
|
||||
}
|
||||
|
||||
// Timezone formatters tests makes sense for localtime.
|
||||
#if FMT_HAS_C99_STRFTIME
|
||||
spec_list = {"%z", "%Z"};
|
||||
#else
|
||||
spec_list = {"%Z"};
|
||||
#endif
|
||||
for (const auto& spec : spec_list) {
|
||||
auto t = std::chrono::system_clock::to_time_t(t1);
|
||||
auto tm = *std::localtime(&t);
|
||||
|
||||
auto sys_output = system_strftime(spec, &tm);
|
||||
|
||||
auto fmt_spec = fmt::format("{{:{}}}", spec);
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
|
||||
|
||||
if (spec == "%z") {
|
||||
sys_output.insert(sys_output.end() - 2, 1, ':');
|
||||
EXPECT_EQ(sys_output, fmt::format("{:%Ez}", tm));
|
||||
EXPECT_EQ(sys_output, fmt::format("{:%Oz}", tm));
|
||||
}
|
||||
}
|
||||
|
||||
// Separate tests for UTC, since std::time_put can use local time and ignoring
|
||||
// the timezone in std::tm (if it presents on platform).
|
||||
if (fmt::detail::has_member_data_tm_zone<std::tm>::value) {
|
||||
auto t = std::chrono::system_clock::to_time_t(t1);
|
||||
auto tm = *std::gmtime(&t);
|
||||
|
||||
std::vector<std::string> tz_names = {"GMT", "UTC"};
|
||||
EXPECT_THAT(tz_names, Contains(fmt::format("{:%Z}", t1)));
|
||||
EXPECT_THAT(tz_names, Contains(fmt::format("{:%Z}", tm)));
|
||||
}
|
||||
|
||||
if (fmt::detail::has_member_data_tm_gmtoff<std::tm>::value) {
|
||||
auto t = std::chrono::system_clock::to_time_t(t1);
|
||||
auto tm = *std::gmtime(&t);
|
||||
|
||||
EXPECT_EQ("+0000", fmt::format("{:%z}", t1));
|
||||
EXPECT_EQ("+0000", fmt::format("{:%z}", tm));
|
||||
|
||||
EXPECT_EQ("+00:00", fmt::format("{:%Ez}", t1));
|
||||
EXPECT_EQ("+00:00", fmt::format("{:%Ez}", tm));
|
||||
|
||||
EXPECT_EQ("+00:00", fmt::format("{:%Oz}", t1));
|
||||
EXPECT_EQ("+00:00", fmt::format("{:%Oz}", tm));
|
||||
}
|
||||
}
|
||||
|
||||
#if FMT_USE_LOCAL_TIME
|
||||
|
||||
TEST(chrono_test, localtime) {
|
||||
auto t = std::time(nullptr);
|
||||
auto tm = *std::localtime(&t);
|
||||
EXPECT_TRUE(equal(tm, fmt::localtime(t)));
|
||||
}
|
||||
|
||||
template <typename Duration>
|
||||
auto strftime_full_local(std::chrono::local_time<Duration> tp) -> std::string {
|
||||
auto t = std::chrono::system_clock::to_time_t(
|
||||
std::chrono::current_zone()->to_sys(tp));
|
||||
auto tm = *std::localtime(&t);
|
||||
return system_strftime("%Y-%m-%d %H:%M:%S", &tm);
|
||||
}
|
||||
|
||||
TEST(chrono_test, time_point) {
|
||||
auto t1 = std::chrono::system_clock::now();
|
||||
EXPECT_EQ(strftime_full(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
|
||||
EXPECT_EQ(strftime_full(t1), fmt::format("{}", t1));
|
||||
using time_point =
|
||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
|
||||
auto t2 = time_point(std::chrono::seconds(42));
|
||||
EXPECT_EQ(strftime_full(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2));
|
||||
TEST(chrono_test, local_system_clock_time_point) {
|
||||
# ifdef _WIN32
|
||||
return; // Not supported on Windows.
|
||||
# endif
|
||||
auto t1 = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
std::chrono::current_zone()->to_local(std::chrono::system_clock::now()));
|
||||
EXPECT_EQ(strftime_full_local(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
|
||||
EXPECT_EQ(strftime_full_local(t1), fmt::format("{}", t1));
|
||||
EXPECT_EQ(strftime_full_local(t1), fmt::format("{:}", t1));
|
||||
using time_point = std::chrono::local_time<std::chrono::seconds>;
|
||||
auto t2 = time_point(std::chrono::seconds(86400 + 42));
|
||||
EXPECT_EQ(strftime_full_local(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2));
|
||||
|
||||
std::vector<std::string> spec_list = {
|
||||
"%%", "%n", "%t", "%Y", "%EY", "%y", "%Oy", "%Ey", "%C",
|
||||
@@ -261,15 +378,20 @@ TEST(chrono_test, time_point) {
|
||||
"%Oe", "%a", "%A", "%w", "%Ow", "%u", "%Ou", "%H", "%OH",
|
||||
"%I", "%OI", "%M", "%OM", "%S", "%OS", "%x", "%Ex", "%X",
|
||||
"%EX", "%D", "%F", "%R", "%T", "%p", "%z", "%Z"};
|
||||
spec_list.push_back("%Y-%m-%d %H:%M:%S");
|
||||
#ifndef _WIN32
|
||||
# ifndef _WIN32
|
||||
// Disabled on Windows because these formats are not consistent among
|
||||
// platforms.
|
||||
spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"});
|
||||
#endif
|
||||
# elif !FMT_HAS_C99_STRFTIME
|
||||
// Only C89 conversion specifiers when using MSVCRT instead of UCRT
|
||||
spec_list = {"%%", "%Y", "%y", "%b", "%B", "%m", "%U", "%W", "%j", "%d", "%a",
|
||||
"%A", "%w", "%H", "%I", "%M", "%S", "%x", "%X", "%p", "%Z"};
|
||||
# endif
|
||||
spec_list.push_back("%Y-%m-%d %H:%M:%S");
|
||||
|
||||
for (const auto& spec : spec_list) {
|
||||
auto t = std::chrono::system_clock::to_time_t(t1);
|
||||
auto t = std::chrono::system_clock::to_time_t(
|
||||
std::chrono::current_zone()->to_sys(t1));
|
||||
auto tm = *std::localtime(&t);
|
||||
|
||||
auto sys_output = system_strftime(spec, &tm);
|
||||
@@ -278,8 +400,26 @@ TEST(chrono_test, time_point) {
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1));
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
|
||||
}
|
||||
|
||||
if (std::find(spec_list.cbegin(), spec_list.cend(), "%z") !=
|
||||
spec_list.cend()) {
|
||||
auto t = std::chrono::system_clock::to_time_t(
|
||||
std::chrono::current_zone()->to_sys(t1));
|
||||
auto tm = *std::localtime(&t);
|
||||
|
||||
auto sys_output = system_strftime("%z", &tm);
|
||||
sys_output.insert(sys_output.end() - 2, 1, ':');
|
||||
|
||||
EXPECT_EQ(sys_output, fmt::format("{:%Ez}", t1));
|
||||
EXPECT_EQ(sys_output, fmt::format("{:%Ez}", tm));
|
||||
|
||||
EXPECT_EQ(sys_output, fmt::format("{:%Oz}", t1));
|
||||
EXPECT_EQ(sys_output, fmt::format("{:%Oz}", tm));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // FMT_USE_LOCAL_TIME
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
|
||||
TEST(chrono_test, format_default) {
|
||||
@@ -324,7 +464,7 @@ TEST(chrono_test, format_default) {
|
||||
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
|
||||
}
|
||||
|
||||
TEST(chrono_test, align) {
|
||||
TEST(chrono_test, duration_align) {
|
||||
auto s = std::chrono::seconds(42);
|
||||
EXPECT_EQ("42s ", fmt::format("{:5}", s));
|
||||
EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5));
|
||||
@@ -340,6 +480,35 @@ TEST(chrono_test, align) {
|
||||
fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12));
|
||||
}
|
||||
|
||||
TEST(chrono_test, tm_align) {
|
||||
auto t = make_tm(1975, 12, 29, 12, 14, 16);
|
||||
EXPECT_EQ("1975-12-29 12:14:16", fmt::format("{:%F %T}", t));
|
||||
EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:30%F %T}", t));
|
||||
EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:{}%F %T}", t, 30));
|
||||
EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:<30%F %T}", t));
|
||||
EXPECT_EQ(" 1975-12-29 12:14:16 ", fmt::format("{:^30%F %T}", t));
|
||||
EXPECT_EQ(" 1975-12-29 12:14:16", fmt::format("{:>30%F %T}", t));
|
||||
|
||||
EXPECT_EQ("1975-12-29 12:14:16***********", fmt::format("{:*<30%F %T}", t));
|
||||
EXPECT_EQ("*****1975-12-29 12:14:16******", fmt::format("{:*^30%F %T}", t));
|
||||
EXPECT_EQ("***********1975-12-29 12:14:16", fmt::format("{:*>30%F %T}", t));
|
||||
}
|
||||
|
||||
TEST(chrono_test, tp_align) {
|
||||
auto tp = std::chrono::time_point_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::from_time_t(0));
|
||||
EXPECT_EQ("00:00.000000", fmt::format("{:%M:%S}", tp));
|
||||
EXPECT_EQ("00:00.000000 ", fmt::format("{:15%M:%S}", tp));
|
||||
EXPECT_EQ("00:00.000000 ", fmt::format("{:{}%M:%S}", tp, 15));
|
||||
EXPECT_EQ("00:00.000000 ", fmt::format("{:<15%M:%S}", tp));
|
||||
EXPECT_EQ(" 00:00.000000 ", fmt::format("{:^15%M:%S}", tp));
|
||||
EXPECT_EQ(" 00:00.000000", fmt::format("{:>15%M:%S}", tp));
|
||||
|
||||
EXPECT_EQ("00:00.000000***", fmt::format("{:*<15%M:%S}", tp));
|
||||
EXPECT_EQ("*00:00.000000**", fmt::format("{:*^15%M:%S}", tp));
|
||||
EXPECT_EQ("***00:00.000000", fmt::format("{:*>15%M:%S}", tp));
|
||||
}
|
||||
|
||||
TEST(chrono_test, format_specs) {
|
||||
EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0)));
|
||||
EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0)));
|
||||
@@ -407,6 +576,10 @@ TEST(chrono_test, invalid_specs) {
|
||||
"invalid format");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Oq}"), sec), fmt::format_error,
|
||||
"invalid format");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:abc}"), sec), fmt::format_error,
|
||||
"invalid format");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.2f}"), sec), fmt::format_error,
|
||||
"invalid format");
|
||||
}
|
||||
|
||||
auto format_tm(const std::tm& time, fmt::string_view spec,
|
||||
@@ -454,7 +627,7 @@ TEST(chrono_test, format_default_fp) {
|
||||
|
||||
TEST(chrono_test, format_precision) {
|
||||
EXPECT_THROW_MSG(
|
||||
(void)fmt::format(runtime("{:.2}"), std::chrono::seconds(42)),
|
||||
(void)fmt::format(runtime("{:.2%Q}"), std::chrono::seconds(42)),
|
||||
fmt::format_error, "precision not allowed for this argument type");
|
||||
EXPECT_EQ("1ms", fmt::format("{:.0}", dms(1.234)));
|
||||
EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234)));
|
||||
@@ -599,12 +772,28 @@ TEST(chrono_test, cpp20_duration_subsecond_support) {
|
||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{-13420148734}),
|
||||
"-13.420148734");
|
||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234");
|
||||
// Check subsecond presision modifier.
|
||||
EXPECT_EQ(fmt::format("{:.6%S}", std::chrono::nanoseconds{1234}),
|
||||
"00.000001");
|
||||
EXPECT_EQ(fmt::format("{:.18%S}", std::chrono::nanoseconds{1234}),
|
||||
"00.000001234000000000");
|
||||
EXPECT_EQ(fmt::format("{:.{}%S}", std::chrono::nanoseconds{1234}, 6),
|
||||
"00.000001");
|
||||
EXPECT_EQ(fmt::format("{:.6%S}", std::chrono::milliseconds{1234}),
|
||||
"01.234000");
|
||||
EXPECT_EQ(fmt::format("{:.6%S}", std::chrono::milliseconds{-1234}),
|
||||
"-01.234000");
|
||||
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");
|
||||
EXPECT_EQ(fmt::format("{:.8%S}", dms(1.234)), "00.00123400");
|
||||
{
|
||||
// Check that {:%H:%M:%S} is equivalent to {:%T}.
|
||||
auto dur = std::chrono::milliseconds{3601234};
|
||||
auto formatted_dur = fmt::format("{:%T}", dur);
|
||||
EXPECT_EQ(formatted_dur, "01:00:01.234");
|
||||
EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur);
|
||||
EXPECT_EQ(fmt::format("{:.6%H:%M:%S}", dur), "01:00:01.234000");
|
||||
}
|
||||
using nanoseconds_dbl = std::chrono::duration<double, std::nano>;
|
||||
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{-123456789}), "-00.123456789");
|
||||
@@ -618,15 +807,173 @@ TEST(chrono_test, cpp20_duration_subsecond_support) {
|
||||
auto formatted_dur = fmt::format("{:%T}", dur);
|
||||
EXPECT_EQ(formatted_dur, "-00:01:39.123456789");
|
||||
EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur);
|
||||
EXPECT_EQ(fmt::format("{:.3%H:%M:%S}", dur), "-00:01:39.123");
|
||||
}
|
||||
// Check that durations with precision greater than std::chrono::seconds have
|
||||
// fixed precision, and print zeros even if there is no fractional part.
|
||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}),
|
||||
"07.000000");
|
||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 3>>(1)),
|
||||
EXPECT_EQ(fmt::format("{:%S}",
|
||||
std::chrono::duration<long long, std::ratio<1, 3>>(1)),
|
||||
"00.333333");
|
||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 7>>(1)),
|
||||
EXPECT_EQ(fmt::format("{:%S}",
|
||||
std::chrono::duration<long long, std::ratio<1, 7>>(1)),
|
||||
"00.142857");
|
||||
|
||||
EXPECT_EQ(
|
||||
fmt::format("{:%S}",
|
||||
std::chrono::duration<signed char, std::ratio<1, 100>>(0x80)),
|
||||
"-01.28");
|
||||
|
||||
EXPECT_EQ(
|
||||
fmt::format("{:%M:%S}",
|
||||
std::chrono::duration<short, std::ratio<1, 100>>(0x8000)),
|
||||
"-05:27.68");
|
||||
|
||||
// Check that floating point seconds with ratio<1,1> are printed.
|
||||
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<double>{1.5}),
|
||||
"01.500000");
|
||||
EXPECT_EQ(fmt::format("{:%M:%S}", std::chrono::duration<double>{-61.25}),
|
||||
"-01:01.250000");
|
||||
}
|
||||
|
||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
|
||||
// Disable the utc_clock test for windows, as the icu.dll used for tzdb
|
||||
// (time zone database) is not shipped with many windows versions.
|
||||
#if FMT_USE_UTC_TIME && !defined(_WIN32)
|
||||
TEST(chrono_test, utc_clock) {
|
||||
auto t1 = std::chrono::system_clock::now();
|
||||
auto t1_utc = std::chrono::utc_clock::from_sys(t1);
|
||||
EXPECT_EQ(fmt::format("{:%Y-%m-%d %H:%M:%S}", t1),
|
||||
fmt::format("{:%Y-%m-%d %H:%M:%S}", t1_utc));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(chrono_test, timestamps_sub_seconds) {
|
||||
std::chrono::time_point<std::chrono::system_clock,
|
||||
std::chrono::duration<long long, std::ratio<1, 3>>>
|
||||
t1(std::chrono::duration<long long, std::ratio<1, 3>>(4));
|
||||
|
||||
EXPECT_EQ(fmt::format("{:%S}", t1), "01.333333");
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock,
|
||||
std::chrono::duration<double, std::ratio<1, 3>>>
|
||||
t2(std::chrono::duration<double, std::ratio<1, 3>>(4));
|
||||
|
||||
EXPECT_EQ(fmt::format("{:%S}", t2), "01.333333");
|
||||
|
||||
const std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>
|
||||
t3(std::chrono::seconds(2));
|
||||
|
||||
EXPECT_EQ(fmt::format("{:%S}", t3), "02");
|
||||
|
||||
const std::chrono::time_point<std::chrono::system_clock,
|
||||
std::chrono::duration<double>>
|
||||
t4(std::chrono::duration<double, std::ratio<1, 1>>(9.5));
|
||||
|
||||
EXPECT_EQ(fmt::format("{:%S}", t4), "09.500000");
|
||||
|
||||
const std::chrono::time_point<std::chrono::system_clock,
|
||||
std::chrono::duration<double>>
|
||||
t5(std::chrono::duration<double, std::ratio<1, 1>>(9));
|
||||
|
||||
EXPECT_EQ(fmt::format("{:%S}", t5), "09");
|
||||
|
||||
const std::chrono::time_point<std::chrono::system_clock,
|
||||
std::chrono::milliseconds>
|
||||
t6(std::chrono::seconds(1) + std::chrono::milliseconds(120));
|
||||
|
||||
EXPECT_EQ(fmt::format("{:%S}", t6), "01.120");
|
||||
|
||||
const std::chrono::time_point<std::chrono::system_clock,
|
||||
std::chrono::microseconds>
|
||||
t7(std::chrono::microseconds(1234567));
|
||||
|
||||
EXPECT_EQ(fmt::format("{:%S}", t7), "01.234567");
|
||||
|
||||
const std::chrono::time_point<std::chrono::system_clock,
|
||||
std::chrono::nanoseconds>
|
||||
t8(std::chrono::nanoseconds(123456789));
|
||||
|
||||
EXPECT_EQ(fmt::format("{:%S}", t8), "00.123456789");
|
||||
|
||||
const auto t9 = std::chrono::time_point_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now());
|
||||
const auto t9_sec = std::chrono::time_point_cast<std::chrono::seconds>(t9);
|
||||
auto t9_sub_sec_part = fmt::format("{0:09}", (t9 - t9_sec).count());
|
||||
|
||||
EXPECT_EQ(fmt::format("{}.{}", strftime_full_utc(t9_sec), t9_sub_sec_part),
|
||||
fmt::format("{:%Y-%m-%d %H:%M:%S}", t9));
|
||||
EXPECT_EQ(fmt::format("{}.{}", strftime_full_utc(t9_sec), t9_sub_sec_part),
|
||||
fmt::format("{:%Y-%m-%d %T}", t9));
|
||||
|
||||
const std::chrono::time_point<std::chrono::system_clock,
|
||||
std::chrono::milliseconds>
|
||||
t10(std::chrono::milliseconds(2000));
|
||||
|
||||
EXPECT_EQ(fmt::format("{:%S}", t10), "02.000");
|
||||
|
||||
{
|
||||
const auto epoch = std::chrono::time_point<std::chrono::system_clock,
|
||||
std::chrono::milliseconds>();
|
||||
const auto d = std::chrono::milliseconds(250);
|
||||
|
||||
EXPECT_EQ("59.750", fmt::format("{:%S}", epoch - d));
|
||||
EXPECT_EQ("00.000", fmt::format("{:%S}", epoch));
|
||||
EXPECT_EQ("00.250", fmt::format("{:%S}", epoch + d));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(chrono_test, glibc_extensions) {
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%0}"), std::chrono::seconds()),
|
||||
fmt::format_error, "invalid format");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%_}"), std::chrono::seconds()),
|
||||
fmt::format_error, "invalid format");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%-}"), std::chrono::seconds()),
|
||||
fmt::format_error, "invalid format");
|
||||
|
||||
{
|
||||
const auto d = std::chrono::hours(1) + std::chrono::minutes(2) +
|
||||
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("{:%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");
|
||||
}
|
||||
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
{
|
||||
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);
|
||||
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");
|
||||
}
|
||||
}
|
||||
+42
-4
@@ -1,11 +1,13 @@
|
||||
# Test if compile errors are produced where necessary.
|
||||
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
cmake_minimum_required(VERSION 3.8...3.25)
|
||||
project(compile-error-test CXX)
|
||||
|
||||
set(fmt_headers "
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/xchar.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <iostream>
|
||||
")
|
||||
|
||||
set(error_test_names "")
|
||||
@@ -62,7 +64,7 @@ function (run_tests)
|
||||
")
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/CMakeLists.txt" "
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
cmake_minimum_required(VERSION 3.8...3.25)
|
||||
project(tests CXX)
|
||||
add_subdirectory(${FMT_DIR} fmt)
|
||||
${cmake_targets}
|
||||
@@ -154,6 +156,28 @@ expect_compile(format-function-error "
|
||||
fmt::format(\"{}\", f);
|
||||
" ERROR)
|
||||
|
||||
# Formatting an unformattable argument should always be a compile time error
|
||||
expect_compile(format-lots-of-arguments-with-unformattable "
|
||||
struct E {};
|
||||
fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, E());
|
||||
" ERROR)
|
||||
expect_compile(format-lots-of-arguments-with-function "
|
||||
void (*f)();
|
||||
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)
|
||||
@@ -181,16 +205,30 @@ if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
|
||||
#error
|
||||
#endif
|
||||
" ERROR)
|
||||
expect_compile(print-string-number-spec-error "
|
||||
#ifdef FMT_HAS_CONSTEVAL
|
||||
fmt::print(\"{:d}\", \"I am not a number\");
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
" ERROR)
|
||||
expect_compile(print-stream-string-number-spec-error "
|
||||
#ifdef FMT_HAS_CONSTEVAL
|
||||
fmt::print(std::cout, \"{:d}\", \"I am not a number\");
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
" ERROR)
|
||||
|
||||
# Compile-time argument name check
|
||||
expect_compile(format-string-name "
|
||||
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
using namespace fmt::literals;
|
||||
fmt::print(\"{foo}\", \"foo\"_a=42);
|
||||
#endif
|
||||
")
|
||||
expect_compile(format-string-name-error "
|
||||
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
using namespace fmt::literals;
|
||||
fmt::print(\"{foo}\", \"bar\"_a=42);
|
||||
#else
|
||||
|
||||
Vendored
+1
-1
@@ -11,7 +11,7 @@
|
||||
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 && \
|
||||
defined(__cpp_constexpr) && __cpp_constexpr >= 201907 && \
|
||||
defined(__cpp_constexpr_dynamic_alloc) && \
|
||||
__cpp_constexpr_dynamic_alloc >= 201907 && __cplusplus >= 202002L
|
||||
__cpp_constexpr_dynamic_alloc >= 201907 && FMT_CPLUSPLUS >= 202002L
|
||||
template <size_t max_string_length, typename Char = char> struct test_string {
|
||||
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
|
||||
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
|
||||
|
||||
Vendored
+19
-7
@@ -187,7 +187,7 @@ TEST(compile_test, named) {
|
||||
EXPECT_THROW(fmt::format(FMT_COMPILE("{invalid}"), fmt::arg("valid", 42)),
|
||||
fmt::format_error);
|
||||
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
using namespace fmt::literals;
|
||||
auto statically_named_field_compiled =
|
||||
fmt::detail::compile<decltype("arg"_a = 42)>(FMT_COMPILE("{arg}"));
|
||||
@@ -227,10 +227,14 @@ TEST(compile_test, format_to_n) {
|
||||
EXPECT_STREQ("2a", buffer);
|
||||
}
|
||||
|
||||
TEST(compile_test, formatted_size) {
|
||||
EXPECT_EQ(2, fmt::formatted_size(FMT_COMPILE("{0}"), 42));
|
||||
EXPECT_EQ(5, fmt::formatted_size(FMT_COMPILE("{0:<4.2f}"), 42.0));
|
||||
#ifdef __cpp_lib_bit_cast
|
||||
TEST(compile_test, constexpr_formatted_size) {
|
||||
FMT_CONSTEXPR20 size_t s1 = fmt::formatted_size(FMT_COMPILE("{0}"), 42);
|
||||
EXPECT_EQ(2, s1);
|
||||
FMT_CONSTEXPR20 size_t s2 = fmt::formatted_size(FMT_COMPILE("{0:<4.2f}"), 42.0);
|
||||
EXPECT_EQ(5, s2);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(compile_test, text_and_arg) {
|
||||
EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42));
|
||||
@@ -285,7 +289,7 @@ TEST(compile_test, print) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
TEST(compile_test, compile_format_string_literal) {
|
||||
using namespace fmt::literals;
|
||||
EXPECT_EQ("", fmt::format(""_cf));
|
||||
@@ -294,8 +298,16 @@ TEST(compile_test, compile_format_string_literal) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 202002L || \
|
||||
(__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002)
|
||||
// MSVS 2019 19.29.30145.0 - Support C++20 and OK.
|
||||
// MSVS 2022 19.32.31332.0 - compile-test.cc(362,3): fatal error C1001: Internal
|
||||
// compiler error.
|
||||
// (compiler file
|
||||
// 'D:\a\_work\1\s\src\vctools\Compiler\CxxFE\sl\p1\c\constexpr\constexpr.cpp',
|
||||
// line 8635)
|
||||
#if ((FMT_CPLUSPLUS >= 202002L) && \
|
||||
(!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9) && \
|
||||
(!FMT_MSC_VERSION || FMT_MSC_VERSION < 1930)) || \
|
||||
(FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
|
||||
template <size_t max_string_length, typename Char = char> struct test_string {
|
||||
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
|
||||
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
|
||||
|
||||
Vendored
+100
-231
@@ -70,6 +70,16 @@ TEST(string_view_test, compare) {
|
||||
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);
|
||||
|
||||
EXPECT_TRUE(string_view("foo").starts_with('f'));
|
||||
EXPECT_FALSE(string_view("foo").starts_with('o'));
|
||||
EXPECT_FALSE(string_view().starts_with('o'));
|
||||
|
||||
EXPECT_TRUE(string_view("foo").starts_with("fo"));
|
||||
EXPECT_TRUE(string_view("foo").starts_with("foo"));
|
||||
EXPECT_FALSE(string_view("foo").starts_with("fooo"));
|
||||
EXPECT_FALSE(string_view().starts_with("fooo"));
|
||||
|
||||
check_op<std::equal_to>();
|
||||
check_op<std::not_equal_to>();
|
||||
check_op<std::less>();
|
||||
@@ -78,24 +88,6 @@ TEST(string_view_test, compare) {
|
||||
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));
|
||||
@@ -128,7 +120,7 @@ TEST(core_test, buffer_appender) {
|
||||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
|
||||
TEST(buffer_test, noncopyable) {
|
||||
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
|
||||
# if !FMT_MSC_VER
|
||||
# if !FMT_MSC_VERSION
|
||||
// std::is_copy_assignable is broken in MSVC2013.
|
||||
EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
|
||||
# endif
|
||||
@@ -136,7 +128,7 @@ TEST(buffer_test, noncopyable) {
|
||||
|
||||
TEST(buffer_test, nonmoveable) {
|
||||
EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
|
||||
# if !FMT_MSC_VER
|
||||
# if !FMT_MSC_VERSION
|
||||
// std::is_move_assignable is broken in MSVC2013.
|
||||
EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
|
||||
# endif
|
||||
@@ -149,7 +141,7 @@ TEST(buffer_test, indestructible) {
|
||||
}
|
||||
|
||||
template <typename T> struct mock_buffer final : buffer<T> {
|
||||
MOCK_METHOD1(do_grow, size_t(size_t capacity));
|
||||
MOCK_METHOD(size_t, do_grow, (size_t));
|
||||
|
||||
void grow(size_t capacity) override {
|
||||
this->set(this->data(), do_grow(capacity));
|
||||
@@ -302,7 +294,7 @@ template <typename Char> struct formatter<test_struct, Char> {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
auto format(test_struct, format_context& ctx) -> decltype(ctx.out()) {
|
||||
auto format(test_struct, format_context& ctx) const -> decltype(ctx.out()) {
|
||||
auto test = string_view("test");
|
||||
return std::copy_n(test.data(), test.size(), ctx.out());
|
||||
}
|
||||
@@ -316,7 +308,7 @@ TEST(arg_test, format_args) {
|
||||
|
||||
TEST(arg_test, make_value_with_custom_context) {
|
||||
auto t = test_struct();
|
||||
fmt::detail::value<custom_context> arg(
|
||||
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("");
|
||||
@@ -335,12 +327,12 @@ template <typename T> struct mock_visitor {
|
||||
ON_CALL(*this, visit(_)).WillByDefault(Return(test_result()));
|
||||
}
|
||||
|
||||
MOCK_METHOD1_T(visit, test_result(T value));
|
||||
MOCK_METHOD0_T(unexpected, void());
|
||||
MOCK_METHOD(test_result, visit, (T));
|
||||
MOCK_METHOD(void, unexpected, ());
|
||||
|
||||
test_result operator()(T value) { return visit(value); }
|
||||
auto operator()(T value) -> test_result { return visit(value); }
|
||||
|
||||
template <typename U> test_result operator()(U) {
|
||||
template <typename U> auto operator()(U) -> test_result {
|
||||
unexpected();
|
||||
return test_result();
|
||||
}
|
||||
@@ -385,20 +377,20 @@ VISIT_TYPE(unsigned long, unsigned long long);
|
||||
|
||||
template <typename T> class numeric_arg_test : public testing::Test {};
|
||||
|
||||
using 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>;
|
||||
TYPED_TEST_SUITE(numeric_arg_test, types);
|
||||
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() {
|
||||
auto test_value() -> T {
|
||||
return static_cast<T>(42);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
fmt::enable_if_t<std::is_floating_point<T>::value, int> = 0>
|
||||
T test_value() {
|
||||
auto test_value() -> T {
|
||||
return static_cast<T>(4.2);
|
||||
}
|
||||
|
||||
@@ -441,8 +433,8 @@ TEST(arg_test, pointer_arg) {
|
||||
}
|
||||
|
||||
struct check_custom {
|
||||
test_result operator()(
|
||||
fmt::basic_format_arg<fmt::format_context>::handle h) const {
|
||||
auto operator()(fmt::basic_format_arg<fmt::format_context>::handle h) const
|
||||
-> test_result {
|
||||
struct test_buffer final : fmt::detail::buffer<char> {
|
||||
char data[10];
|
||||
test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
|
||||
@@ -461,13 +453,13 @@ 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;
|
||||
auto&& v = testing::StrictMock<visitor>();
|
||||
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;
|
||||
auto&& visitor = testing::StrictMock<mock_visitor<fmt::monostate>>();
|
||||
EXPECT_CALL(visitor, visit(_));
|
||||
auto arg = fmt::basic_format_arg<fmt::format_context>();
|
||||
fmt::visit_format_arg(visitor, arg);
|
||||
@@ -475,185 +467,78 @@ TEST(arg_test, visit_invalid_arg) {
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
|
||||
enum class arg_id_result { none, empty, index, name, error };
|
||||
enum class arg_id_result { none, empty, index, name };
|
||||
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 on_auto() { res = arg_id_result::empty; }
|
||||
|
||||
constexpr void operator()(int i) {
|
||||
constexpr void on_index(int i) {
|
||||
res = arg_id_result::index;
|
||||
index = i;
|
||||
}
|
||||
|
||||
constexpr void operator()(string_view n) {
|
||||
constexpr void on_name(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;
|
||||
auto h = test_arg_id_handler();
|
||||
fmt::detail::parse_arg_id(s, s + N, h);
|
||||
return h;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_parse_arg_id) {
|
||||
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, "");
|
||||
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;
|
||||
template <size_t N> constexpr auto parse_test_specs(const char (&s)[N]) {
|
||||
auto ctx = fmt::detail::compile_parse_context<char>(fmt::string_view(s, N),
|
||||
43, nullptr);
|
||||
auto specs = fmt::detail::dynamic_format_specs<>();
|
||||
fmt::detail::parse_format_specs(s, s + N - 1, specs, ctx,
|
||||
fmt::detail::type::float_type);
|
||||
return specs;
|
||||
}
|
||||
|
||||
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("<").align == fmt::align::left, "");
|
||||
static_assert(parse_test_specs("*^").fill[0] == '*', "");
|
||||
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("#").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").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, "");
|
||||
static_assert(
|
||||
parse_test_specs("f").type == fmt::presentation_type::fixed_lower, "");
|
||||
}
|
||||
|
||||
struct test_format_string_handler {
|
||||
constexpr void on_text(const char*, const char*) {}
|
||||
|
||||
constexpr int on_arg_id() { return 0; }
|
||||
constexpr auto on_arg_id() -> int { return 0; }
|
||||
|
||||
template <typename T> constexpr int on_arg_id(T) { return 0; }
|
||||
template <typename T> constexpr auto on_arg_id(T) -> int { return 0; }
|
||||
|
||||
constexpr void on_replacement_field(int, const char*) {}
|
||||
|
||||
constexpr const char* on_format_specs(int, const char* begin, const char*) {
|
||||
constexpr auto on_format_specs(int, const char* begin, const char*) -> const
|
||||
char* {
|
||||
return begin;
|
||||
}
|
||||
|
||||
@@ -668,7 +553,7 @@ template <size_t N> constexpr bool parse_string(const char (&s)[N]) {
|
||||
return !h.error;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_parse_format_string) {
|
||||
TEST(core_test, constexpr_parse_format_string) {
|
||||
static_assert(parse_string("foo"), "");
|
||||
static_assert(!parse_string("}"), "");
|
||||
static_assert(parse_string("{}"), "");
|
||||
@@ -679,6 +564,7 @@ TEST(format_test, constexpr_parse_format_string) {
|
||||
#endif // FMT_USE_CONSTEXPR
|
||||
|
||||
struct enabled_formatter {};
|
||||
struct enabled_ptr_formatter {};
|
||||
struct disabled_formatter {};
|
||||
struct disabled_formatter_convertible {
|
||||
operator int() const { return 42; }
|
||||
@@ -689,7 +575,18 @@ 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()) {
|
||||
auto format(enabled_formatter, format_context& ctx) const
|
||||
-> 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) const
|
||||
-> decltype(ctx.out()) {
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
@@ -757,42 +654,32 @@ 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<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, "");
|
||||
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_VER || FMT_MSC_VER >= 1910
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
||||
static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, "");
|
||||
#endif
|
||||
|
||||
@@ -806,27 +693,28 @@ TEST(core_test, is_formattable) {
|
||||
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, "");
|
||||
static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, "");
|
||||
}
|
||||
|
||||
TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); }
|
||||
|
||||
TEST(core_test, format_to) {
|
||||
std::string s;
|
||||
auto s = std::string();
|
||||
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");
|
||||
#ifdef __cpp_lib_byte
|
||||
TEST(core_test, format_byte) {
|
||||
EXPECT_EQ(fmt::format("{}", std::byte(42)), "42");
|
||||
}
|
||||
#endif
|
||||
|
||||
struct convertible_to_int {
|
||||
operator int() const { return 42; }
|
||||
};
|
||||
|
||||
struct convertible_to_c_string {
|
||||
struct convertible_to_cstring {
|
||||
operator const char*() const { return "foo"; }
|
||||
};
|
||||
|
||||
@@ -835,16 +723,17 @@ 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()) {
|
||||
auto format(convertible_to_int, format_context& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
return std::copy_n("foo", 3, ctx.out());
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct formatter<convertible_to_c_string> {
|
||||
template <> struct formatter<convertible_to_cstring> {
|
||||
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
auto format(convertible_to_c_string, format_context& ctx)
|
||||
auto format(convertible_to_cstring, format_context& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
return std::copy_n("bar", 3, ctx.out());
|
||||
}
|
||||
@@ -853,7 +742,7 @@ 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");
|
||||
EXPECT_EQ(fmt::format("{}", convertible_to_cstring()), "bar");
|
||||
}
|
||||
|
||||
// Test that check is not found by ADL.
|
||||
@@ -862,29 +751,28 @@ 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()));
|
||||
TEST(core_test, no_implicit_conversion_to_string_view) {
|
||||
EXPECT_FALSE(
|
||||
fmt::is_formattable<implicitly_convertible_to_string_view>::value);
|
||||
}
|
||||
|
||||
#ifdef FMT_USE_STRING_VIEW
|
||||
struct implicitly_convertible_to_std_string_view {
|
||||
operator std::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(core_test, no_implicit_conversion_to_std_string_view) {
|
||||
EXPECT_FALSE(
|
||||
fmt::is_formattable<implicitly_convertible_to_std_string_view>::value);
|
||||
}
|
||||
#endif
|
||||
|
||||
// std::is_constructible is broken in MSVC until version 2015.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900
|
||||
struct explicitly_convertible_to_string_view {
|
||||
explicit operator fmt::string_view() const { return "foo"; }
|
||||
};
|
||||
@@ -911,25 +799,6 @@ TEST(core_test, format_explicitly_convertible_to_std_string_view) {
|
||||
# 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;
|
||||
|
||||
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
// Formatting library for C++ - tests of formatters for standard library types
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <exception> // _GLIBCXX_RELEASE & _LIBCPP_VERSION
|
||||
|
||||
#if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8
|
||||
# error libfound "stdc++fs"
|
||||
#elif !defined(__apple_build_version__) && defined(_LIBCPP_VERSION) && \
|
||||
_LIBCPP_VERSION >= 7000 && _LIBCPP_VERSION < 9000
|
||||
# error libfound "c++fs"
|
||||
#else
|
||||
// none if std::filesystem does not require additional libraries
|
||||
# error libfound ""
|
||||
#endif
|
||||
+2
@@ -8,12 +8,14 @@
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#define I 42 // simulate https://en.cppreference.com/w/c/numeric/complex/I
|
||||
#include "fmt/chrono.h"
|
||||
#include "fmt/color.h"
|
||||
#include "fmt/format.h"
|
||||
#include "fmt/ostream.h"
|
||||
#include "fmt/ranges.h"
|
||||
#include "fmt/xchar.h"
|
||||
#undef I
|
||||
|
||||
// Exercise the API to verify that everything we expect to can compile.
|
||||
void test_format_api() {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
cmake_minimum_required(VERSION 3.8...3.25)
|
||||
|
||||
project(fmt-test)
|
||||
|
||||
|
||||
Vendored
+270
-57
@@ -24,9 +24,9 @@ static_assert(!std::is_copy_constructible<bigint>::value, "");
|
||||
static_assert(!std::is_copy_assignable<bigint>::value, "");
|
||||
|
||||
TEST(bigint_test, construct) {
|
||||
EXPECT_EQ("", fmt::format("{}", bigint()));
|
||||
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
|
||||
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
|
||||
EXPECT_EQ(fmt::to_string(bigint()), "");
|
||||
EXPECT_EQ(fmt::to_string(bigint(0x42)), "42");
|
||||
EXPECT_EQ(fmt::to_string(bigint(0x123456789abcedf0)), "123456789abcedf0");
|
||||
}
|
||||
|
||||
TEST(bigint_test, compare) {
|
||||
@@ -72,46 +72,56 @@ TEST(bigint_test, add_compare) {
|
||||
TEST(bigint_test, shift_left) {
|
||||
bigint n(0x42);
|
||||
n <<= 0;
|
||||
EXPECT_EQ("42", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "42");
|
||||
n <<= 1;
|
||||
EXPECT_EQ("84", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "84");
|
||||
n <<= 25;
|
||||
EXPECT_EQ("108000000", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "108000000");
|
||||
}
|
||||
|
||||
TEST(bigint_test, multiply) {
|
||||
bigint n(0x42);
|
||||
EXPECT_THROW(n *= 0, assertion_failure);
|
||||
n *= 1;
|
||||
EXPECT_EQ("42", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "42");
|
||||
|
||||
n *= 2;
|
||||
EXPECT_EQ("84", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "84");
|
||||
n *= 0x12345678;
|
||||
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "962fc95e0");
|
||||
|
||||
bigint bigmax(max_value<uint32_t>());
|
||||
bigmax *= max_value<uint32_t>();
|
||||
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
|
||||
bigmax.assign(max_value<uint64_t>());
|
||||
bigmax *= max_value<uint64_t>();
|
||||
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
|
||||
EXPECT_EQ(fmt::to_string(bigmax), "fffffffe00000001");
|
||||
|
||||
const auto max64 = max_value<uint64_t>();
|
||||
bigmax = max64;
|
||||
bigmax *= max64;
|
||||
EXPECT_EQ(fmt::to_string(bigmax), "fffffffffffffffe0000000000000001");
|
||||
|
||||
const auto max128 = (fmt::detail::uint128_t(max64) << 64) | max64;
|
||||
bigmax = max128;
|
||||
bigmax *= max128;
|
||||
EXPECT_EQ(fmt::to_string(bigmax),
|
||||
"fffffffffffffffffffffffffffffffe00000000000000000000000000000001");
|
||||
}
|
||||
|
||||
TEST(bigint_test, square) {
|
||||
bigint n0(0);
|
||||
n0.square();
|
||||
EXPECT_EQ("0", fmt::format("{}", n0));
|
||||
EXPECT_EQ(fmt::to_string(n0), "0");
|
||||
bigint n1(0x100);
|
||||
n1.square();
|
||||
EXPECT_EQ("10000", fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1), "10000");
|
||||
bigint n2(0xfffffffff);
|
||||
n2.square();
|
||||
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2));
|
||||
EXPECT_EQ(fmt::to_string(n2), "ffffffffe000000001");
|
||||
bigint n3(max_value<uint64_t>());
|
||||
n3.square();
|
||||
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3));
|
||||
EXPECT_EQ(fmt::to_string(n3), "fffffffffffffffe0000000000000001");
|
||||
bigint n4;
|
||||
n4.assign_pow10(10);
|
||||
EXPECT_EQ("2540be400", fmt::format("{}", n4));
|
||||
EXPECT_EQ(fmt::to_string(n4), "2540be400");
|
||||
}
|
||||
|
||||
TEST(bigint_test, divmod_assign_zero_divisor) {
|
||||
@@ -133,8 +143,8 @@ TEST(bigint_test, divmod_assign_unaligned) {
|
||||
n2.assign_pow10(100);
|
||||
int result = n1.divmod_assign(n2);
|
||||
EXPECT_EQ(result, 9406);
|
||||
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96",
|
||||
fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1),
|
||||
"10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96");
|
||||
}
|
||||
|
||||
TEST(bigint_test, divmod_assign) {
|
||||
@@ -142,19 +152,19 @@ TEST(bigint_test, divmod_assign) {
|
||||
bigint n1(100);
|
||||
int result = n1.divmod_assign(bigint(10));
|
||||
EXPECT_EQ(result, 10);
|
||||
EXPECT_EQ("0", fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1), "0");
|
||||
// pow(10, 100) / (42 << 320):
|
||||
n1.assign_pow10(100);
|
||||
result = n1.divmod_assign(bigint(42) <<= 320);
|
||||
EXPECT_EQ(result, 111);
|
||||
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96",
|
||||
fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1),
|
||||
"13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96");
|
||||
// 42 / 100:
|
||||
bigint n2(42);
|
||||
n1.assign_pow10(2);
|
||||
result = n2.divmod_assign(n1);
|
||||
EXPECT_EQ(result, 0);
|
||||
EXPECT_EQ("2a", fmt::format("{}", n2));
|
||||
EXPECT_EQ(fmt::to_string(n2), "2a");
|
||||
}
|
||||
|
||||
template <bool is_iec559> void run_double_tests() {
|
||||
@@ -173,8 +183,8 @@ TEST(fp_test, double_tests) {
|
||||
TEST(fp_test, normalize) {
|
||||
const auto v = fp(0xbeef, 42);
|
||||
auto normalized = normalize(v);
|
||||
EXPECT_EQ(0xbeef000000000000, normalized.f);
|
||||
EXPECT_EQ(-6, normalized.e);
|
||||
EXPECT_EQ(normalized.f, 0xbeef000000000000);
|
||||
EXPECT_EQ(normalized.e, -6);
|
||||
}
|
||||
|
||||
TEST(fp_test, multiply) {
|
||||
@@ -200,8 +210,8 @@ TEST(fp_test, get_cached_power) {
|
||||
cache <<= power.e;
|
||||
exact.align(cache);
|
||||
cache.align(exact);
|
||||
auto exact_str = fmt::format("{}", exact);
|
||||
auto cache_str = fmt::format("{}", cache);
|
||||
auto exact_str = fmt::to_string(exact);
|
||||
auto cache_str = fmt::to_string(cache);
|
||||
EXPECT_EQ(exact_str.size(), cache_str.size());
|
||||
EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15));
|
||||
int diff = cache_str[15] - exact_str[15];
|
||||
@@ -212,11 +222,11 @@ TEST(fp_test, get_cached_power) {
|
||||
} else {
|
||||
cache.assign_pow10(-dec_exp);
|
||||
cache *= power.f + 1; // Inexact check.
|
||||
exact.assign(1);
|
||||
exact = 1;
|
||||
exact <<= -power.e;
|
||||
exact.align(cache);
|
||||
auto exact_str = fmt::format("{}", exact);
|
||||
auto cache_str = fmt::format("{}", cache);
|
||||
auto exact_str = fmt::to_string(exact);
|
||||
auto cache_str = fmt::to_string(cache);
|
||||
EXPECT_EQ(exact_str.size(), cache_str.size());
|
||||
EXPECT_EQ(exact_str.substr(0, 16), cache_str.substr(0, 16));
|
||||
}
|
||||
@@ -232,35 +242,35 @@ TEST(fp_test, dragonbox_max_k) {
|
||||
floor_log10_pow2(std::numeric_limits<float>::min_exponent -
|
||||
fmt::detail::num_significand_bits<float>() - 1));
|
||||
using double_info = fmt::detail::dragonbox::float_info<double>;
|
||||
EXPECT_EQ(
|
||||
fmt::detail::const_check(double_info::max_k),
|
||||
double_info::kappa -
|
||||
floor_log10_pow2(std::numeric_limits<double>::min_exponent -
|
||||
fmt::detail::num_significand_bits<double>() - 1));
|
||||
EXPECT_EQ(fmt::detail::const_check(double_info::max_k),
|
||||
double_info::kappa -
|
||||
floor_log10_pow2(
|
||||
std::numeric_limits<double>::min_exponent -
|
||||
2 * fmt::detail::num_significand_bits<double>() - 1));
|
||||
}
|
||||
|
||||
TEST(fp_test, get_round_direction) {
|
||||
using fmt::detail::get_round_direction;
|
||||
using fmt::detail::round_direction;
|
||||
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0));
|
||||
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10));
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(100, 60, 10));
|
||||
EXPECT_EQ(get_round_direction(100, 50, 0), round_direction::down);
|
||||
EXPECT_EQ(get_round_direction(100, 51, 0), round_direction::up);
|
||||
EXPECT_EQ(get_round_direction(100, 40, 10), round_direction::down);
|
||||
EXPECT_EQ(get_round_direction(100, 60, 10), round_direction::up);
|
||||
for (size_t i = 41; i < 60; ++i)
|
||||
EXPECT_EQ(round_direction::unknown, get_round_direction(100, i, 10));
|
||||
EXPECT_EQ(get_round_direction(100, i, 10), round_direction::unknown);
|
||||
uint64_t max = max_value<uint64_t>();
|
||||
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure);
|
||||
EXPECT_THROW(get_round_direction(100, 0, 100), assertion_failure);
|
||||
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
|
||||
// Check that remainder + error doesn't overflow.
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 2));
|
||||
EXPECT_EQ(get_round_direction(max, max - 1, 2), round_direction::up);
|
||||
// Check that 2 * (remainder + error) doesn't overflow.
|
||||
EXPECT_EQ(round_direction::unknown,
|
||||
get_round_direction(max, max / 2 + 1, max / 2));
|
||||
EXPECT_EQ(get_round_direction(max, max / 2 + 1, max / 2),
|
||||
round_direction::unknown);
|
||||
// Check that remainder - error doesn't overflow.
|
||||
EXPECT_EQ(round_direction::unknown, get_round_direction(100, 40, 41));
|
||||
EXPECT_EQ(get_round_direction(100, 40, 41), round_direction::unknown);
|
||||
// Check that 2 * (remainder - error) doesn't overflow.
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 1));
|
||||
EXPECT_EQ(get_round_direction(max, max - 1, 1), round_direction::up);
|
||||
}
|
||||
|
||||
TEST(fp_test, fixed_handler) {
|
||||
@@ -283,20 +293,20 @@ TEST(fp_test, fixed_handler) {
|
||||
}
|
||||
|
||||
TEST(fp_test, grisu_format_compiles_with_on_ieee_double) {
|
||||
fmt::memory_buffer buf;
|
||||
auto buf = fmt::memory_buffer();
|
||||
format_float(0.42, -1, fmt::detail::float_specs(), buf);
|
||||
}
|
||||
|
||||
TEST(format_impl_test, format_error_code) {
|
||||
std::string msg = "error 42", sep = ": ";
|
||||
{
|
||||
fmt::memory_buffer buffer;
|
||||
format_to(fmt::appender(buffer), "garbage");
|
||||
auto buffer = fmt::memory_buffer();
|
||||
fmt::format_to(fmt::appender(buffer), "garbage");
|
||||
fmt::detail::format_error_code(buffer, 42, "test");
|
||||
EXPECT_EQ("test: " + msg, to_string(buffer));
|
||||
EXPECT_EQ(to_string(buffer), "test: " + msg);
|
||||
}
|
||||
{
|
||||
fmt::memory_buffer buffer;
|
||||
auto buffer = fmt::memory_buffer();
|
||||
auto prefix =
|
||||
std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
|
||||
fmt::detail::format_error_code(buffer, 42, prefix);
|
||||
@@ -317,7 +327,7 @@ TEST(format_impl_test, format_error_code) {
|
||||
// Test with a message that doesn't fit into the buffer.
|
||||
prefix += 'x';
|
||||
fmt::detail::format_error_code(buffer, codes[i], prefix);
|
||||
EXPECT_EQ(msg, to_string(buffer));
|
||||
EXPECT_EQ(to_string(buffer), msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,8 +343,8 @@ template <typename Int> void test_count_digits() {
|
||||
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
|
||||
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
|
||||
n *= 10;
|
||||
EXPECT_EQ(i, fmt::detail::count_digits(n - 1));
|
||||
EXPECT_EQ(i + 1, fmt::detail::count_digits(n));
|
||||
EXPECT_EQ(fmt::detail::count_digits(n - 1), i);
|
||||
EXPECT_EQ(fmt::detail::count_digits(n), i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,13 +353,216 @@ TEST(format_impl_test, count_digits) {
|
||||
test_count_digits<uint64_t>();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
TEST(format_impl_test, countl_zero) {
|
||||
constexpr auto num_bits = fmt::detail::num_bits<uint32_t>();
|
||||
uint32_t n = 1u;
|
||||
for (int i = 1; i < num_bits - 1; i++) {
|
||||
n <<= 1;
|
||||
EXPECT_EQ(fmt::detail::countl_zero(n - 1), num_bits - i);
|
||||
EXPECT_EQ(fmt::detail::countl_zero(n), num_bits - i - 1);
|
||||
}
|
||||
}
|
||||
|
||||
#if FMT_USE_FLOAT128
|
||||
TEST(format_impl_test, write_float128) {
|
||||
auto s = std::string();
|
||||
fmt::detail::write<char>(std::back_inserter(s), __float128(42));
|
||||
EXPECT_EQ(s, "42");
|
||||
}
|
||||
#endif
|
||||
|
||||
struct double_double {
|
||||
double a;
|
||||
double b;
|
||||
|
||||
explicit constexpr double_double(double a_val = 0, double b_val = 0)
|
||||
: a(a_val), b(b_val) {}
|
||||
|
||||
operator double() const { return a + b; }
|
||||
auto operator-() const -> double_double { return double_double(-a, -b); }
|
||||
};
|
||||
|
||||
auto format_as(double_double d) -> double { return d; }
|
||||
|
||||
bool operator>=(const double_double& lhs, const double_double& rhs) {
|
||||
return lhs.a + lhs.b >= rhs.a + rhs.b;
|
||||
}
|
||||
|
||||
struct slow_float {
|
||||
float value;
|
||||
|
||||
explicit constexpr slow_float(float val = 0) : value(val) {}
|
||||
operator float() const { return value; }
|
||||
auto operator-() const -> slow_float { return slow_float(-value); }
|
||||
};
|
||||
|
||||
auto format_as(slow_float f) -> float { return f; }
|
||||
|
||||
namespace std {
|
||||
template <> struct is_floating_point<double_double> : std::true_type {};
|
||||
template <> struct numeric_limits<double_double> {
|
||||
// is_iec559 is true for double-double in libstdc++.
|
||||
static constexpr bool is_iec559 = true;
|
||||
static constexpr int digits = 106;
|
||||
};
|
||||
|
||||
template <> struct is_floating_point<slow_float> : std::true_type {};
|
||||
template <> struct numeric_limits<slow_float> : numeric_limits<float> {};
|
||||
} // namespace std
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
template <> struct is_fast_float<slow_float> : std::false_type {};
|
||||
namespace dragonbox {
|
||||
template <> struct float_info<slow_float> {
|
||||
using carrier_uint = uint32_t;
|
||||
static const int exponent_bits = 8;
|
||||
};
|
||||
} // namespace dragonbox
|
||||
} // namespace detail
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(format_impl_test, write_double_double) {
|
||||
auto s = std::string();
|
||||
fmt::detail::write<char>(std::back_inserter(s), double_double(42), {});
|
||||
// Specializing is_floating_point is broken in MSVC.
|
||||
if (!FMT_MSC_VERSION) EXPECT_EQ(s, "42");
|
||||
}
|
||||
|
||||
TEST(format_impl_test, write_dragon_even) {
|
||||
auto s = std::string();
|
||||
fmt::detail::write<char>(std::back_inserter(s), slow_float(33554450.0f), {});
|
||||
// Specializing is_floating_point is broken in MSVC.
|
||||
if (!FMT_MSC_VERSION) EXPECT_EQ(s, "33554450");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
|
||||
TEST(format_impl_test, write_console_signature) {
|
||||
decltype(WriteConsoleW)* p = fmt::detail::WriteConsoleW;
|
||||
decltype(::WriteConsoleW)* p = fmt::detail::WriteConsoleW;
|
||||
(void)p;
|
||||
}
|
||||
#endif
|
||||
|
||||
// A public domain branchless UTF-8 decoder by Christopher Wellons:
|
||||
// https://github.com/skeeto/branchless-utf8
|
||||
constexpr bool unicode_is_surrogate(uint32_t c) {
|
||||
return c >= 0xD800U && c <= 0xDFFFU;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR char* utf8_encode(char* s, uint32_t c) {
|
||||
if (c >= (1UL << 16)) {
|
||||
s[0] = static_cast<char>(0xf0 | (c >> 18));
|
||||
s[1] = static_cast<char>(0x80 | ((c >> 12) & 0x3f));
|
||||
s[2] = static_cast<char>(0x80 | ((c >> 6) & 0x3f));
|
||||
s[3] = static_cast<char>(0x80 | ((c >> 0) & 0x3f));
|
||||
return s + 4;
|
||||
} else if (c >= (1UL << 11)) {
|
||||
s[0] = static_cast<char>(0xe0 | (c >> 12));
|
||||
s[1] = static_cast<char>(0x80 | ((c >> 6) & 0x3f));
|
||||
s[2] = static_cast<char>(0x80 | ((c >> 0) & 0x3f));
|
||||
return s + 3;
|
||||
} else if (c >= (1UL << 7)) {
|
||||
s[0] = static_cast<char>(0xc0 | (c >> 6));
|
||||
s[1] = static_cast<char>(0x80 | ((c >> 0) & 0x3f));
|
||||
return s + 2;
|
||||
} else {
|
||||
s[0] = static_cast<char>(c);
|
||||
return s + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure it can decode every character
|
||||
TEST(format_impl_test, utf8_decode_decode_all) {
|
||||
for (uint32_t i = 0; i < 0x10ffff; i++) {
|
||||
if (!unicode_is_surrogate(i)) {
|
||||
int e;
|
||||
uint32_t c;
|
||||
char buf[8] = {0};
|
||||
char* end = utf8_encode(buf, i);
|
||||
const char* res = fmt::detail::utf8_decode(buf, &c, &e);
|
||||
EXPECT_EQ(end, res);
|
||||
EXPECT_EQ(c, i);
|
||||
EXPECT_EQ(e, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reject everything outside of U+0000..U+10FFFF
|
||||
TEST(format_impl_test, utf8_decode_out_of_range) {
|
||||
for (uint32_t i = 0x110000; i < 0x1fffff; i++) {
|
||||
int e;
|
||||
uint32_t c;
|
||||
char buf[8] = {0};
|
||||
utf8_encode(buf, i);
|
||||
const char* end = fmt::detail::utf8_decode(buf, &c, &e);
|
||||
EXPECT_NE(e, 0);
|
||||
EXPECT_EQ(end - buf, 4);
|
||||
}
|
||||
}
|
||||
|
||||
// Does it reject all surrogate halves?
|
||||
TEST(format_impl_test, utf8_decode_surrogate_halves) {
|
||||
for (uint32_t i = 0xd800; i <= 0xdfff; i++) {
|
||||
int e;
|
||||
uint32_t c;
|
||||
char buf[8] = {0};
|
||||
utf8_encode(buf, i);
|
||||
fmt::detail::utf8_decode(buf, &c, &e);
|
||||
EXPECT_NE(e, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// How about non-canonical encodings?
|
||||
TEST(format_impl_test, utf8_decode_non_canonical_encodings) {
|
||||
int e;
|
||||
uint32_t c;
|
||||
const char* end;
|
||||
|
||||
char buf2[8] = {char(0xc0), char(0xA4)};
|
||||
end = fmt::detail::utf8_decode(buf2, &c, &e);
|
||||
EXPECT_NE(e, 0); // non-canonical len 2
|
||||
EXPECT_EQ(end, buf2 + 2); // non-canonical recover 2
|
||||
|
||||
char buf3[8] = {char(0xe0), char(0x80), char(0xA4)};
|
||||
end = fmt::detail::utf8_decode(buf3, &c, &e);
|
||||
EXPECT_NE(e, 0); // non-canonical len 3
|
||||
EXPECT_EQ(end, buf3 + 3); // non-canonical recover 3
|
||||
|
||||
char buf4[8] = {char(0xf0), char(0x80), char(0x80), char(0xA4)};
|
||||
end = fmt::detail::utf8_decode(buf4, &c, &e);
|
||||
EXPECT_NE(e, 0); // non-canonical encoding len 4
|
||||
EXPECT_EQ(end, buf4 + 4); // non-canonical recover 4
|
||||
}
|
||||
|
||||
// Let's try some bogus byte sequences
|
||||
TEST(format_impl_test, utf8_decode_bogus_byte_sequences) {
|
||||
int e;
|
||||
uint32_t c;
|
||||
|
||||
// Invalid first byte
|
||||
char buf0[4] = {char(0xff)};
|
||||
auto len = fmt::detail::utf8_decode(buf0, &c, &e) - buf0;
|
||||
EXPECT_NE(e, 0); // "bogus [ff] 0x%02x U+%04lx", e, (unsigned long)c);
|
||||
EXPECT_EQ(len, 1); // "bogus [ff] recovery %d", len);
|
||||
|
||||
// Invalid first byte
|
||||
char buf1[4] = {char(0x80)};
|
||||
len = fmt::detail::utf8_decode(buf1, &c, &e) - buf1;
|
||||
EXPECT_NE(e, 0); // "bogus [80] 0x%02x U+%04lx", e, (unsigned long)c);
|
||||
EXPECT_EQ(len, 1); // "bogus [80] recovery %d", len);
|
||||
|
||||
// Looks like a two-byte sequence but second byte is wrong
|
||||
char buf2[4] = {char(0xc0), char(0x0a)};
|
||||
len = fmt::detail::utf8_decode(buf2, &c, &e) - buf2;
|
||||
EXPECT_NE(e, 0); // "bogus [c0 0a] 0x%02x U+%04lx", e, (unsigned long)c
|
||||
EXPECT_EQ(len, 2); // "bogus [c0 0a] recovery %d", len);
|
||||
}
|
||||
|
||||
TEST(format_impl_test, unicode_to_utf8) {
|
||||
auto s = std::string("ёжик");
|
||||
fmt::detail::unicode_to_utf8<wchar_t> u(L"\x0451\x0436\x0438\x043A");
|
||||
EXPECT_EQ(s, u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
Vendored
+314
-345
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -22,7 +22,7 @@ function(add_fuzzer source)
|
||||
if (FMT_FUZZ_LDFLAGS)
|
||||
target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS})
|
||||
endif ()
|
||||
target_compile_features(${name} PRIVATE cxx_generic_lambdas)
|
||||
target_compile_features(${name} PRIVATE cxx_std_14)
|
||||
endfunction()
|
||||
|
||||
foreach (source chrono-duration.cc chrono-timepoint.cc float.cc named-arg.cc one-arg.cc two-args.cc)
|
||||
|
||||
Vendored
+2
-2
@@ -30,8 +30,8 @@ void invoke_fmt(const uint8_t* data, size_t size) {
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(format_str.get(), *value);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, format_str.get(), *value);
|
||||
auto buf = fmt::memory_buffer();
|
||||
fmt::format_to(std::back_inserter(buf), format_str.get(), *value);
|
||||
#endif
|
||||
} catch (std::exception&) {
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -27,8 +27,8 @@ void invoke_fmt(const uint8_t* data, size_t size) {
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(format_str, item1, item2);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, format_str, item1, item2);
|
||||
auto buf = fmt::memory_buffer();
|
||||
fmt::format_to(std::back_inserter(buf), format_str, item1, item2);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
-1
@@ -201,6 +201,7 @@ TEST(gtest_extra_test, expect_write_streaming) {
|
||||
// EXPECT_THROW_MSG macro.
|
||||
TEST(gtest_extra_test, expect_throw_no_unreachable_code_warning) {
|
||||
int n = 0;
|
||||
(void)n;
|
||||
using std::runtime_error;
|
||||
EXPECT_THROW_MSG(throw runtime_error(""), runtime_error, "");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_THROW_MSG(n++, runtime_error, ""), "");
|
||||
@@ -213,6 +214,7 @@ TEST(gtest_extra_test, expect_throw_no_unreachable_code_warning) {
|
||||
// EXPECT_SYSTEM_ERROR macro.
|
||||
TEST(gtest_extra_test, expect_system_error_no_unreachable_code_warning) {
|
||||
int n = 0;
|
||||
(void)n;
|
||||
EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "test"), EDOM, "test");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), "");
|
||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw 1, EDOM, ""), "");
|
||||
@@ -347,7 +349,7 @@ TEST(output_redirect_test, flush_error_in_ctor) {
|
||||
|
||||
TEST(output_redirect_test, dup_error_in_ctor) {
|
||||
buffered_file f = open_buffered_file();
|
||||
int fd = (f.fileno)();
|
||||
int fd = (f.descriptor)();
|
||||
file copy = file::dup(fd);
|
||||
FMT_POSIX(close(fd));
|
||||
std::unique_ptr<output_redirect> redir{nullptr};
|
||||
|
||||
Vendored
+1
-6
@@ -12,12 +12,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef FMT_MODULE_TEST
|
||||
import fmt;
|
||||
#else
|
||||
# include "fmt/os.h"
|
||||
#endif // FMG_MODULE_TEST
|
||||
|
||||
#include "fmt/os.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
|
||||
|
||||
Vendored
+1
-7
@@ -9,6 +9,7 @@ add_library(gtest STATIC
|
||||
gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h)
|
||||
target_compile_definitions(gtest PUBLIC GTEST_HAS_STD_WSTRING=1)
|
||||
target_include_directories(gtest SYSTEM PUBLIC .)
|
||||
target_compile_features(gtest PUBLIC cxx_std_11)
|
||||
|
||||
find_package(Threads)
|
||||
if (Threads_FOUND)
|
||||
@@ -17,13 +18,6 @@ else ()
|
||||
target_compile_definitions(gtest PUBLIC GTEST_HAS_PTHREAD=0)
|
||||
endif ()
|
||||
|
||||
# Workaround GTest bug https://github.com/google/googletest/issues/705.
|
||||
check_cxx_compiler_flag(
|
||||
-fno-delete-null-pointer-checks HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
|
||||
if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
|
||||
target_compile_options(gtest PUBLIC -fno-delete-null-pointer-checks)
|
||||
endif ()
|
||||
|
||||
if (MSVC)
|
||||
# Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions.
|
||||
target_compile_definitions(gtest PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
+2
-2
@@ -2202,7 +2202,7 @@ bool UnitTestOptions::MatchesFilter(const std::string& name_str,
|
||||
|
||||
// Check if this pattern matches name_str.
|
||||
if (PatternMatchesString(name_str, pattern, pattern_end)) {
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Give up on this pattern. However, if we found a pattern separator (:),
|
||||
@@ -13778,7 +13778,7 @@ UntypedActionResultHolderBase* UntypedFunctionMockerBase::UntypedInvokeWith(
|
||||
|
||||
UntypedActionResultHolderBase* result = nullptr;
|
||||
|
||||
auto perform_action = [&] {
|
||||
auto perform_action = [&, this] {
|
||||
return untyped_action == nullptr
|
||||
? this->UntypedPerformDefaultAction(untyped_args, ss.str())
|
||||
: this->UntypedPerformAction(untyped_action, untyped_args);
|
||||
|
||||
Vendored
+2
-2
@@ -20,8 +20,8 @@ template <typename T> class mock_allocator {
|
||||
mock_allocator() {}
|
||||
mock_allocator(const mock_allocator&) {}
|
||||
using value_type = T;
|
||||
MOCK_METHOD1_T(allocate, T*(size_t n));
|
||||
MOCK_METHOD2_T(deallocate, void(T* p, size_t n));
|
||||
MOCK_METHOD(T*, allocate, (size_t));
|
||||
MOCK_METHOD(void, deallocate, (T*, size_t));
|
||||
};
|
||||
|
||||
template <typename Allocator> class allocator_ref {
|
||||
|
||||
Vendored
+36
-88
@@ -15,8 +15,6 @@
|
||||
# define FMT_HIDE_MODULE_BUGS
|
||||
#endif
|
||||
|
||||
#define FMT_MODULE_TEST
|
||||
|
||||
#include <bit>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
@@ -41,7 +39,6 @@
|
||||
#else
|
||||
# define FMT_POSIX(call) call
|
||||
#endif
|
||||
#define FMT_OS_H_ // don't pull in os.h directly or indirectly
|
||||
|
||||
import fmt;
|
||||
|
||||
@@ -53,13 +50,8 @@ static bool macro_leaked =
|
||||
false;
|
||||
#endif
|
||||
|
||||
// Include sources to pick up functions and classes from the module rather than
|
||||
// from the non-modular library which is baked into the 'test-main' library.
|
||||
// This averts linker problems:
|
||||
// - strong ownership model: missing linker symbols
|
||||
// - weak ownership model: duplicate linker symbols
|
||||
#include "gtest-extra.cc"
|
||||
#include "util.cc"
|
||||
#define FMT_OS_H_ // don't pull in os.h, neither directly nor indirectly
|
||||
#include "gtest-extra.h"
|
||||
|
||||
// an implicitly exported namespace must be visible [module.interface]/2.2
|
||||
TEST(module_test, namespace) {
|
||||
@@ -75,9 +67,8 @@ bool oops_detail_namespace_is_visible;
|
||||
namespace fmt {
|
||||
bool namespace_detail_invisible() {
|
||||
#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \
|
||||
((_MSC_VER == 1929 && _MSC_FULL_VER <= 192930136) || \
|
||||
(_MSC_VER == 1930 && _MSC_FULL_VER <= 193030704))
|
||||
// bug in msvc up to 16.11.5 / 17.0-pre5:
|
||||
_MSC_FULL_VER <= 193700000
|
||||
// bug in msvc up to at least 17.7:
|
||||
|
||||
// the namespace is visible even when it is neither
|
||||
// implicitly nor explicitly exported
|
||||
@@ -111,7 +102,7 @@ TEST(module_test, macros) {
|
||||
// but rather visibility of all client-facing overloads, reachability of
|
||||
// non-exported entities, name lookup and overload resolution within
|
||||
// template instantitions.
|
||||
// Excercise all exported entities of the API at least once.
|
||||
// Exercise all exported entities of the API at least once.
|
||||
// Instantiate as many code paths as possible.
|
||||
|
||||
TEST(module_test, to_string) {
|
||||
@@ -140,7 +131,7 @@ TEST(module_test, format_to) {
|
||||
EXPECT_EQ("42", std::string_view(buffer));
|
||||
|
||||
fmt::memory_buffer mb;
|
||||
fmt::format_to(mb, "{}", 42);
|
||||
fmt::format_to(std::back_inserter(mb), "{}", 42);
|
||||
EXPECT_EQ("42", std::string_view(buffer));
|
||||
|
||||
std::wstring w;
|
||||
@@ -152,7 +143,7 @@ TEST(module_test, format_to) {
|
||||
EXPECT_EQ(L"42", std::wstring_view(wbuffer));
|
||||
|
||||
fmt::wmemory_buffer wb;
|
||||
fmt::format_to(wb, L"{}", 42);
|
||||
fmt::format_to(std::back_inserter(wb), L"{}", 42);
|
||||
EXPECT_EQ(L"42", std::wstring_view(wbuffer));
|
||||
}
|
||||
|
||||
@@ -211,8 +202,8 @@ TEST(module_test, dynamic_format_args) {
|
||||
|
||||
TEST(module_test, vformat) {
|
||||
EXPECT_EQ("42", fmt::vformat("{}", fmt::make_format_args(42)));
|
||||
EXPECT_EQ(L"42", fmt::vformat(fmt::to_string_view(L"{}"),
|
||||
fmt::make_wformat_args(42)));
|
||||
EXPECT_EQ(L"42",
|
||||
fmt::vformat(fmt::wstring_view(L"{}"), fmt::make_wformat_args(42)));
|
||||
}
|
||||
|
||||
TEST(module_test, vformat_to) {
|
||||
@@ -245,9 +236,9 @@ TEST(module_test, vformat_to_n) {
|
||||
auto wstore = fmt::make_wformat_args(12345);
|
||||
std::wstring w;
|
||||
auto wresult = fmt::vformat_to_n(std::back_inserter(w), 1,
|
||||
fmt::to_string_view(L"{}"), wstore);
|
||||
fmt::wstring_view(L"{}"), wstore);
|
||||
wchar_t wbuffer[4] = {0};
|
||||
fmt::vformat_to_n(wbuffer, 3, fmt::to_string_view(L"{:}"), wstore);
|
||||
fmt::vformat_to_n(wbuffer, 3, fmt::wstring_view(L"{:}"), wstore);
|
||||
}
|
||||
|
||||
std::string as_string(std::wstring_view text) {
|
||||
@@ -258,22 +249,18 @@ std::string as_string(std::wstring_view text) {
|
||||
TEST(module_test, print) {
|
||||
EXPECT_WRITE(stdout, fmt::print("{}µ", 42), "42µ");
|
||||
EXPECT_WRITE(stderr, fmt::print(stderr, "{}µ", 4.2), "4.2µ");
|
||||
if (false) {
|
||||
EXPECT_WRITE(stdout, fmt::print(L"{}µ", 42), as_string(L"42µ"));
|
||||
EXPECT_WRITE(stderr, fmt::print(stderr, L"{}µ", 4.2), as_string(L"4.2µ"));
|
||||
}
|
||||
EXPECT_WRITE(stdout, fmt::print(L"{}µ", 42), as_string(L"42µ"));
|
||||
EXPECT_WRITE(stderr, fmt::print(stderr, L"{}µ", 4.2), as_string(L"4.2µ"));
|
||||
}
|
||||
|
||||
TEST(module_test, vprint) {
|
||||
EXPECT_WRITE(stdout, fmt::vprint("{:}µ", fmt::make_format_args(42)), "42µ");
|
||||
EXPECT_WRITE(stderr, fmt::vprint(stderr, "{}", fmt::make_format_args(4.2)),
|
||||
"4.2");
|
||||
if (false) {
|
||||
EXPECT_WRITE(stdout, fmt::vprint(L"{:}µ", fmt::make_wformat_args(42)),
|
||||
as_string(L"42µ"));
|
||||
EXPECT_WRITE(stderr, fmt::vprint(stderr, L"{}", fmt::make_wformat_args(42)),
|
||||
as_string(L"42"));
|
||||
}
|
||||
EXPECT_WRITE(stdout, fmt::vprint(L"{:}µ", fmt::make_wformat_args(42)),
|
||||
as_string(L"42µ"));
|
||||
EXPECT_WRITE(stderr, fmt::vprint(stderr, L"{}", fmt::make_wformat_args(42)),
|
||||
as_string(L"42"));
|
||||
}
|
||||
|
||||
TEST(module_test, named_args) {
|
||||
@@ -284,9 +271,7 @@ TEST(module_test, named_args) {
|
||||
TEST(module_test, literals) {
|
||||
using namespace fmt::literals;
|
||||
EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = 42));
|
||||
EXPECT_EQ("42", "{}"_format(42));
|
||||
EXPECT_EQ(L"42", fmt::format(L"{answer}", L"answer"_a = 42));
|
||||
EXPECT_EQ(L"42", L"{}"_format(42));
|
||||
}
|
||||
|
||||
TEST(module_test, locale) {
|
||||
@@ -320,7 +305,7 @@ TEST(module_test, string_view) {
|
||||
|
||||
TEST(module_test, memory_buffer) {
|
||||
fmt::basic_memory_buffer<char, fmt::inline_buffer_size> buffer;
|
||||
fmt::format_to(buffer, "{}", "42");
|
||||
fmt::format_to(std::back_inserter(buffer), "{}", "42");
|
||||
EXPECT_EQ("42", to_string(buffer));
|
||||
fmt::memory_buffer nbuffer(std::move(buffer));
|
||||
EXPECT_EQ("42", to_string(nbuffer));
|
||||
@@ -395,27 +380,20 @@ struct test_formatter : fmt::formatter<char> {
|
||||
bool check() { return true; }
|
||||
};
|
||||
|
||||
struct test_dynamic_formatter : fmt::dynamic_formatter<> {
|
||||
bool check() { return true; }
|
||||
};
|
||||
|
||||
TEST(module_test, formatter) {
|
||||
EXPECT_TRUE(test_formatter{}.check());
|
||||
EXPECT_TRUE(test_dynamic_formatter{}.check());
|
||||
}
|
||||
TEST(module_test, formatter) { EXPECT_TRUE(test_formatter{}.check()); }
|
||||
|
||||
TEST(module_test, join) {
|
||||
int arr[3] = {1, 2, 3};
|
||||
std::vector<double> vec{1.0, 2.0, 3.0};
|
||||
std::initializer_list<int> il{1, 2, 3};
|
||||
auto sep = fmt::to_string_view(", ");
|
||||
auto sep = fmt::string_view(", ");
|
||||
EXPECT_EQ("1, 2, 3", to_string(fmt::join(arr + 0, arr + 3, sep)));
|
||||
EXPECT_EQ("1, 2, 3", to_string(fmt::join(arr, sep)));
|
||||
EXPECT_EQ("1, 2, 3", to_string(fmt::join(vec.begin(), vec.end(), sep)));
|
||||
EXPECT_EQ("1, 2, 3", to_string(fmt::join(vec, sep)));
|
||||
EXPECT_EQ("1, 2, 3", to_string(fmt::join(il, sep)));
|
||||
|
||||
auto wsep = fmt::to_string_view(L", ");
|
||||
auto wsep = fmt::wstring_view(L", ");
|
||||
EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(arr + 0, arr + 3, wsep)));
|
||||
EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(arr, wsep)));
|
||||
EXPECT_EQ(L"1, 2, 3", fmt::format(L"{}", fmt::join(il, wsep)));
|
||||
@@ -426,7 +404,6 @@ TEST(module_test, time) {
|
||||
EXPECT_TRUE(fmt::localtime(time_now).tm_year > 120);
|
||||
EXPECT_TRUE(fmt::gmtime(time_now).tm_year > 120);
|
||||
auto chrono_now = std::chrono::system_clock::now();
|
||||
EXPECT_TRUE(fmt::localtime(chrono_now).tm_year > 120);
|
||||
EXPECT_TRUE(fmt::gmtime(chrono_now).tm_year > 120);
|
||||
}
|
||||
|
||||
@@ -453,34 +430,16 @@ TEST(module_test, weekday) {
|
||||
EXPECT_EQ("Mon", fmt::format(std::locale::classic(), "{}", fmt::weekday(1)));
|
||||
}
|
||||
|
||||
TEST(module_test, to_string_view) {
|
||||
using fmt::to_string_view;
|
||||
fmt::string_view nsv{to_string_view("42")};
|
||||
EXPECT_EQ("42", nsv);
|
||||
fmt::wstring_view wsv{to_string_view(L"42")};
|
||||
EXPECT_EQ(L"42", wsv);
|
||||
}
|
||||
|
||||
TEST(module_test, printf) {
|
||||
EXPECT_WRITE(stdout, fmt::printf("%f", 42.123456), "42.123456");
|
||||
EXPECT_WRITE(stdout, fmt::printf("%d", 42), "42");
|
||||
if (false) {
|
||||
EXPECT_WRITE(stdout, fmt::printf(L"%f", 42.123456),
|
||||
as_string(L"42.123456"));
|
||||
EXPECT_WRITE(stdout, fmt::printf(L"%d", 42), as_string(L"42"));
|
||||
}
|
||||
EXPECT_WRITE(stdout, fmt::printf(L"%f", 42.123456), as_string(L"42.123456"));
|
||||
EXPECT_WRITE(stdout, fmt::printf(L"%d", 42), as_string(L"42"));
|
||||
}
|
||||
|
||||
TEST(module_test, fprintf) {
|
||||
EXPECT_WRITE(stderr, fmt::fprintf(stderr, "%d", 42), "42");
|
||||
std::ostringstream os;
|
||||
fmt::fprintf(os, "%s", "bla");
|
||||
EXPECT_EQ("bla", os.str());
|
||||
|
||||
EXPECT_WRITE(stderr, fmt::fprintf(stderr, L"%d", 42), as_string(L"42"));
|
||||
std::wostringstream ws;
|
||||
fmt::fprintf(ws, L"%s", L"bla");
|
||||
EXPECT_EQ(L"bla", ws.str());
|
||||
}
|
||||
|
||||
TEST(module_test, sprintf) {
|
||||
@@ -490,25 +449,15 @@ TEST(module_test, sprintf) {
|
||||
|
||||
TEST(module_test, vprintf) {
|
||||
EXPECT_WRITE(stdout, fmt::vprintf("%d", fmt::make_printf_args(42)), "42");
|
||||
if (false) {
|
||||
EXPECT_WRITE(stdout, fmt::vprintf(L"%d", fmt::make_wprintf_args(42)),
|
||||
as_string(L"42"));
|
||||
}
|
||||
EXPECT_WRITE(stdout, fmt::vprintf(L"%d", fmt::make_wprintf_args(42)),
|
||||
as_string(L"42"));
|
||||
}
|
||||
|
||||
TEST(module_test, vfprintf) {
|
||||
auto args = fmt::make_printf_args(42);
|
||||
EXPECT_WRITE(stderr, fmt::vfprintf(stderr, "%d", args), "42");
|
||||
std::ostringstream os;
|
||||
fmt::vfprintf(os, "%d", args);
|
||||
EXPECT_EQ("42", os.str());
|
||||
auto wargs = fmt::make_wprintf_args(42);
|
||||
if (false) {
|
||||
EXPECT_WRITE(stderr, fmt::vfprintf(stderr, L"%d", wargs), as_string(L"42"));
|
||||
}
|
||||
std::wostringstream ws;
|
||||
fmt::vfprintf(ws, L"%d", wargs);
|
||||
EXPECT_EQ(L"42", ws.str());
|
||||
EXPECT_WRITE(stderr, fmt::vfprintf(stderr, L"%d", wargs), as_string(L"42"));
|
||||
}
|
||||
|
||||
TEST(module_test, vsprintf) {
|
||||
@@ -538,8 +487,13 @@ TEST(module_test, buffered_file) {
|
||||
}
|
||||
|
||||
TEST(module_test, output_file) {
|
||||
#ifdef __clang__
|
||||
fmt::println("\033[0;33m[=disabled=] {}\033[0;0m",
|
||||
"Clang 16.0 emits multiple copies of vtables");
|
||||
#else
|
||||
fmt::ostream out = fmt::output_file("module-test", fmt::buffer_size = 1);
|
||||
out.close();
|
||||
#endif
|
||||
}
|
||||
|
||||
struct custom_context {
|
||||
@@ -552,21 +506,15 @@ TEST(module_test, custom_context) {
|
||||
EXPECT_TRUE(!custom_arg);
|
||||
}
|
||||
|
||||
struct disabled_formatter {};
|
||||
|
||||
TEST(module_test, has_formatter) {
|
||||
EXPECT_FALSE(
|
||||
(fmt::has_formatter<disabled_formatter, fmt::format_context>::value));
|
||||
}
|
||||
|
||||
TEST(module_test, is_formattable) {
|
||||
EXPECT_FALSE(fmt::is_formattable<disabled_formatter>::value);
|
||||
}
|
||||
|
||||
TEST(module_test, compile_format_string) {
|
||||
using namespace fmt::literals;
|
||||
#ifdef __clang__
|
||||
fmt::println("\033[0;33m[=disabled=] {}\033[0;0m",
|
||||
"Clang 16.0 fails to import user-defined literals");
|
||||
#else
|
||||
EXPECT_EQ("42", fmt::format("{0:x}"_cf, 0x42));
|
||||
EXPECT_EQ(L"42", fmt::format(L"{:}"_cf, 42));
|
||||
EXPECT_EQ("4.2", fmt::format("{arg:3.1f}"_cf, "arg"_a = 4.2));
|
||||
EXPECT_EQ(L" 42", fmt::format(L"{arg:>3}"_cf, L"arg"_a = L"42"));
|
||||
#endif
|
||||
}
|
||||
Vendored
+24
-69
@@ -14,10 +14,6 @@
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef fileno
|
||||
# undef fileno
|
||||
#endif
|
||||
|
||||
using fmt::buffered_file;
|
||||
using testing::HasSubstr;
|
||||
using wstring_view = fmt::basic_string_view<wchar_t>;
|
||||
@@ -26,60 +22,6 @@ using wstring_view = fmt::basic_string_view<wchar_t>;
|
||||
|
||||
# include <windows.h>
|
||||
|
||||
TEST(util_test, utf16_to_utf8) {
|
||||
auto s = std::string("ёжик");
|
||||
fmt::detail::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
|
||||
EXPECT_EQ(s, u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
TEST(util_test, utf16_to_utf8_empty_string) {
|
||||
std::string s = "";
|
||||
fmt::detail::utf16_to_utf8 u(L"");
|
||||
EXPECT_EQ(s, u.str());
|
||||
EXPECT_EQ(s.size(), u.size());
|
||||
}
|
||||
|
||||
template <typename Converter, typename Char>
|
||||
void check_utf_conversion_error(const char* message,
|
||||
fmt::basic_string_view<Char> str =
|
||||
fmt::basic_string_view<Char>(nullptr, 1)) {
|
||||
fmt::memory_buffer out;
|
||||
fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
|
||||
auto error = std::system_error(std::error_code());
|
||||
try {
|
||||
(Converter)(str);
|
||||
} catch (const std::system_error& e) {
|
||||
error = e;
|
||||
}
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, error.code().value());
|
||||
EXPECT_THAT(error.what(), HasSubstr(fmt::to_string(out)));
|
||||
}
|
||||
|
||||
TEST(util_test, utf16_to_utf8_error) {
|
||||
check_utf_conversion_error<fmt::detail::utf16_to_utf8, wchar_t>(
|
||||
"cannot convert string from UTF-16 to UTF-8");
|
||||
}
|
||||
|
||||
TEST(util_test, utf16_to_utf8_convert) {
|
||||
fmt::detail::utf16_to_utf8 u;
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(wstring_view(nullptr, 1)));
|
||||
EXPECT_EQ(ERROR_INVALID_PARAMETER,
|
||||
u.convert(wstring_view(L"foo", INT_MAX + 1u)));
|
||||
}
|
||||
|
||||
TEST(os_test, format_std_error_code) {
|
||||
EXPECT_EQ("generic:42",
|
||||
fmt::format(FMT_STRING("{0}"),
|
||||
std::error_code(42, std::generic_category())));
|
||||
EXPECT_EQ("system:42",
|
||||
fmt::format(FMT_STRING("{0}"),
|
||||
std::error_code(42, fmt::system_category())));
|
||||
EXPECT_EQ("system:-42",
|
||||
fmt::format(FMT_STRING("{0}"),
|
||||
std::error_code(-42, fmt::system_category())));
|
||||
}
|
||||
|
||||
TEST(os_test, format_windows_error) {
|
||||
LPWSTR message = nullptr;
|
||||
auto result = FormatMessageW(
|
||||
@@ -87,7 +29,8 @@ TEST(os_test, format_windows_error) {
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPWSTR>(&message), 0, nullptr);
|
||||
fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2));
|
||||
fmt::detail::unicode_to_utf8<wchar_t> utf8_message(
|
||||
wstring_view(message, result - 2));
|
||||
LocalFree(message);
|
||||
fmt::memory_buffer actual_message;
|
||||
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
|
||||
@@ -112,7 +55,8 @@ TEST(os_test, format_long_windows_error) {
|
||||
LocalFree(message);
|
||||
return;
|
||||
}
|
||||
fmt::detail::utf16_to_utf8 utf8_message(wstring_view(message, result - 2));
|
||||
fmt::detail::unicode_to_utf8<wchar_t> utf8_message(
|
||||
wstring_view(message, result - 2));
|
||||
LocalFree(message);
|
||||
fmt::memory_buffer actual_message;
|
||||
fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,
|
||||
@@ -143,6 +87,17 @@ TEST(os_test, report_windows_error) {
|
||||
fmt::to_string(out));
|
||||
}
|
||||
|
||||
# if FMT_USE_FCNTL && !defined(__MINGW32__)
|
||||
TEST(file_test, open_windows_file) {
|
||||
using fmt::file;
|
||||
file out = file::open_windows_file(L"test-file",
|
||||
file::WRONLY | file::CREATE | file::TRUNC);
|
||||
out.write("x", 1);
|
||||
file in = file::open_windows_file(L"test-file", file::RDONLY);
|
||||
EXPECT_READ(in, "x");
|
||||
}
|
||||
# endif // FMT_USE_FCNTL && !defined(__MINGW32__)
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
@@ -205,7 +160,7 @@ TEST(buffered_file_test, move_assignment) {
|
||||
TEST(buffered_file_test, move_assignment_closes_file) {
|
||||
buffered_file bf = open_buffered_file();
|
||||
buffered_file bf2 = open_buffered_file();
|
||||
int old_fd = bf2.fileno();
|
||||
int old_fd = bf2.descriptor();
|
||||
bf2 = std::move(bf);
|
||||
EXPECT_TRUE(isclosed(old_fd));
|
||||
}
|
||||
@@ -225,7 +180,7 @@ TEST(buffered_file_test, move_from_temporary_in_assignment) {
|
||||
|
||||
TEST(buffered_file_test, move_from_temporary_in_assignment_closes_file) {
|
||||
buffered_file f = open_buffered_file();
|
||||
int old_fd = f.fileno();
|
||||
int old_fd = f.descriptor();
|
||||
f = open_buffered_file();
|
||||
EXPECT_TRUE(isclosed(old_fd));
|
||||
}
|
||||
@@ -234,7 +189,7 @@ TEST(buffered_file_test, close_file_in_dtor) {
|
||||
int fd = 0;
|
||||
{
|
||||
buffered_file f = open_buffered_file();
|
||||
fd = f.fileno();
|
||||
fd = f.descriptor();
|
||||
}
|
||||
EXPECT_TRUE(isclosed(fd));
|
||||
}
|
||||
@@ -249,7 +204,7 @@ TEST(buffered_file_test, close_error_in_dtor) {
|
||||
// otherwise the system may recycle closed file descriptor when
|
||||
// redirecting the output in EXPECT_STDERR and the second close
|
||||
// will break output redirection.
|
||||
FMT_POSIX(close(f->fileno()));
|
||||
FMT_POSIX(close(f->descriptor()));
|
||||
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||
},
|
||||
system_error_message(EBADF, "cannot close file") + "\n");
|
||||
@@ -257,7 +212,7 @@ TEST(buffered_file_test, close_error_in_dtor) {
|
||||
|
||||
TEST(buffered_file_test, close) {
|
||||
buffered_file f = open_buffered_file();
|
||||
int fd = f.fileno();
|
||||
int fd = f.descriptor();
|
||||
f.close();
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
EXPECT_TRUE(isclosed(fd));
|
||||
@@ -265,15 +220,15 @@ TEST(buffered_file_test, close) {
|
||||
|
||||
TEST(buffered_file_test, close_error) {
|
||||
buffered_file f = open_buffered_file();
|
||||
FMT_POSIX(close(f.fileno()));
|
||||
FMT_POSIX(close(f.descriptor()));
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(buffered_file_test, fileno) {
|
||||
TEST(buffered_file_test, descriptor) {
|
||||
auto f = open_buffered_file();
|
||||
EXPECT_TRUE(f.fileno() != -1);
|
||||
file copy = file::dup(f.fileno());
|
||||
EXPECT_TRUE(f.descriptor() != -1);
|
||||
file copy = file::dup(f.descriptor());
|
||||
EXPECT_READ(copy, file_content);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+50
-57
@@ -5,6 +5,8 @@
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
using fmt::runtime;
|
||||
@@ -30,12 +32,12 @@ template <> struct formatter<test> : formatter<int> {
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const date& d) {
|
||||
auto operator<<(std::ostream& os, const date& d) -> std::ostream& {
|
||||
os << d.year() << '-' << d.month() << '-' << d.day();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::wostream& operator<<(std::wostream& os, const date& d) {
|
||||
auto operator<<(std::wostream& os, const date& d) -> std::wostream& {
|
||||
os << d.year() << L'-' << d.month() << L'-' << d.day();
|
||||
return os;
|
||||
}
|
||||
@@ -47,14 +49,17 @@ template <typename T> type_with_comma_op operator<<(T&, const date&);
|
||||
|
||||
enum streamable_enum {};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, streamable_enum) {
|
||||
auto operator<<(std::ostream& os, streamable_enum) -> std::ostream& {
|
||||
return os << "streamable_enum";
|
||||
}
|
||||
|
||||
enum unstreamable_enum {};
|
||||
auto format_as(unstreamable_enum e) -> int { return e; }
|
||||
|
||||
struct empty_test {};
|
||||
std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; }
|
||||
auto operator<<(std::ostream& os, empty_test) -> std::ostream& {
|
||||
return os << "";
|
||||
}
|
||||
|
||||
namespace fmt {
|
||||
template <> struct formatter<test_string> : ostream_formatter {};
|
||||
@@ -81,13 +86,13 @@ TEST(ostream_test, format_specs) {
|
||||
EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def")));
|
||||
EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def")));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), test_string()),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), test_string()),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), test_string()),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), test_string()),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), test_string()),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
EXPECT_EQ("test ", fmt::format("{0:13}", test_string("test")));
|
||||
@@ -101,9 +106,17 @@ TEST(ostream_test, empty_custom_output) {
|
||||
}
|
||||
|
||||
TEST(ostream_test, print) {
|
||||
std::ostringstream os;
|
||||
fmt::print(os, "Don't {}!", "panic");
|
||||
EXPECT_EQ("Don't panic!", os.str());
|
||||
{
|
||||
std::ostringstream os;
|
||||
fmt::print(os, "Don't {}!", "panic");
|
||||
EXPECT_EQ("Don't panic!", os.str());
|
||||
}
|
||||
|
||||
{
|
||||
std::ostringstream os;
|
||||
fmt::println(os, "Don't {}!", "panic");
|
||||
EXPECT_EQ("Don't panic!\n", os.str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ostream_test, write_to_ostream) {
|
||||
@@ -127,8 +140,8 @@ TEST(ostream_test, write_to_ostream_max_size) {
|
||||
} buffer(max_size);
|
||||
|
||||
struct mock_streambuf : std::streambuf {
|
||||
MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n));
|
||||
std::streamsize xsputn(const char* s, std::streamsize n) override {
|
||||
MOCK_METHOD(std::streamsize, xsputn, (const void*, std::streamsize));
|
||||
auto xsputn(const char* s, std::streamsize n) -> std::streamsize override {
|
||||
const void* v = s;
|
||||
return xsputn(v, n);
|
||||
}
|
||||
@@ -165,15 +178,16 @@ TEST(ostream_test, join_fallback_formatter) {
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
TEST(ostream_test, constexpr_string) {
|
||||
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
|
||||
EXPECT_EQ("a string", format(FMT_STRING("{0}"), test_string("a string")));
|
||||
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), std::string("42")));
|
||||
EXPECT_EQ("a string",
|
||||
fmt::format(FMT_STRING("{0}"), test_string("a string")));
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace fmt_test {
|
||||
struct abc {};
|
||||
|
||||
template <typename Output> Output& operator<<(Output& out, abc) {
|
||||
template <typename Output> auto operator<<(Output& out, abc) -> Output& {
|
||||
return out << "abc";
|
||||
}
|
||||
} // namespace fmt_test
|
||||
@@ -181,7 +195,7 @@ template <typename Output> Output& operator<<(Output& out, abc) {
|
||||
template <typename T> struct test_template {};
|
||||
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& os, test_template<T>) {
|
||||
auto operator<<(std::ostream& os, test_template<T>) -> std::ostream& {
|
||||
return os << 1;
|
||||
}
|
||||
|
||||
@@ -212,45 +226,6 @@ TEST(ostream_test, format_to_n) {
|
||||
EXPECT_EQ("xabx", fmt::string_view(buffer, 4));
|
||||
}
|
||||
|
||||
template <typename T> struct convertible {
|
||||
T value;
|
||||
explicit convertible(const T& val) : value(val) {}
|
||||
operator T() const { return value; }
|
||||
};
|
||||
|
||||
TEST(ostream_test, disable_builtin_ostream_operators) {
|
||||
EXPECT_EQ("42", fmt::format("{:d}", convertible<unsigned short>(42)));
|
||||
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
|
||||
}
|
||||
|
||||
struct streamable_and_convertible_to_bool {
|
||||
operator bool() const { return true; }
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) {
|
||||
return os << "foo";
|
||||
}
|
||||
|
||||
TEST(ostream_test, format_convertible_to_bool) {
|
||||
// operator<< is intentionally not used because of potential ODR violations.
|
||||
EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_bool()), "true");
|
||||
}
|
||||
|
||||
struct streamable_and_convertible_to_string_view {
|
||||
operator fmt::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
streamable_and_convertible_to_string_view) {
|
||||
return os << "bar";
|
||||
}
|
||||
|
||||
TEST(ostream_test, format_convertible_to_string_vew) {
|
||||
// operator<< is intentionally not used because of potential ODR violations.
|
||||
EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_string_view()),
|
||||
"foo");
|
||||
}
|
||||
|
||||
struct copyfmt_test {};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, copyfmt_test) {
|
||||
@@ -279,7 +254,7 @@ TEST(ostream_test, range) {
|
||||
struct abstract {
|
||||
virtual ~abstract() = default;
|
||||
virtual void f() = 0;
|
||||
friend std::ostream& operator<<(std::ostream& os, const abstract&) {
|
||||
friend auto operator<<(std::ostream& os, const abstract&) -> std::ostream& {
|
||||
return os;
|
||||
}
|
||||
};
|
||||
@@ -296,3 +271,21 @@ TEST(ostream_test, is_formattable) {
|
||||
EXPECT_TRUE(fmt::is_formattable<std::string>());
|
||||
EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>());
|
||||
}
|
||||
|
||||
struct streamable_and_unformattable {};
|
||||
|
||||
auto operator<<(std::ostream& os, streamable_and_unformattable)
|
||||
-> std::ostream& {
|
||||
return os << "foo";
|
||||
}
|
||||
|
||||
TEST(ostream_test, streamed) {
|
||||
EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>());
|
||||
EXPECT_EQ(fmt::format("{}", fmt::streamed(streamable_and_unformattable())),
|
||||
"foo");
|
||||
}
|
||||
|
||||
TEST(ostream_test, closed_ofstream) {
|
||||
std::ofstream ofs;
|
||||
fmt::print(ofs, "discard");
|
||||
}
|
||||
Vendored
+3
-9
@@ -6,7 +6,7 @@
|
||||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
@@ -72,12 +72,6 @@ int test::open(const char* path, int oflag, int mode) {
|
||||
EMULATE_EINTR(open, -1);
|
||||
return ::open(path, oflag, mode);
|
||||
}
|
||||
#else
|
||||
errno_t test::sopen_s(int* pfh, const char* filename, int oflag, int shflag,
|
||||
int pmode) {
|
||||
EMULATE_EINTR(open, EINTR);
|
||||
return _sopen_s(pfh, filename, oflag, shflag, pmode);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -220,11 +214,11 @@ TEST(os_test, getpagesize) {
|
||||
}
|
||||
|
||||
TEST(file_test, open_retry) {
|
||||
# ifndef _WIN32
|
||||
write_file("temp", "there must be something here");
|
||||
std::unique_ptr<file> f{nullptr};
|
||||
EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
|
||||
"cannot open file temp");
|
||||
# ifndef _WIN32
|
||||
char c = 0;
|
||||
f->read(&c, 1);
|
||||
# endif
|
||||
@@ -438,7 +432,7 @@ TEST(buffered_file_test, fileno_no_retry) {
|
||||
file::pipe(read_end, write_end);
|
||||
buffered_file f = read_end.fdopen("r");
|
||||
fileno_count = 1;
|
||||
EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
|
||||
EXPECT_SYSTEM_ERROR((f.descriptor)(), EINTR, "cannot get file descriptor");
|
||||
EXPECT_EQ(2, fileno_count);
|
||||
fileno_count = 0;
|
||||
}
|
||||
|
||||
Vendored
-2
@@ -37,8 +37,6 @@ int fstat(int fd, struct stat* buf);
|
||||
#else
|
||||
typedef unsigned size_t;
|
||||
typedef int ssize_t;
|
||||
errno_t sopen_s(int* pfh, const char* filename, int oflag, int shflag,
|
||||
int pmode);
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
Vendored
+5
-9
@@ -225,10 +225,8 @@ TEST(printf_test, hash_flag) {
|
||||
EXPECT_PRINTF("-42.0000", "%#g", -42.0);
|
||||
EXPECT_PRINTF("-42.0000", "%#G", -42.0);
|
||||
|
||||
safe_sprintf(buffer, "%#a", 16.0);
|
||||
EXPECT_PRINTF(buffer, "%#a", 16.0);
|
||||
safe_sprintf(buffer, "%#A", 16.0);
|
||||
EXPECT_PRINTF(buffer, "%#A", 16.0);
|
||||
EXPECT_PRINTF("0x1.p+4", "%#a", 16.0);
|
||||
EXPECT_PRINTF("0X1.P+4", "%#A", 16.0);
|
||||
|
||||
// '#' flag is ignored for non-numeric types.
|
||||
EXPECT_PRINTF("x", "%#c", 'x');
|
||||
@@ -239,7 +237,7 @@ TEST(printf_test, width) {
|
||||
|
||||
// Width cannot be specified twice.
|
||||
EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error,
|
||||
"invalid type specifier");
|
||||
"invalid format specifier");
|
||||
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}d", big_num), 42), format_error,
|
||||
"number is too big");
|
||||
@@ -407,10 +405,7 @@ TEST(printf_test, length) {
|
||||
EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max);
|
||||
}
|
||||
|
||||
TEST(printf_test, bool) {
|
||||
EXPECT_PRINTF("1", "%d", true);
|
||||
EXPECT_PRINTF("true", "%s", true);
|
||||
}
|
||||
TEST(printf_test, bool) { EXPECT_PRINTF("1", "%d", true); }
|
||||
|
||||
TEST(printf_test, int) {
|
||||
EXPECT_PRINTF("-42", "%d", -42);
|
||||
@@ -504,6 +499,7 @@ TEST(printf_test, pointer) {
|
||||
}
|
||||
|
||||
enum test_enum { answer = 42 };
|
||||
auto format_as(test_enum e) -> int { return e; }
|
||||
|
||||
TEST(printf_test, enum) {
|
||||
EXPECT_PRINTF("42", "%d", answer);
|
||||
|
||||
Vendored
+140
-43
@@ -12,6 +12,8 @@
|
||||
#include "fmt/ranges.h"
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -21,7 +23,7 @@
|
||||
# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
||||
#endif
|
||||
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1910
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1910
|
||||
# define FMT_RANGES_TEST_ENABLE_JOIN
|
||||
# define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
||||
#endif
|
||||
@@ -40,6 +42,8 @@ TEST(ranges_test, format_2d_array) {
|
||||
TEST(ranges_test, format_array_of_literals) {
|
||||
const char* arr[] = {"1234", "abcd"};
|
||||
EXPECT_EQ(fmt::format("{}", arr), "[\"1234\", \"abcd\"]");
|
||||
EXPECT_EQ(fmt::format("{:n}", arr), "\"1234\", \"abcd\"");
|
||||
EXPECT_EQ(fmt::format("{:n:}", arr), "1234, abcd");
|
||||
}
|
||||
#endif // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
||||
|
||||
@@ -47,17 +51,28 @@ TEST(ranges_test, format_vector) {
|
||||
auto v = std::vector<int>{1, 2, 3, 5, 7, 11};
|
||||
EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]");
|
||||
EXPECT_EQ(fmt::format("{::#x}", v), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]");
|
||||
EXPECT_EQ(fmt::format("{:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb");
|
||||
|
||||
auto vc = std::vector<char>{'a', 'b', 'c'};
|
||||
auto vvc = std::vector<std::vector<char>>{vc, vc};
|
||||
EXPECT_EQ(fmt::format("{}", vc), "['a', 'b', 'c']");
|
||||
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'");
|
||||
EXPECT_EQ(fmt::format("{:n:n:}", vvc), "a, b, c, a, b, c");
|
||||
}
|
||||
|
||||
TEST(ranges_test, format_vector2) {
|
||||
auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}};
|
||||
EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]");
|
||||
EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]");
|
||||
EXPECT_EQ(fmt::format("{:n:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb");
|
||||
}
|
||||
|
||||
TEST(ranges_test, format_map) {
|
||||
auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}};
|
||||
EXPECT_EQ(fmt::format("{}", m), "{\"one\": 1, \"two\": 2}");
|
||||
EXPECT_EQ(fmt::format("{:n}", m), "\"one\": 1, \"two\": 2");
|
||||
}
|
||||
|
||||
TEST(ranges_test, format_set) {
|
||||
@@ -70,13 +85,9 @@ struct box {
|
||||
int value;
|
||||
};
|
||||
|
||||
auto begin(const box& b) -> const int* {
|
||||
return &b.value;
|
||||
}
|
||||
auto begin(const box& b) -> const int* { return &b.value; }
|
||||
|
||||
auto end(const box& b) -> const int* {
|
||||
return &b.value + 1;
|
||||
}
|
||||
auto end(const box& b) -> const int* { return &b.value + 1; }
|
||||
} // namespace adl
|
||||
|
||||
TEST(ranges_test, format_adl_begin_end) {
|
||||
@@ -89,11 +100,42 @@ TEST(ranges_test, format_pair) {
|
||||
EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)");
|
||||
}
|
||||
|
||||
struct unformattable {};
|
||||
|
||||
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("{}", std::tuple<>()), "()");
|
||||
|
||||
EXPECT_TRUE((fmt::is_formattable<std::tuple<>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable, int>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::tuple<int, unformattable>>::value));
|
||||
EXPECT_FALSE(
|
||||
(fmt::is_formattable<std::tuple<unformattable, unformattable>>::value));
|
||||
EXPECT_TRUE((fmt::is_formattable<std::tuple<int, float>>::value));
|
||||
}
|
||||
|
||||
struct not_default_formattable {};
|
||||
struct bad_format {};
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct formatter<not_default_formattable> {
|
||||
auto parse(format_parse_context&) -> const char* { throw bad_format(); }
|
||||
auto format(not_default_formattable, format_context& ctx)
|
||||
-> format_context::iterator {
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(ranges_test, tuple_parse_calls_element_parse) {
|
||||
auto f = fmt::formatter<std::tuple<not_default_formattable>>();
|
||||
auto ctx = fmt::format_parse_context("");
|
||||
EXPECT_THROW(f.parse(ctx), bad_format);
|
||||
}
|
||||
|
||||
#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
||||
@@ -137,32 +179,24 @@ TEST(ranges_test, format_to) {
|
||||
EXPECT_STREQ(buf, "[1, 2, 3]");
|
||||
}
|
||||
|
||||
struct path_like {
|
||||
template <typename Char> struct path_like {
|
||||
const path_like* begin() const;
|
||||
const path_like* end() const;
|
||||
|
||||
operator std::string() const;
|
||||
operator std::basic_string<Char>() const;
|
||||
};
|
||||
|
||||
TEST(ranges_test, path_like) {
|
||||
EXPECT_FALSE((fmt::is_range<path_like, char>::value));
|
||||
TEST(ranges_test, disabled_range_formatting_of_path) {
|
||||
// Range formatting of path is disabled because of infinite recursion
|
||||
// (path element is itself a path).
|
||||
EXPECT_EQ((fmt::range_format_kind<path_like<char>, char>::value),
|
||||
fmt::range_format::disabled);
|
||||
EXPECT_EQ((fmt::range_format_kind<path_like<wchar_t>, char>::value),
|
||||
fmt::range_format::disabled);
|
||||
}
|
||||
|
||||
#ifdef FMT_USE_STRING_VIEW
|
||||
struct string_like {
|
||||
const char* begin();
|
||||
const char* end();
|
||||
operator fmt::string_view() const { return "foo"; }
|
||||
operator std::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(ranges_test, format_string_like) {
|
||||
EXPECT_EQ(fmt::format("{}", string_like()), "foo");
|
||||
}
|
||||
#endif // FMT_USE_STRING_VIEW
|
||||
|
||||
// A range that provides non-const only begin()/end() to test fmt::join handles
|
||||
// that.
|
||||
// A range that provides non-const only begin()/end() to test fmt::join
|
||||
// handles that.
|
||||
//
|
||||
// Some ranges (e.g. those produced by range-v3's views::filter()) can cache
|
||||
// information during iteration so they only provide non-const begin()/end().
|
||||
@@ -186,7 +220,7 @@ template <typename T> class noncopyable_range {
|
||||
std::vector<T> vec;
|
||||
|
||||
public:
|
||||
using const_iterator = typename ::std::vector<T>::const_iterator;
|
||||
using iterator = typename ::std::vector<T>::iterator;
|
||||
|
||||
template <typename... Args>
|
||||
explicit noncopyable_range(Args&&... args)
|
||||
@@ -195,8 +229,8 @@ template <typename T> class noncopyable_range {
|
||||
noncopyable_range(noncopyable_range const&) = delete;
|
||||
noncopyable_range(noncopyable_range&) = delete;
|
||||
|
||||
const_iterator begin() const { return vec.begin(); }
|
||||
const_iterator end() const { return vec.end(); }
|
||||
iterator begin() { return vec.begin(); }
|
||||
iterator end() { return vec.end(); }
|
||||
};
|
||||
|
||||
TEST(ranges_test, range) {
|
||||
@@ -217,15 +251,14 @@ TEST(ranges_test, range) {
|
||||
}
|
||||
|
||||
enum test_enum { foo };
|
||||
auto format_as(test_enum e) -> int { return e; }
|
||||
|
||||
TEST(ranges_test, enum_range) {
|
||||
auto v = std::vector<test_enum>{test_enum::foo};
|
||||
EXPECT_EQ(fmt::format("{}", v), "[0]");
|
||||
}
|
||||
|
||||
#if !FMT_MSC_VER
|
||||
struct unformattable {};
|
||||
|
||||
#if !FMT_MSC_VERSION
|
||||
TEST(ranges_test, unformattable_range) {
|
||||
EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,
|
||||
fmt::format_context>::value));
|
||||
@@ -357,7 +390,7 @@ TEST(ranges_test, is_printable) {
|
||||
EXPECT_FALSE(is_printable(0x110000));
|
||||
}
|
||||
|
||||
TEST(ranges_test, escape_string) {
|
||||
TEST(ranges_test, escape) {
|
||||
using vec = std::vector<std::string>;
|
||||
EXPECT_EQ(fmt::format("{}", vec{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]");
|
||||
EXPECT_EQ(fmt::format("{}", vec{"\x07"}), "[\"\\x07\"]");
|
||||
@@ -368,21 +401,22 @@ TEST(ranges_test, escape_string) {
|
||||
EXPECT_EQ(fmt::format("{}", vec{"\xcd\xb8"}), "[\"\\u0378\"]");
|
||||
// Unassigned Unicode code points.
|
||||
EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
|
||||
// Broken utf-8.
|
||||
EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}),
|
||||
"[\"\\xf4\\x8f\\xbf\\xc0\"]");
|
||||
EXPECT_EQ(fmt::format("{}", vec{"\xf0\x28"}), "[\"\\xf0(\"]");
|
||||
EXPECT_EQ(fmt::format("{}", vec{"\xe1\x28"}), "[\"\\xe1(\"]");
|
||||
EXPECT_EQ(fmt::format("{}", vec{std::string("\xf0\x28\0\0anything", 12)}),
|
||||
"[\"\\xf0(\\x00\\x00anything\"]");
|
||||
|
||||
// Correct utf-8.
|
||||
EXPECT_EQ(fmt::format("{}", vec{"🦄"}), "[\"🦄\"]");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FMT_USE_STRING_VIEW
|
||||
struct convertible_to_string_view {
|
||||
operator std::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(ranges_test, escape_convertible_to_string_view) {
|
||||
EXPECT_EQ(fmt::format("{}", std::vector<convertible_to_string_view>(1)),
|
||||
"[\"foo\"]");
|
||||
EXPECT_EQ(fmt::format("{}", std::vector<std::vector<char>>{{'x'}}),
|
||||
"[['x']]");
|
||||
EXPECT_EQ(fmt::format("{}", std::tuple<std::vector<char>>{{'x'}}), "(['x'])");
|
||||
}
|
||||
#endif // FMT_USE_STRING_VIEW
|
||||
|
||||
template <typename R> struct fmt_ref_view {
|
||||
R* r;
|
||||
@@ -398,3 +432,66 @@ TEST(ranges_test, range_of_range_of_mixed_const) {
|
||||
fmt_ref_view<decltype(v)> r{&v};
|
||||
EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]");
|
||||
}
|
||||
|
||||
TEST(ranges_test, vector_char) {
|
||||
EXPECT_EQ(fmt::format("{}", std::vector<char>{'a', 'b'}), "['a', 'b']");
|
||||
}
|
||||
|
||||
TEST(ranges_test, container_adaptor) {
|
||||
{
|
||||
using fmt::detail::is_container_adaptor_like;
|
||||
using T = std::nullptr_t;
|
||||
static_assert(is_container_adaptor_like<std::stack<T>>::value, "");
|
||||
static_assert(is_container_adaptor_like<std::queue<T>>::value, "");
|
||||
static_assert(is_container_adaptor_like<std::priority_queue<T>>::value, "");
|
||||
static_assert(!is_container_adaptor_like<std::vector<T>>::value, "");
|
||||
}
|
||||
|
||||
{
|
||||
auto s = std::stack<int>();
|
||||
s.push(1);
|
||||
s.push(2);
|
||||
EXPECT_EQ(fmt::format("{}", s), "[1, 2]");
|
||||
EXPECT_EQ(fmt::format("{}", const_cast<const decltype(s)&>(s)), "[1, 2]");
|
||||
}
|
||||
|
||||
{
|
||||
auto q = std::queue<int>();
|
||||
q.push(1);
|
||||
q.push(2);
|
||||
EXPECT_EQ(fmt::format("{}", q), "[1, 2]");
|
||||
}
|
||||
|
||||
{
|
||||
auto q = std::priority_queue<int>();
|
||||
q.push(3);
|
||||
q.push(1);
|
||||
q.push(2);
|
||||
q.push(4);
|
||||
EXPECT_EQ(fmt::format("{}", q), "[4, 3, 2, 1]");
|
||||
}
|
||||
|
||||
{
|
||||
auto s = std::stack<char, std::string>();
|
||||
s.push('a');
|
||||
s.push('b');
|
||||
// See https://cplusplus.github.io/LWG/issue3881.
|
||||
EXPECT_EQ(fmt::format("{}", s), "['a', 'b']");
|
||||
}
|
||||
|
||||
{
|
||||
struct my_container_adaptor {
|
||||
using value_type = int;
|
||||
using container_type = std::vector<value_type>;
|
||||
void push(const value_type& v) { c.push_back(v); }
|
||||
|
||||
protected:
|
||||
container_type c;
|
||||
};
|
||||
|
||||
auto m = my_container_adaptor();
|
||||
m.push(1);
|
||||
m.push(2);
|
||||
EXPECT_EQ(fmt::format("{}", m), "[1, 2]");
|
||||
}
|
||||
}
|
||||
Vendored
+1
-1
@@ -65,7 +65,7 @@ TEST(scan_test, read_string_view) {
|
||||
EXPECT_EQ(s, "foo");
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
#ifdef FMT_HAVE_STRPTIME
|
||||
namespace fmt {
|
||||
template <> struct scanner<tm> {
|
||||
std::string format;
|
||||
|
||||
Vendored
+16
-11
@@ -42,7 +42,7 @@ struct scan_context {
|
||||
public:
|
||||
using iterator = const char*;
|
||||
|
||||
explicit scan_context(string_view input) : input_(input) {}
|
||||
explicit FMT_CONSTEXPR scan_context(string_view input) : input_(input) {}
|
||||
|
||||
iterator begin() const { return input_.data(); }
|
||||
iterator end() const { return begin() + input_.size(); }
|
||||
@@ -83,17 +83,21 @@ class scan_arg {
|
||||
// TODO: more types
|
||||
};
|
||||
|
||||
scan_arg() : type(scan_type::none_type) {}
|
||||
scan_arg(int& value) : type(scan_type::int_type), int_value(&value) {}
|
||||
scan_arg(unsigned& value) : type(scan_type::uint_type), uint_value(&value) {}
|
||||
scan_arg(long long& value)
|
||||
FMT_CONSTEXPR scan_arg() : type(scan_type::none_type), int_value(nullptr) {}
|
||||
FMT_CONSTEXPR scan_arg(int& value)
|
||||
: type(scan_type::int_type), int_value(&value) {}
|
||||
FMT_CONSTEXPR scan_arg(unsigned& value)
|
||||
: type(scan_type::uint_type), uint_value(&value) {}
|
||||
FMT_CONSTEXPR scan_arg(long long& value)
|
||||
: type(scan_type::long_long_type), long_long_value(&value) {}
|
||||
scan_arg(unsigned long long& value)
|
||||
FMT_CONSTEXPR scan_arg(unsigned long long& value)
|
||||
: type(scan_type::ulong_long_type), ulong_long_value(&value) {}
|
||||
scan_arg(std::string& value) : type(scan_type::string_type), string(&value) {}
|
||||
scan_arg(fmt::string_view& value)
|
||||
FMT_CONSTEXPR scan_arg(std::string& value)
|
||||
: type(scan_type::string_type), string(&value) {}
|
||||
FMT_CONSTEXPR scan_arg(fmt::string_view& value)
|
||||
: type(scan_type::string_view_type), string_view(&value) {}
|
||||
template <typename T> scan_arg(T& value) : type(scan_type::custom_type) {
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR scan_arg(T& value) : type(scan_type::custom_type) {
|
||||
custom.value = &value;
|
||||
custom.scan = scan_custom_arg<T>;
|
||||
}
|
||||
@@ -114,7 +118,7 @@ struct scan_args {
|
||||
const detail::scan_arg* data;
|
||||
|
||||
template <size_t N>
|
||||
scan_args(const std::array<detail::scan_arg, N>& store)
|
||||
FMT_CONSTEXPR scan_args(const std::array<detail::scan_arg, N>& store)
|
||||
: size(N), data(store.data()) {
|
||||
static_assert(N < INT_MAX, "too many arguments");
|
||||
}
|
||||
@@ -154,7 +158,8 @@ struct scan_handler : error_handler {
|
||||
}
|
||||
|
||||
public:
|
||||
scan_handler(string_view format, string_view input, scan_args args)
|
||||
FMT_CONSTEXPR scan_handler(string_view format, string_view input,
|
||||
scan_args args)
|
||||
: parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
|
||||
|
||||
const char* pos() const { return scan_ctx_.begin(); }
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
cmake_minimum_required(VERSION 3.8...3.25)
|
||||
|
||||
project(fmt-link CXX)
|
||||
|
||||
|
||||
Vendored
+221
@@ -0,0 +1,221 @@
|
||||
// Formatting library for C++ - tests of formatters for standard library types
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/std.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "fmt/os.h" // fmt::system_category
|
||||
#include "fmt/ranges.h"
|
||||
#include "gtest-extra.h" // StartsWith
|
||||
|
||||
using testing::StartsWith;
|
||||
|
||||
#ifdef __cpp_lib_filesystem
|
||||
TEST(std_test, path) {
|
||||
EXPECT_EQ(fmt::format("{:8}", std::filesystem::path("foo")), "\"foo\" ");
|
||||
EXPECT_EQ(fmt::format("{}", std::filesystem::path("foo\"bar.txt")),
|
||||
"\"foo\\\"bar.txt\"");
|
||||
EXPECT_EQ(fmt::format("{:?}", std::filesystem::path("foo\"bar.txt")),
|
||||
"\"foo\\\"bar.txt\"");
|
||||
|
||||
# ifdef _WIN32
|
||||
EXPECT_EQ(fmt::format("{}", std::filesystem::path(
|
||||
L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448"
|
||||
L"\x0447\x044B\x043D\x0430")),
|
||||
"\"Шчучыншчына\"");
|
||||
EXPECT_EQ(fmt::format("{}", std::filesystem::path(L"\xd800")), "\"\\ud800\"");
|
||||
# endif
|
||||
}
|
||||
|
||||
// Test ambiguity problem described in #2954.
|
||||
TEST(ranges_std_test, format_vector_path) {
|
||||
auto p = std::filesystem::path("foo/bar.txt");
|
||||
auto c = std::vector<std::string>{"abc", "def"};
|
||||
EXPECT_EQ(fmt::format("path={}, range={}", p, c),
|
||||
"path=\"foo/bar.txt\", range=[\"abc\", \"def\"]");
|
||||
}
|
||||
|
||||
// Test that path is not escaped twice in the debug mode.
|
||||
TEST(ranges_std_test, format_quote_path) {
|
||||
auto vec =
|
||||
std::vector<std::filesystem::path>{"path1/file1.txt", "path2/file2.txt"};
|
||||
EXPECT_EQ(fmt::format("{}", vec),
|
||||
"[\"path1/file1.txt\", \"path2/file2.txt\"]");
|
||||
# ifdef __cpp_lib_optional
|
||||
auto o = std::optional<std::filesystem::path>("path/file.txt");
|
||||
EXPECT_EQ(fmt::format("{}", o), "optional(\"path/file.txt\")");
|
||||
EXPECT_EQ(fmt::format("{:?}", o), "optional(\"path/file.txt\")");
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(std_test, thread_id) {
|
||||
EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty());
|
||||
}
|
||||
|
||||
TEST(std_test, optional) {
|
||||
#ifdef __cpp_lib_optional
|
||||
EXPECT_EQ(fmt::format("{}", std::optional<int>{}), "none");
|
||||
EXPECT_EQ(fmt::format("{}", std::pair{1, "second"}), "(1, \"second\")");
|
||||
EXPECT_EQ(fmt::format("{}", std::vector{std::optional{1}, std::optional{2},
|
||||
std::optional{3}}),
|
||||
"[optional(1), optional(2), optional(3)]");
|
||||
EXPECT_EQ(
|
||||
fmt::format("{}", std::optional<std::optional<const char*>>{{"nested"}}),
|
||||
"optional(optional(\"nested\"))");
|
||||
EXPECT_EQ(
|
||||
fmt::format("{:<{}}", std::optional{std::string{"left aligned"}}, 30),
|
||||
"optional(\"left aligned\" )");
|
||||
EXPECT_EQ(
|
||||
fmt::format("{::d}", std::optional{std::vector{'h', 'e', 'l', 'l', 'o'}}),
|
||||
"optional([104, 101, 108, 108, 111])");
|
||||
EXPECT_EQ(fmt::format("{}", std::optional{std::string{"string"}}),
|
||||
"optional(\"string\")");
|
||||
EXPECT_EQ(fmt::format("{}", std::optional{'C'}), "optional(\'C\')");
|
||||
EXPECT_EQ(fmt::format("{:.{}f}", std::optional{3.14}, 1), "optional(3.1)");
|
||||
|
||||
struct unformattable {};
|
||||
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::optional<unformattable>>::value));
|
||||
EXPECT_TRUE((fmt::is_formattable<std::optional<int>>::value));
|
||||
#endif
|
||||
}
|
||||
|
||||
struct throws_on_move {
|
||||
throws_on_move() = default;
|
||||
|
||||
[[noreturn]] throws_on_move(throws_on_move&&) {
|
||||
throw std::runtime_error("Thrown by throws_on_move");
|
||||
}
|
||||
|
||||
throws_on_move(const throws_on_move&) = default;
|
||||
};
|
||||
|
||||
namespace fmt {
|
||||
template <> struct formatter<throws_on_move> : formatter<string_view> {
|
||||
auto format(const throws_on_move&, format_context& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
string_view str("<throws_on_move>");
|
||||
return formatter<string_view>::format(str, ctx);
|
||||
}
|
||||
};
|
||||
} // namespace fmt
|
||||
|
||||
TEST(std_test, variant) {
|
||||
#ifdef __cpp_lib_variant
|
||||
EXPECT_EQ(fmt::format("{}", std::monostate{}), "monostate");
|
||||
using V0 = std::variant<int, float, std::string, char>;
|
||||
V0 v0(42);
|
||||
V0 v1(1.5f);
|
||||
V0 v2("hello");
|
||||
V0 v3('i');
|
||||
EXPECT_EQ(fmt::format("{}", v0), "variant(42)");
|
||||
EXPECT_EQ(fmt::format("{}", v1), "variant(1.5)");
|
||||
EXPECT_EQ(fmt::format("{}", v2), "variant(\"hello\")");
|
||||
EXPECT_EQ(fmt::format("{}", v3), "variant('i')");
|
||||
|
||||
struct unformattable {};
|
||||
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable, int>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<int, unformattable>>::value));
|
||||
EXPECT_FALSE(
|
||||
(fmt::is_formattable<std::variant<unformattable, unformattable>>::value));
|
||||
EXPECT_TRUE((fmt::is_formattable<std::variant<int, float>>::value));
|
||||
|
||||
using V1 = std::variant<std::monostate, std::string, std::string>;
|
||||
V1 v4{};
|
||||
V1 v5{std::in_place_index<1>, "yes, this is variant"};
|
||||
|
||||
EXPECT_EQ(fmt::format("{}", v4), "variant(monostate)");
|
||||
EXPECT_EQ(fmt::format("{}", v5), "variant(\"yes, this is variant\")");
|
||||
|
||||
volatile int i = 42; // Test compile error before GCC 11 described in #3068.
|
||||
EXPECT_EQ(fmt::format("{}", i), "42");
|
||||
|
||||
std::variant<std::monostate, throws_on_move> v6;
|
||||
|
||||
try {
|
||||
throws_on_move thrower;
|
||||
v6.emplace<throws_on_move>(std::move(thrower));
|
||||
} catch (const std::runtime_error&) {
|
||||
}
|
||||
// v6 is now valueless by exception
|
||||
|
||||
EXPECT_EQ(fmt::format("{}", v6), "variant(valueless by exception)");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(std_test, error_code) {
|
||||
EXPECT_EQ("generic:42",
|
||||
fmt::format(FMT_STRING("{0}"),
|
||||
std::error_code(42, std::generic_category())));
|
||||
EXPECT_EQ("system:42",
|
||||
fmt::format(FMT_STRING("{0}"),
|
||||
std::error_code(42, fmt::system_category())));
|
||||
EXPECT_EQ("system:-42",
|
||||
fmt::format(FMT_STRING("{0}"),
|
||||
std::error_code(-42, fmt::system_category())));
|
||||
}
|
||||
|
||||
template <typename Catch> void exception_test() {
|
||||
try {
|
||||
throw std::runtime_error("Test Exception");
|
||||
} catch (const Catch& ex) {
|
||||
EXPECT_EQ("Test Exception", fmt::format("{}", ex));
|
||||
EXPECT_EQ("std::runtime_error: Test Exception", fmt::format("{:t}", ex));
|
||||
}
|
||||
}
|
||||
|
||||
namespace my_ns1 {
|
||||
namespace my_ns2 {
|
||||
struct my_exception : public std::exception {
|
||||
private:
|
||||
std::string msg;
|
||||
|
||||
public:
|
||||
my_exception(const std::string& s) : msg(s) {}
|
||||
const char* what() const noexcept override;
|
||||
};
|
||||
const char* my_exception::what() const noexcept { return msg.c_str(); }
|
||||
} // namespace my_ns2
|
||||
} // namespace my_ns1
|
||||
|
||||
TEST(std_test, exception) {
|
||||
exception_test<std::exception>();
|
||||
exception_test<std::runtime_error>();
|
||||
|
||||
try {
|
||||
using namespace my_ns1::my_ns2;
|
||||
throw my_exception("My Exception");
|
||||
} catch (const std::exception& ex) {
|
||||
EXPECT_EQ("my_ns1::my_ns2::my_exception: My Exception",
|
||||
fmt::format("{:t}", ex));
|
||||
EXPECT_EQ("My Exception", fmt::format("{:}", ex));
|
||||
}
|
||||
|
||||
try {
|
||||
throw std::system_error(std::error_code(), "message");
|
||||
} catch (const std::system_error& ex) {
|
||||
EXPECT_THAT(fmt::format("{:t}", ex), StartsWith("std::system_error: "));
|
||||
}
|
||||
|
||||
#ifdef __cpp_lib_filesystem
|
||||
// Tests that the inline namespace is stripped out, e.g.
|
||||
// std::filesystem::__cxx11::* -> std::filesystem::*.
|
||||
try {
|
||||
throw std::filesystem::filesystem_error("message", std::error_code());
|
||||
} catch (const std::filesystem::filesystem_error& ex) {
|
||||
EXPECT_THAT(fmt::format("{:t}", ex),
|
||||
StartsWith("std::filesystem::filesystem_error: "));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Vendored
+2
-3
@@ -15,9 +15,6 @@
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# include <crtdbg.h>
|
||||
#else
|
||||
# define _CrtSetReportFile(a, b)
|
||||
# define _CrtSetReportMode(a, b)
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -28,11 +25,13 @@ int main(int argc, char** argv) {
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
|
||||
SEM_NOOPENFILEERRORBOX);
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
// Disable message boxes on assertion failures.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
|
||||
#endif
|
||||
try {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
|
||||
Vendored
+4
-4
@@ -18,12 +18,12 @@ using testing::Contains;
|
||||
TEST(unicode_test, is_utf8) { EXPECT_TRUE(fmt::detail::is_utf8()); }
|
||||
|
||||
TEST(unicode_test, legacy_locale) {
|
||||
auto loc = get_locale("ru_RU.CP1251", "Russian_Russia.1251");
|
||||
auto loc = get_locale("be_BY.CP1251", "Belarusian_Belarus.1251");
|
||||
if (loc == std::locale::classic()) return;
|
||||
|
||||
auto s = std::string();
|
||||
try {
|
||||
s = fmt::format(loc, "День недели: {:L}", fmt::weekday(1));
|
||||
s = fmt::format(loc, "Дзень тыдня: {:L}", fmt::weekday(1));
|
||||
} catch (const fmt::format_error& e) {
|
||||
// Formatting can fail due to an unsupported encoding.
|
||||
fmt::print("Format error: {}\n", e.what());
|
||||
@@ -38,11 +38,11 @@ TEST(unicode_test, legacy_locale) {
|
||||
os << std::put_time(&tm, "%a");
|
||||
auto wd = os.str();
|
||||
if (wd == "??") {
|
||||
EXPECT_EQ(s, "День недели: ??");
|
||||
EXPECT_EQ(s, "Дзень тыдня: ??");
|
||||
fmt::print("std::locale gives ?? as a weekday.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
EXPECT_THAT((std::vector<std::string>{"День недели: пн", "День недели: Пн"}),
|
||||
EXPECT_THAT((std::vector<std::string>{"Дзень тыдня: пн", "Дзень тыдня: Пан"}),
|
||||
Contains(s));
|
||||
}
|
||||
Vendored
+2
-6
@@ -10,11 +10,7 @@
|
||||
#include <locale>
|
||||
#include <string>
|
||||
|
||||
#ifdef FMT_MODULE_TEST
|
||||
import fmt;
|
||||
#else
|
||||
# include "fmt/os.h"
|
||||
#endif // FMT_MODULE_TEST
|
||||
#include "fmt/os.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define FMT_VSNPRINTF vsprintf_s
|
||||
@@ -81,5 +77,5 @@ class date {
|
||||
int day() const { return day_; }
|
||||
};
|
||||
|
||||
// Returns a locale with the given name if available or classic locale othewise.
|
||||
// Returns a locale with the given name if available or classic locale otherwise.
|
||||
std::locale get_locale(const char* name, const char* alt_name = nullptr);
|
||||
Vendored
+152
-81
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "fmt/xchar.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <complex>
|
||||
#include <cwchar>
|
||||
#include <vector>
|
||||
@@ -15,31 +16,21 @@
|
||||
#include "fmt/color.h"
|
||||
#include "fmt/ostream.h"
|
||||
#include "fmt/ranges.h"
|
||||
#include "fmt/std.h"
|
||||
#include "gtest-extra.h" // Contains
|
||||
#include "util.h" // get_locale
|
||||
|
||||
using fmt::detail::max_value;
|
||||
using testing::Contains;
|
||||
|
||||
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()};
|
||||
}
|
||||
#if defined(__MINGW32__) && !defined(_UCRT)
|
||||
// Only C89 conversion specifiers when using MSVCRT instead of UCRT
|
||||
# define FMT_HAS_C99_STRFTIME 0
|
||||
#else
|
||||
# define FMT_HAS_C99_STRFTIME 1
|
||||
#endif
|
||||
|
||||
struct non_string {};
|
||||
} // namespace test_ns
|
||||
|
||||
template <typename T> class is_string_test : public testing::Test {};
|
||||
|
||||
@@ -61,12 +52,11 @@ TYPED_TEST(is_string_test, is_string) {
|
||||
using fmt_string_view = fmt::detail::std_string_view<TypeParam>;
|
||||
EXPECT_TRUE(std::is_empty<fmt_string_view>::value !=
|
||||
fmt::detail::is_string<fmt_string_view>::value);
|
||||
EXPECT_TRUE(fmt::detail::is_string<test_ns::test_string<TypeParam>>::value);
|
||||
EXPECT_FALSE(fmt::detail::is_string<test_ns::non_string>::value);
|
||||
EXPECT_FALSE(fmt::detail::is_string<non_string>::value);
|
||||
}
|
||||
|
||||
// std::is_constructible is broken in MSVC until version 2015.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900
|
||||
struct explicitly_convertible_to_wstring_view {
|
||||
explicit operator fmt::wstring_view() const { return L"foo"; }
|
||||
};
|
||||
@@ -84,7 +74,7 @@ TEST(xchar_test, format) {
|
||||
EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2));
|
||||
EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc"));
|
||||
EXPECT_EQ(L"z", fmt::format(L"{}", L'z'));
|
||||
EXPECT_THROW(fmt::format(L"{:*\x343E}", 42), fmt::format_error);
|
||||
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'));
|
||||
@@ -98,12 +88,13 @@ TEST(xchar_test, is_formattable) {
|
||||
}
|
||||
|
||||
TEST(xchar_test, compile_time_string) {
|
||||
#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L
|
||||
EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42));
|
||||
EXPECT_EQ(fmt::format(fmt::wformat_string<int>(L"{}"), 42), L"42");
|
||||
#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L
|
||||
EXPECT_EQ(fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42), L"42");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if __cplusplus > 201103L
|
||||
#if FMT_CPLUSPLUS > 201103L
|
||||
struct custom_char {
|
||||
int value;
|
||||
custom_char() = default;
|
||||
@@ -111,10 +102,12 @@ struct custom_char {
|
||||
template <typename T>
|
||||
constexpr custom_char(T val) : value(static_cast<int>(val)) {}
|
||||
|
||||
operator int() const { return value; }
|
||||
operator char() const {
|
||||
return value <= 0xff ? static_cast<char>(value) : '\0';
|
||||
}
|
||||
};
|
||||
|
||||
int to_ascii(custom_char c) { return c; }
|
||||
auto to_ascii(custom_char c) -> char { return c; }
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct is_char<custom_char> : std::true_type {};
|
||||
@@ -185,11 +178,6 @@ TEST(format_test, wide_format_to_n) {
|
||||
}
|
||||
|
||||
#if FMT_USE_USER_DEFINED_LITERALS
|
||||
TEST(xchar_test, format_udl) {
|
||||
using namespace fmt::literals;
|
||||
EXPECT_EQ(L"{}c{}"_format(L"ab", 1), fmt::format(L"{}c{}", L"ab", 1));
|
||||
}
|
||||
|
||||
TEST(xchar_test, named_arg_udl) {
|
||||
using namespace fmt::literals;
|
||||
auto udl_a =
|
||||
@@ -204,7 +192,10 @@ TEST(xchar_test, named_arg_udl) {
|
||||
|
||||
TEST(xchar_test, print) {
|
||||
// Check that the wide print overload compiles.
|
||||
if (fmt::detail::const_check(false)) fmt::print(L"test");
|
||||
if (fmt::detail::const_check(false)) {
|
||||
fmt::print(L"test");
|
||||
fmt::println(L"test");
|
||||
}
|
||||
}
|
||||
|
||||
TEST(xchar_test, join) {
|
||||
@@ -227,39 +218,31 @@ struct formatter<streamable_enum, wchar_t> : basic_ostream_formatter<wchar_t> {
|
||||
} // namespace fmt
|
||||
|
||||
enum unstreamable_enum {};
|
||||
auto format_as(unstreamable_enum e) -> int { return e; }
|
||||
|
||||
TEST(xchar_test, enum) {
|
||||
EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum()));
|
||||
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
|
||||
}
|
||||
|
||||
struct streamable_and_unformattable {};
|
||||
|
||||
auto operator<<(std::wostream& os, streamable_and_unformattable)
|
||||
-> std::wostream& {
|
||||
return os << L"foo";
|
||||
}
|
||||
|
||||
TEST(xchar_test, streamed) {
|
||||
EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>());
|
||||
EXPECT_EQ(fmt::format(L"{}", fmt::streamed(streamable_and_unformattable())),
|
||||
L"foo");
|
||||
}
|
||||
|
||||
TEST(xchar_test, sign_not_truncated) {
|
||||
wchar_t format_str[] = {
|
||||
L'{', L':',
|
||||
'+' | static_cast<wchar_t>(1 << fmt::detail::num_bits<char>()), L'}', 0};
|
||||
EXPECT_THROW(fmt::format(format_str, 42), fmt::format_error);
|
||||
}
|
||||
|
||||
namespace fake_qt {
|
||||
class QString {
|
||||
public:
|
||||
QString(const wchar_t* s) : s_(s) {}
|
||||
const wchar_t* utf16() const noexcept { return s_.data(); }
|
||||
int size() const noexcept { return static_cast<int>(s_.size()); }
|
||||
|
||||
private:
|
||||
std::wstring s_;
|
||||
};
|
||||
|
||||
fmt::basic_string_view<wchar_t> to_string_view(const QString& s) noexcept {
|
||||
return {s.utf16(), static_cast<size_t>(s.size())};
|
||||
}
|
||||
} // namespace fake_qt
|
||||
|
||||
TEST(format_test, format_foreign_strings) {
|
||||
using fake_qt::QString;
|
||||
EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
|
||||
EXPECT_EQ(fmt::format(QString(L"{}"), QString(L"42")), L"42");
|
||||
EXPECT_THROW(fmt::format(fmt::runtime(format_str), 42), fmt::format_error);
|
||||
}
|
||||
|
||||
TEST(xchar_test, chrono) {
|
||||
@@ -295,8 +278,9 @@ std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr,
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(chrono_test, time_point) {
|
||||
auto t1 = std::chrono::system_clock::now();
|
||||
TEST(chrono_test_wchar, time_point) {
|
||||
auto t1 = std::chrono::time_point_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now());
|
||||
|
||||
std::vector<std::wstring> spec_list = {
|
||||
L"%%", L"%n", L"%t", L"%Y", L"%EY", L"%y", L"%Oy", L"%Ey", L"%C",
|
||||
@@ -304,14 +288,36 @@ TEST(chrono_test, time_point) {
|
||||
L"%OU", L"%W", L"%OW", L"%V", L"%OV", L"%j", L"%d", L"%Od", L"%e",
|
||||
L"%Oe", L"%a", L"%A", L"%w", L"%Ow", L"%u", L"%Ou", L"%H", L"%OH",
|
||||
L"%I", L"%OI", L"%M", L"%OM", L"%S", L"%OS", L"%x", L"%Ex", L"%X",
|
||||
L"%EX", L"%D", L"%F", L"%R", L"%T", L"%p", L"%z", L"%Z"};
|
||||
spec_list.push_back(L"%Y-%m-%d %H:%M:%S");
|
||||
L"%EX", L"%D", L"%F", L"%R", L"%T", L"%p"};
|
||||
#ifndef _WIN32
|
||||
// Disabled on Windows, because these formats is not consistent among
|
||||
// platforms.
|
||||
spec_list.insert(spec_list.end(), {L"%c", L"%Ec", L"%r"});
|
||||
#elif !FMT_HAS_C99_STRFTIME
|
||||
// Only C89 conversion specifiers when using MSVCRT instead of UCRT
|
||||
spec_list = {L"%%", L"%Y", L"%y", L"%b", L"%B", L"%m", L"%U",
|
||||
L"%W", L"%j", L"%d", L"%a", L"%A", L"%w", L"%H",
|
||||
L"%I", L"%M", L"%S", L"%x", L"%X", L"%p"};
|
||||
#endif
|
||||
spec_list.push_back(L"%Y-%m-%d %H:%M:%S");
|
||||
|
||||
for (const auto& spec : spec_list) {
|
||||
auto t = std::chrono::system_clock::to_time_t(t1);
|
||||
auto tm = *std::gmtime(&t);
|
||||
|
||||
auto sys_output = system_wcsftime(spec, &tm);
|
||||
|
||||
auto fmt_spec = fmt::format(L"{{:{}}}", spec);
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1));
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
|
||||
}
|
||||
|
||||
// Timezone formatters tests makes sense for localtime.
|
||||
#if FMT_HAS_C99_STRFTIME
|
||||
spec_list = {L"%z", L"%Z"};
|
||||
#else
|
||||
spec_list = {L"%Z"};
|
||||
#endif
|
||||
for (const auto& spec : spec_list) {
|
||||
auto t = std::chrono::system_clock::to_time_t(t1);
|
||||
auto tm = *std::localtime(&t);
|
||||
@@ -319,8 +325,38 @@ TEST(chrono_test, time_point) {
|
||||
auto sys_output = system_wcsftime(spec, &tm);
|
||||
|
||||
auto fmt_spec = fmt::format(L"{{:{}}}", spec);
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt_spec, t1));
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt_spec, tm));
|
||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
|
||||
|
||||
if (spec == L"%z") {
|
||||
sys_output.insert(sys_output.end() - 2, 1, L':');
|
||||
EXPECT_EQ(sys_output, fmt::format(L"{:%Ez}", tm));
|
||||
EXPECT_EQ(sys_output, fmt::format(L"{:%Oz}", tm));
|
||||
}
|
||||
}
|
||||
|
||||
// Separate tests for UTC, since std::time_put can use local time and ignoring
|
||||
// the timezone in std::tm (if it presents on platform).
|
||||
if (fmt::detail::has_member_data_tm_zone<std::tm>::value) {
|
||||
auto t = std::chrono::system_clock::to_time_t(t1);
|
||||
auto tm = *std::gmtime(&t);
|
||||
|
||||
std::vector<std::wstring> tz_names = {L"GMT", L"UTC"};
|
||||
EXPECT_THAT(tz_names, Contains(fmt::format(L"{:%Z}", t1)));
|
||||
EXPECT_THAT(tz_names, Contains(fmt::format(L"{:%Z}", tm)));
|
||||
}
|
||||
|
||||
if (fmt::detail::has_member_data_tm_gmtoff<std::tm>::value) {
|
||||
auto t = std::chrono::system_clock::to_time_t(t1);
|
||||
auto tm = *std::gmtime(&t);
|
||||
|
||||
EXPECT_EQ(L"+0000", fmt::format(L"{:%z}", t1));
|
||||
EXPECT_EQ(L"+0000", fmt::format(L"{:%z}", tm));
|
||||
|
||||
EXPECT_EQ(L"+00:00", fmt::format(L"{:%Ez}", t1));
|
||||
EXPECT_EQ(L"+00:00", fmt::format(L"{:%Ez}", tm));
|
||||
|
||||
EXPECT_EQ(L"+00:00", fmt::format(L"{:%Oz}", t1));
|
||||
EXPECT_EQ(L"+00:00", fmt::format(L"{:%Oz}", tm));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,14 +366,36 @@ TEST(xchar_test, color) {
|
||||
}
|
||||
|
||||
TEST(xchar_test, ostream) {
|
||||
std::wostringstream wos;
|
||||
fmt::print(wos, L"Don't {}!", L"panic");
|
||||
EXPECT_EQ(L"Don't panic!", wos.str());
|
||||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409
|
||||
{
|
||||
std::wostringstream wos;
|
||||
fmt::print(wos, L"Don't {}!", L"panic");
|
||||
EXPECT_EQ(wos.str(), L"Don't panic!");
|
||||
}
|
||||
|
||||
{
|
||||
std::wostringstream wos;
|
||||
fmt::println(wos, L"Don't {}!", L"panic");
|
||||
EXPECT_EQ(wos.str(), L"Don't panic!\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(xchar_test, format_map) {
|
||||
auto m = std::map<std::wstring, int>{{L"one", 1}, {L"t\"wo", 2}};
|
||||
EXPECT_EQ(fmt::format(L"{}", m), L"{\"one\": 1, \"t\\\"wo\": 2}");
|
||||
}
|
||||
|
||||
TEST(xchar_test, escape_string) {
|
||||
using vec = std::vector<std::wstring>;
|
||||
EXPECT_EQ(fmt::format(L"{}", vec{L"\n\r\t\"\\"}), L"[\"\\n\\r\\t\\\"\\\\\"]");
|
||||
EXPECT_EQ(fmt::format(L"{}", vec{L"понедельник"}), L"[\"понедельник\"]");
|
||||
}
|
||||
|
||||
TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
|
||||
template <typename Char> struct numpunct : std::numpunct<Char> {
|
||||
protected:
|
||||
Char do_decimal_point() const override { return '?'; }
|
||||
@@ -368,10 +426,14 @@ template <typename Char> struct small_grouping : std::numpunct<Char> {
|
||||
|
||||
TEST(locale_test, localized_double) {
|
||||
auto loc = std::locale(std::locale(), new numpunct<char>());
|
||||
EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23));
|
||||
EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23));
|
||||
EXPECT_EQ("1~234?5", fmt::format(loc, "{:L}", 1234.5));
|
||||
EXPECT_EQ("12~000", fmt::format(loc, "{:L}", 12000.0));
|
||||
EXPECT_EQ(fmt::format(loc, "{:L}", 1.23), "1?23");
|
||||
EXPECT_EQ(fmt::format(loc, "{:Lf}", 1.23), "1?230000");
|
||||
EXPECT_EQ(fmt::format(loc, "{:L}", 1234.5), "1~234?5");
|
||||
EXPECT_EQ(fmt::format(loc, "{:L}", 12000.0), "12~000");
|
||||
EXPECT_EQ(fmt::format(loc, "{:8L}", 1230.0), " 1~230");
|
||||
EXPECT_EQ(fmt::format(loc, "{:15.6Lf}", 0.1), " 0?100000");
|
||||
EXPECT_EQ(fmt::format(loc, "{:15.6Lf}", 1.0), " 1?000000");
|
||||
EXPECT_EQ(fmt::format(loc, "{:15.6Lf}", 1e3), " 1~000?000000");
|
||||
}
|
||||
|
||||
TEST(locale_test, format) {
|
||||
@@ -434,16 +496,16 @@ TEST(locale_test, wformat) {
|
||||
fmt::format(small_grouping_loc, L"{:L}", max_value<uint32_t>()));
|
||||
}
|
||||
|
||||
TEST(locale_test, double_formatter) {
|
||||
TEST(locale_test, int_formatter) {
|
||||
auto loc = std::locale(std::locale(), new special_grouping<char>());
|
||||
auto f = fmt::formatter<int>();
|
||||
auto parse_ctx = fmt::format_parse_context("L");
|
||||
f.parse(parse_ctx);
|
||||
char buf[10] = {};
|
||||
fmt::basic_format_context<char*, char> format_ctx(
|
||||
buf, {}, fmt::detail::locale_ref(loc));
|
||||
*f.format(12345, format_ctx) = 0;
|
||||
EXPECT_STREQ("12,345", buf);
|
||||
auto buf = fmt::memory_buffer();
|
||||
fmt::basic_format_context<fmt::appender, char> format_ctx(
|
||||
fmt::appender(buf), {}, fmt::detail::locale_ref(loc));
|
||||
f.format(12345, format_ctx);
|
||||
EXPECT_EQ(fmt::to_string(buf), "12,345");
|
||||
}
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
@@ -454,13 +516,10 @@ template <class charT> struct formatter<std::complex<double>, charT> {
|
||||
public:
|
||||
FMT_CONSTEXPR typename basic_format_parse_context<charT>::iterator parse(
|
||||
basic_format_parse_context<charT>& ctx) {
|
||||
using handler_type =
|
||||
detail::dynamic_specs_handler<basic_format_parse_context<charT>>;
|
||||
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
||||
detail::type::string_type);
|
||||
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
detail::parse_float_type_spec(specs_, ctx.error_handler());
|
||||
return it;
|
||||
auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
|
||||
detail::type::float_type);
|
||||
detail::parse_float_type_spec(specs_, detail::error_handler());
|
||||
return end;
|
||||
}
|
||||
|
||||
template <class FormatContext>
|
||||
@@ -507,4 +566,16 @@ TEST(locale_test, chrono_weekday) {
|
||||
std::locale::global(loc_old);
|
||||
}
|
||||
|
||||
TEST(locale_test, sign) {
|
||||
EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50");
|
||||
}
|
||||
|
||||
TEST(std_test_xchar, optional) {
|
||||
# ifdef __cpp_lib_optional
|
||||
EXPECT_EQ(fmt::format(L"{}", std::optional{L'C'}), L"optional(\'C\')");
|
||||
EXPECT_EQ(fmt::format(L"{}", std::optional{std::wstring{L"wide string"}}),
|
||||
L"optional(\"wide string\")");
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
Reference in new issue
Block a user