1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-08-03 14:41:48 +02:00

Update formatting library.

This commit is contained in:
Sandu Liviu Catalin
2022-03-16 22:36:44 +02:00
parent 38f0a53cd8
commit e253dc2038
66 changed files with 6165 additions and 4591 deletions

View File

@@ -4,23 +4,10 @@ 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)
target_link_libraries(test-main gtest fmt)
include(CheckCXXCompilerFlag)
# 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(test-main PUBLIC -fno-delete-null-pointer-checks)
endif ()
# Use less strict pedantic flags for the tests because GMock doesn't compile
# cleanly with -pedantic and -std=c++98.
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
#set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wno-long-long -Wno-variadic-macros)
endif ()
function(add_fmt_executable name)
add_executable(${name} ${ARGN})
if (MINGW)
@@ -84,8 +71,13 @@ if (NOT (MSVC AND BUILD_SHARED_LIBS))
endif ()
add_fmt_test(ostream-test)
add_fmt_test(compile-test)
add_fmt_test(compile-fp-test HEADER_ONLY)
if (MSVC)
# Without this option, MSVC returns 199711L for the __cplusplus macro.
target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus)
endif()
add_fmt_test(printf-test)
add_fmt_test(ranges-test)
add_fmt_test(ranges-test ranges-odr-test.cc)
add_fmt_test(scan-test)
add_fmt_test(unicode-test HEADER_ONLY)
if (MSVC)
@@ -136,29 +128,20 @@ if (NOT MSVC_STATIC_RUNTIME)
if (FMT_PEDANTIC)
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
if (HAVE_STRTOD_L)
target_compile_definitions(posix-mock-test PRIVATE FMT_LOCALE)
endif ()
add_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(os-test)
endif ()
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
if (FMT_PEDANTIC AND CXX_STANDARD LESS 20)
# MSVC fails to compile GMock when C++17 is enabled.
if (FMT_HAS_VARIANT AND NOT MSVC)
add_fmt_test(std-format-test)
set_property(TARGET std-format-test PROPERTY CXX_STANDARD 17)
endif ()
if (FMT_PEDANTIC)
# Test that the library can be compiled with exceptions disabled.
# -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822.
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
endif ()
if (HAVE_FNO_EXCEPTIONS_FLAG)
add_library(noexception-test ../src/format.cc)
add_library(noexception-test ../src/format.cc noexception-test.cc)
target_include_directories(
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_options(noexception-test PRIVATE -fno-exceptions)
@@ -173,7 +156,11 @@ if (FMT_PEDANTIC AND CXX_STANDARD LESS 20)
nolocale-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(
nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1)
endif ()
# These tests are disabled on Windows because they take too long.
if (FMT_PEDANTIC AND NOT WIN32)
# Test if incorrect API usages produce compilation error.
add_test(compile-error-test ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test"
@@ -182,14 +169,11 @@ if (FMT_PEDANTIC AND CXX_STANDARD LESS 20)
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
--build-options
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
"-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}"
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DFMT_DIR=${CMAKE_SOURCE_DIR}"
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
endif ()
# These tests are disabled on Windows because they take too long.
if (FMT_PEDANTIC AND NOT WIN32)
# Test if the targets are found from the build directory.
add_test(find-package-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE}

View File

@@ -7,10 +7,12 @@
#include "fmt/args.h"
#include <memory>
#include "gtest/gtest.h"
TEST(args_test, basic) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc1");
store.push_back(1.5f);
@@ -19,7 +21,7 @@ TEST(args_test, basic) {
TEST(args_test, strings_and_refs) {
// Unfortunately the tests are compiled with old ABI so strings use COW.
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
char str[] = "1234567890";
store.push_back(str);
store.push_back(std::cref(str));
@@ -48,7 +50,7 @@ template <> struct formatter<custom_type> {
FMT_END_NAMESPACE
TEST(args_test, custom_format) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto c = custom_type();
store.push_back(c);
++c.i;
@@ -77,7 +79,7 @@ template <> struct formatter<to_stringable> {
FMT_END_NAMESPACE
TEST(args_test, to_string_and_formatter) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto s = to_stringable();
store.push_back(s);
store.push_back(std::cref(s));
@@ -85,13 +87,13 @@ TEST(args_test, to_string_and_formatter) {
}
TEST(args_test, named_int) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("a1", 42));
EXPECT_EQ("42", fmt::vformat("{a1}", store));
}
TEST(args_test, named_strings) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
char str[] = "1234567890";
store.push_back(fmt::arg("a1", str));
store.push_back(fmt::arg("a2", std::cref(str)));
@@ -100,7 +102,7 @@ TEST(args_test, named_strings) {
}
TEST(args_test, named_arg_by_ref) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones";
store.push_back(fmt::arg("band", std::cref(band)));
band[9] = 'c'; // Changing band affects the output.
@@ -108,7 +110,7 @@ TEST(args_test, named_arg_by_ref) {
}
TEST(args_test, named_custom_format) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto c = custom_type();
store.push_back(fmt::arg("c1", c));
++c.i;
@@ -121,7 +123,7 @@ TEST(args_test, named_custom_format) {
}
TEST(args_test, clear) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
auto result = fmt::vformat("{}", store);
@@ -138,7 +140,7 @@ TEST(args_test, clear) {
}
TEST(args_test, reserve) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.reserve(2, 1);
store.push_back(1.5f);
store.push_back(fmt::arg("a1", 42));
@@ -163,7 +165,7 @@ template <> struct formatter<copy_throwable> {
FMT_END_NAMESPACE
TEST(args_test, throw_on_copy) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(std::string("foo"));
try {
store.push_back(copy_throwable());
@@ -172,15 +174,13 @@ TEST(args_test, throw_on_copy) {
EXPECT_EQ(fmt::vformat("{}", store), "foo");
}
TEST(args_test, copy_constructor) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
store.push_back(fmt::arg("test1", "value1"));
store.push_back(fmt::arg("test2", "value2"));
store.push_back(fmt::arg("test3", "value3"));
auto store2 = store;
store2.push_back(fmt::arg("test4", "value4"));
auto result = fmt::vformat("{test1} {test2} {test3} {test4}", store2);
EXPECT_EQ(result, "value1 value2 value3 value4");
TEST(args_test, move_constructor) {
using store_type = fmt::dynamic_format_arg_store<fmt::format_context>;
auto store = std::unique_ptr<store_type>(new store_type());
store->push_back(42);
store->push_back(std::string("foo"));
store->push_back(fmt::arg("a1", "foo"));
auto moved_store = std::move(*store);
store.reset();
EXPECT_EQ(fmt::vformat("{} {} {a1}", moved_store), "42 foo foo");
}

View File

@@ -7,6 +7,9 @@
#include "fmt/chrono.h"
#include <ctime>
#include <vector>
#include "gtest-extra.h" // EXPECT_THROW_MSG
#include "util.h" // get_locale
@@ -38,6 +41,36 @@ auto make_second(int s) -> std::tm {
return time;
}
std::string system_strftime(const std::string& format, const std::tm* timeptr,
std::locale* locptr = nullptr) {
auto loc = locptr ? *locptr : std::locale::classic();
auto& facet = std::use_facet<std::time_put<char>>(loc);
std::ostringstream os;
os.imbue(loc);
facet.put(os, os, ' ', timeptr, format.c_str(),
format.c_str() + format.size());
#ifdef _WIN32
// Workaround a bug in older versions of Universal CRT.
auto str = os.str();
if (str == "-0000") str = "+0000";
return str;
#else
return os.str();
#endif
}
FMT_CONSTEXPR std::tm make_tm(int year, int mon, int mday, int hour, int min,
int sec) {
auto tm = std::tm();
tm.tm_sec = sec;
tm.tm_min = min;
tm.tm_hour = hour;
tm.tm_mday = mday;
tm.tm_mon = mon - 1;
tm.tm_year = year - 1900;
return tm;
}
TEST(chrono_test, format_tm) {
auto tm = std::tm();
tm.tm_year = 116;
@@ -48,14 +81,132 @@ TEST(chrono_test, format_tm) {
tm.tm_sec = 33;
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is 2016-04-25 11:22:33.");
EXPECT_EQ(fmt::format("{:%Y}", tm), "2016");
EXPECT_EQ(fmt::format("{:%C}", tm), "20");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
EXPECT_EQ(fmt::format("{:%e}", tm), "25");
EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/16");
EXPECT_EQ(fmt::format("{:%F}", tm), "2016-04-25");
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
// Short year
tm.tm_year = 999 - 1900;
tm.tm_mon = 0; // for %G
tm.tm_mday = 2; // for %G
tm.tm_wday = 3; // for %G
tm.tm_yday = 1; // for %G
EXPECT_EQ(fmt::format("{:%Y}", tm), "0999");
EXPECT_EQ(fmt::format("{:%C%y}", tm), "0999");
EXPECT_EQ(fmt::format("{:%G}", tm), "0999");
tm.tm_year = 27 - 1900;
EXPECT_EQ(fmt::format("{:%Y}", tm), "0027");
EXPECT_EQ(fmt::format("{:%C%y}", tm), "0027");
// Overflow year
tm.tm_year = 2147483647;
EXPECT_EQ(fmt::format("{:%Y}", tm), "2147485547");
tm.tm_year = -2147483648;
EXPECT_EQ(fmt::format("{:%Y}", tm), "-2147481748");
// for week on the year
// https://www.cl.cam.ac.uk/~mgk25/iso-time.html
std::vector<std::tm> tm_list = {
make_tm(1975, 12, 29, 12, 14, 16), // W01
make_tm(1977, 1, 2, 12, 14, 16), // W53
make_tm(1999, 12, 27, 12, 14, 16), // W52
make_tm(1999, 12, 31, 12, 14, 16), // W52
make_tm(2000, 1, 1, 12, 14, 16), // W52
make_tm(2000, 1, 2, 12, 14, 16), // W52
make_tm(2000, 1, 3, 12, 14, 16) // W1
};
const std::string iso_week_spec = "%Y-%m-%d: %G %g %V";
for (auto ctm : tm_list) {
// Calculate tm_yday, tm_wday, etc.
std::time_t t = std::mktime(&ctm);
tm = *std::localtime(&t);
auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec);
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
fmt::format(fmt::runtime(fmt_spec), tm));
}
// Every day from 1970-01-01
std::time_t time_now = std::time(nullptr);
for (std::time_t t = 6 * 3600; t < time_now; t += 86400) {
tm = *std::localtime(&t);
auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec);
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
fmt::format(fmt::runtime(fmt_spec), tm));
}
}
// MSVC:
// minkernel\crts\ucrt\src\appcrt\time\wcsftime.cpp(971) : Assertion failed:
// timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099
#ifndef _WIN32
TEST(chrono_test, format_tm_future) {
auto tm = std::tm();
tm.tm_year = 10445; // 10000+ years
tm.tm_mon = 3;
tm.tm_mday = 25;
tm.tm_hour = 11;
tm.tm_min = 22;
tm.tm_sec = 33;
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is 12345-04-25 11:22:33.");
EXPECT_EQ(fmt::format("{:%Y}", tm), "12345");
EXPECT_EQ(fmt::format("{:%C}", tm), "123");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/45");
EXPECT_EQ(fmt::format("{:%F}", tm), "12345-04-25");
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
}
TEST(chrono_test, format_tm_past) {
auto tm = std::tm();
tm.tm_year = -2001;
tm.tm_mon = 3;
tm.tm_mday = 25;
tm.tm_hour = 11;
tm.tm_min = 22;
tm.tm_sec = 33;
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is -101-04-25 11:22:33.");
EXPECT_EQ(fmt::format("{:%Y}", tm), "-101");
// macOS %C - "-1"
// Linux %C - "-2"
// fmt %C - "-1"
EXPECT_EQ(fmt::format("{:%C}", tm), "-1");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
// macOS %D - "04/25/01" (%y)
// Linux %D - "04/25/99" (%y)
// fmt %D - "04/25/01" (%y)
EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/01");
EXPECT_EQ(fmt::format("{:%F}", tm), "-101-04-25");
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
tm.tm_year = -1901; // -1
EXPECT_EQ(fmt::format("{:%Y}", tm), "-001");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
tm.tm_year = -1911; // -11
EXPECT_EQ(fmt::format("{:%Y}", tm), "-011");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
}
#endif
TEST(chrono_test, grow_buffer) {
auto s = std::string("{:");
for (int i = 0; i < 30; ++i) s += "%c";
s += "}\n";
auto t = std::time(nullptr);
fmt::format(fmt::runtime(s), *std::localtime(&t));
(void)fmt::format(fmt::runtime(s), *std::localtime(&t));
}
TEST(chrono_test, format_to_empty_container) {
@@ -88,22 +239,45 @@ TEST(chrono_test, gmtime) {
EXPECT_TRUE(equal(tm, fmt::gmtime(t)));
}
template <typename TimePoint> auto strftime(TimePoint tp) -> std::string {
template <typename TimePoint> auto strftime_full(TimePoint tp) -> std::string {
auto t = std::chrono::system_clock::to_time_t(tp);
auto tm = *std::localtime(&t);
char output[256] = {};
std::strftime(output, sizeof(output), "%Y-%m-%d %H:%M:%S", &tm);
return output;
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(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
EXPECT_EQ(strftime(t1), fmt::format("{}", t1));
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(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2));
EXPECT_EQ(strftime_full(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", "%z", "%Z"};
spec_list.push_back("%Y-%m-%d %H:%M:%S");
#ifndef _WIN32
// Disabled on Windows because these formats are not consistent among
// platforms.
spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"});
#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), t1));
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
}
}
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
@@ -197,41 +371,41 @@ TEST(chrono_test, format_specs) {
TEST(chrono_test, invalid_specs) {
auto sec = std::chrono::seconds(0);
EXPECT_THROW_MSG(fmt::format(runtime("{:%a}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%a}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%A}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%A}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%c}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%c}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%x}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%x}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Ex}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ex}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%X}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%X}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%EX}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%EX}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%D}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%D}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%F}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%F}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Ec}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ec}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%w}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%w}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%u}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%u}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%b}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%b}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%B}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%B}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%z}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%z}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Z}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Z}"), sec), fmt::format_error,
"no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Eq}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Eq}"), sec), fmt::format_error,
"invalid format");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Oq}"), sec), fmt::format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Oq}"), sec), fmt::format_error,
"invalid format");
}
@@ -279,20 +453,33 @@ TEST(chrono_test, format_default_fp) {
}
TEST(chrono_test, format_precision) {
EXPECT_THROW_MSG(fmt::format(runtime("{:.2}"), std::chrono::seconds(42)),
fmt::format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{:.2}"), 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)));
EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2));
EXPECT_EQ("13ms", fmt::format("{:.0}", dms(12.56)));
EXPECT_EQ("12.6ms", fmt::format("{:.1}", dms(12.56)));
EXPECT_EQ("12.56ms", fmt::format("{:.2}", dms(12.56)));
}
TEST(chrono_test, format_full_specs) {
EXPECT_EQ("1ms ", fmt::format("{:6.0}", dms(1.234)));
EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234)));
EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2));
EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1));
EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8));
EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3));
EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234)));
EXPECT_EQ("13ms ", fmt::format("{:6.0}", dms(12.56)));
EXPECT_EQ(" 13ms", fmt::format("{:>8.{}}", dms(12.56), 0));
EXPECT_EQ(" 13ms ", fmt::format("{:^{}.{}}", dms(12.56), 6, 0));
EXPECT_EQ(" 13ms ", fmt::format("{0:^{2}.{1}}", dms(12.56), 0, 8));
EXPECT_EQ("==13ms===", fmt::format("{:=^{}.{}}", dms(12.56), 9, 0));
EXPECT_EQ("***13ms***", fmt::format("{:*^10.0}", dms(12.56)));
}
TEST(chrono_test, format_simple_q) {
@@ -306,29 +493,37 @@ TEST(chrono_test, format_simple_q) {
}
TEST(chrono_test, format_precision_q) {
EXPECT_THROW_MSG(fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)),
fmt::format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)),
fmt::format_error, "precision not allowed for this argument type");
EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234)));
EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2));
}
TEST(chrono_test, format_full_specs_q) {
EXPECT_EQ("1 ms ", fmt::format("{:7.0%Q %q}", dms(1.234)));
EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234)));
EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2));
EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1));
EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9));
EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3));
EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234)));
EXPECT_EQ("13 ms ", fmt::format("{:7.0%Q %q}", dms(12.56)));
EXPECT_EQ(" 13 ms", fmt::format("{:>8.{}%Q %q}", dms(12.56), 0));
EXPECT_EQ(" 13 ms ", fmt::format("{:^{}.{}%Q %q}", dms(12.56), 8, 0));
EXPECT_EQ(" 13 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(12.56), 0, 9));
EXPECT_EQ("==13 ms==", fmt::format("{:=^{}.{}%Q %q}", dms(12.56), 9, 0));
EXPECT_EQ("***13 ms***", fmt::format("{:*^11.0%Q %q}", dms(12.56)));
}
TEST(chrono_test, invalid_width_id) {
EXPECT_THROW(fmt::format(runtime("{:{o}"), std::chrono::seconds(0)),
EXPECT_THROW((void)fmt::format(runtime("{:{o}"), std::chrono::seconds(0)),
fmt::format_error);
}
TEST(chrono_test, invalid_colons) {
EXPECT_THROW(fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)),
EXPECT_THROW((void)fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)),
fmt::format_error);
}
@@ -348,15 +543,12 @@ TEST(chrono_test, negative_durations) {
}
TEST(chrono_test, special_durations) {
EXPECT_EQ(
"40.",
fmt::format("{:%S}", std::chrono::duration<double>(1e20)).substr(0, 3));
auto value = fmt::format("{:%S}", std::chrono::duration<double>(1e20));
EXPECT_EQ(value, "40");
auto nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ(
"nan nan nan nan nan:nan nan",
fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan)));
fmt::format("{:%S}",
std::chrono::duration<float, std::atto>(1.79400457e+31f));
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::exa>(1)),
"1Es");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)),
@@ -365,6 +557,9 @@ TEST(chrono_test, special_durations) {
"03:33");
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
"03:33:20");
EXPECT_EQ("44.000000000000",
fmt::format("{:%S}", std::chrono::duration<float, std::pico>(
1.54213895E+26)));
}
TEST(chrono_test, unsigned_duration) {
@@ -375,11 +570,63 @@ TEST(chrono_test, weekday) {
auto loc = get_locale("ru_RU.UTF-8");
std::locale::global(loc);
auto mon = fmt::weekday(1);
auto tm = std::tm();
tm.tm_wday = static_cast<int>(mon.c_encoding());
EXPECT_EQ(fmt::format("{}", mon), "Mon");
EXPECT_EQ(fmt::format("{:%a}", tm), "Mon");
if (loc != std::locale::classic()) {
EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}),
Contains(fmt::format(loc, "{:L}", mon)));
EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}),
Contains(fmt::format(loc, "{:%a}", tm)));
}
}
TEST(chrono_test, cpp20_duration_subsecond_support) {
using attoseconds = std::chrono::duration<long long, std::atto>;
// Check that 18 digits of subsecond precision are supported.
EXPECT_EQ(fmt::format("{:%S}", attoseconds{999999999999999999}),
"00.999999999999999999");
EXPECT_EQ(fmt::format("{:%S}", attoseconds{673231113420148734}),
"00.673231113420148734");
EXPECT_EQ(fmt::format("{:%S}", attoseconds{-673231113420148734}),
"-00.673231113420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{13420148734}),
"13.420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{-13420148734}),
"-13.420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234");
{
// 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);
}
using nanoseconds_dbl = std::chrono::duration<double, std::nano>;
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{-123456789}), "-00.123456789");
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{9123456789}), "09.123456789");
// Verify that only the seconds part is extracted and printed.
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123456789}), "39.123456789");
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123000000}), "39.123000000");
{
// Now the hour is printed, and we also test if negative doubles work.
auto dur = nanoseconds_dbl{-99123456789};
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);
}
// 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)),
"00.333333");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 7>>(1)),
"00.142857");
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR

View File

@@ -50,6 +50,12 @@ TEST(color_test, format) {
"\x1b[105mtbmagenta\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
"\x1b[31mfoo\x1b[0m");
EXPECT_EQ(fmt::format("{}{}", fmt::styled("red", fg(fmt::color::red)),
fmt::styled("bold", fmt::emphasis::bold)),
"\x1b[38;2;255;000;000mred\x1b[0m\x1b[1mbold\x1b[0m");
EXPECT_EQ(fmt::format("{}", fmt::styled("bar", fg(fmt::color::blue) |
fmt::emphasis::underline)),
"\x1b[4m\x1b[38;2;000;000;255mbar\x1b[0m");
}
TEST(color_test, format_to) {

View File

@@ -1,71 +1,158 @@
# Test if compile errors are produced where necessary.
cmake_minimum_required(VERSION 3.1...3.18)
project(compile-error-test CXX)
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag)
set(fmt_headers "
#include <fmt/format.h>
#include <fmt/xchar.h>
")
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../../include)
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG} ${PEDANTIC_COMPILE_FLAGS})
set(error_test_names "")
set(non_error_test_content "")
function (generate_source result fragment)
set(${result} "
#define FMT_HEADER_ONLY 1
#include \"fmt/format.h\"
int main() {
${fragment}
}
" PARENT_SCOPE)
# For error tests (we expect them to produce compilation error):
# * adds a name of test into `error_test_names` list
# * generates a single source file (with the same name) for each test
# For non-error tests (we expect them to compile successfully):
# * adds a code segment as separate function to `non_error_test_content`
function (expect_compile name code_fragment)
cmake_parse_arguments(EXPECT_COMPILE "ERROR" "" "" ${ARGN})
string(MAKE_C_IDENTIFIER "${name}" test_name)
if (EXPECT_COMPILE_ERROR)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/${test_name}.cc" "
${fmt_headers}
void ${test_name}() {
${code_fragment}
}
")
set(error_test_names_copy "${error_test_names}")
list(APPEND error_test_names_copy "${test_name}")
set(error_test_names "${error_test_names_copy}" PARENT_SCOPE)
else()
set(non_error_test_content "
${non_error_test_content}
void ${test_name}() {
${code_fragment}
}" PARENT_SCOPE)
endif()
endfunction ()
function (expect_compile code)
generate_source(source "${code}")
check_cxx_source_compiles("${source}" compiles)
if (NOT compiles)
set(error_msg "Compile error for: ${code}")
endif ()
# Unset the CMake cache variable compiles. Otherwise the compile test will
# just use cached information next time it runs.
unset(compiles CACHE)
if (error_msg)
message(FATAL_ERROR ${error_msg})
# Generates a source file for non-error test with `non_error_test_content` and
# CMake project file with all error and single non-error test targets.
function (run_tests)
set(cmake_targets "")
foreach(test_name IN LISTS error_test_names)
set(cmake_targets "
${cmake_targets}
add_library(test-${test_name} ${test_name}.cc)
target_link_libraries(test-${test_name} PRIVATE fmt::fmt)
")
endforeach()
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/non_error_test.cc" "
${fmt_headers}
${non_error_test_content}
")
set(cmake_targets "
${cmake_targets}
add_library(non-error-test non_error_test.cc)
target_link_libraries(non-error-test PRIVATE fmt::fmt)
")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/CMakeLists.txt" "
cmake_minimum_required(VERSION 3.1...3.18)
project(tests CXX)
add_subdirectory(${FMT_DIR} fmt)
${cmake_targets}
")
set(build_directory "${CMAKE_CURRENT_BINARY_DIR}/test/build")
file(MAKE_DIRECTORY "${build_directory}")
execute_process(
COMMAND
"${CMAKE_COMMAND}"
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
"-DCMAKE_GENERATOR=${CMAKE_GENERATOR}"
"-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}"
"-DFMT_DIR=${FMT_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}/test"
WORKING_DIRECTORY "${build_directory}"
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_VARIABLE output_var)
if (NOT result_var EQUAL 0)
message(FATAL_ERROR "Unable to configure:\n${output_var}")
endif()
foreach(test_name IN LISTS error_test_names)
execute_process(
COMMAND
"${CMAKE_COMMAND}" --build "${build_directory}" --target "test-${test_name}"
WORKING_DIRECTORY "${build_directory}"
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_QUIET)
if (result_var EQUAL 0)
message(SEND_ERROR "No compile error for \"${test_name}\":\n${output_var}")
endif ()
endforeach()
execute_process(
COMMAND
"${CMAKE_COMMAND}" --build "${build_directory}" --target "non-error-test"
WORKING_DIRECTORY "${build_directory}"
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_VARIABLE output_var)
if (NOT result_var EQUAL 0)
message(SEND_ERROR "Compile error for combined non-error test:\n${output_var}")
endif ()
endfunction ()
function (expect_compile_error code)
generate_source(source "${code}")
check_cxx_source_compiles("${source}" compiles)
if (compiles)
set(error_msg "No compile error for: ${code}")
endif ()
# Unset the CMake cache variable compiles. Otherwise the compile test will
# just use cached information next time it runs.
unset(compiles CACHE)
if (error_msg)
message(FATAL_ERROR ${error_msg})
endif ()
endfunction ()
# check if the source file skeleton compiles
expect_compile("")
expect_compile(check "")
expect_compile(check-error "compilation_error" ERROR)
# Formatting a wide character with a narrow format string is forbidden.
expect_compile_error("fmt::format(\"{}\", L'a');")
expect_compile(wide-character-narrow-format-string "fmt::format(L\"{}\", L'a');")
expect_compile(wide-character-narrow-format-string-error "fmt::format(\"{}\", L'a');" ERROR)
# Formatting a wide string with a narrow format string is forbidden.
expect_compile_error("fmt::format(\"{}\", L\"foo\");")
expect_compile(wide-string-narrow-format-string "fmt::format(L\"{}\", L\"foo\");")
expect_compile(wide-string-narrow-format-string-error "fmt::format(\"{}\", L\"foo\");" ERROR)
# Formatting a narrow string with a wide format string is forbidden because
# mixing UTF-8 with UTF-16/32 can result in an invalid output.
expect_compile_error("fmt::format(L\"{}\", \"foo\");")
expect_compile(narrow-string-wide-format-string "fmt::format(L\"{}\", L\"foo\");")
expect_compile(narrow-string-wide-format-string-error "fmt::format(L\"{}\", \"foo\");" ERROR)
# Formatting a wide string with a narrow format string is forbidden.
expect_compile_error("
expect_compile(cast-to-string "
struct S {
operator std::string() const { return std::string(); }
};
fmt::format(\"{}\", std::string(S()));
")
expect_compile(cast-to-string-error "
struct S {
operator std::string() const { return std::string(); }
};
fmt::format(\"{}\", S());
" ERROR)
# Formatting a function
expect_compile(format-function "
void (*f)();
fmt::format(\"{}\", fmt::ptr(f));
")
expect_compile(format-function-error "
void (*f)();
fmt::format(\"{}\", f);
" ERROR)
# Make sure that compiler features detected in the header
# match the features detected in CMake.
@@ -74,6 +161,43 @@ if (SUPPORTS_USER_DEFINED_LITERALS)
else ()
set(supports_udl 0)
endif ()
expect_compile("#if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl}
# error
#endif")
expect_compile(udl-check "
#if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl}
# error
#endif
")
if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
# Compile-time argument type check
expect_compile(format-string-number-spec "
#ifdef FMT_HAS_CONSTEVAL
fmt::format(\"{:d}\", 42);
#endif
")
expect_compile(format-string-number-spec-error "
#ifdef FMT_HAS_CONSTEVAL
fmt::format(\"{: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
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
using namespace fmt::literals;
fmt::print(\"{foo}\", \"bar\"_a=42);
#else
#error
#endif
" ERROR)
endif ()
# Run all tests
run_tests()

62
vendor/Fmt/test/compile-fp-test.cc vendored Normal file
View File

@@ -0,0 +1,62 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/compile.h"
#include "gmock/gmock.h"
#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
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;
}
Char buffer[max_string_length]{};
};
template <size_t max_string_length, typename Char = char, typename... Args>
consteval auto test_format(auto format, const Args&... args) {
test_string<max_string_length, Char> string{};
fmt::format_to(string.buffer, format, args...);
return string;
}
TEST(compile_time_formatting_test, floating_point) {
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{}"), 0.0f));
EXPECT_EQ("392.500000", test_format<11>(FMT_COMPILE("{0:f}"), 392.5f));
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:}"), 0.0));
EXPECT_EQ("0.000000", test_format<9>(FMT_COMPILE("{:f}"), 0.0));
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:g}"), 0.0));
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:}"), 392.65));
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:g}"), 392.65));
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:G}"), 392.65));
EXPECT_EQ("4.9014e+06", test_format<11>(FMT_COMPILE("{:g}"), 4.9014e6));
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:f}"), -392.65));
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:F}"), -392.65));
EXPECT_EQ("3.926500e+02", test_format<13>(FMT_COMPILE("{0:e}"), 392.65));
EXPECT_EQ("3.926500E+02", test_format<13>(FMT_COMPILE("{0:E}"), 392.65));
EXPECT_EQ("+0000392.6", test_format<11>(FMT_COMPILE("{0:+010.4g}"), 392.65));
EXPECT_EQ("9223372036854775808.000000",
test_format<27>(FMT_COMPILE("{:f}"), 9223372036854775807.0));
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ("nan", test_format<4>(FMT_COMPILE("{}"), nan));
EXPECT_EQ("+nan", test_format<5>(FMT_COMPILE("{:+}"), nan));
if (std::signbit(-nan))
EXPECT_EQ("-nan", test_format<5>(FMT_COMPILE("{}"), -nan));
else
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
constexpr double inf = std::numeric_limits<double>::infinity();
EXPECT_EQ("inf", test_format<4>(FMT_COMPILE("{}"), inf));
EXPECT_EQ("+inf", test_format<5>(FMT_COMPILE("{:+}"), inf));
EXPECT_EQ("-inf", test_format<5>(FMT_COMPILE("{}"), -inf));
}
#endif

View File

@@ -59,7 +59,24 @@ TEST(compile_test, compile_fallback) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
}
#ifdef __cpp_if_constexpr
struct type_with_get {
template <int> friend void get(type_with_get);
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<type_with_get> : formatter<int> {
template <typename FormatContext>
auto format(type_with_get, FormatContext& ctx) -> decltype(ctx.out()) {
return formatter<int>::format(42, ctx);
}
};
FMT_END_NAMESPACE
TEST(compile_test, compile_type_with_get) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), type_with_get()));
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
struct test_formattable {};
FMT_BEGIN_NAMESPACE
@@ -184,6 +201,11 @@ TEST(compile_test, named) {
# endif
}
TEST(compile_test, join) {
unsigned char data[] = {0x1, 0x2, 0xaf};
EXPECT_EQ("0102af", fmt::format(FMT_COMPILE("{:02x}"), fmt::join(data, "")));
}
TEST(compile_test, format_to) {
char buf[8];
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);

View File

@@ -408,15 +408,7 @@ TYPED_TEST(numeric_arg_test, make_and_visit) {
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
}
namespace fmt {
template <> struct is_char<wchar_t> : std::true_type {};
} // namespace fmt
TEST(arg_test, char_arg) {
CHECK_ARG(char, 'a', 'a');
CHECK_ARG(wchar_t, L'a', 'a');
CHECK_ARG(wchar_t, L'a', L'a');
}
TEST(arg_test, char_arg) { CHECK_ARG(char, 'a', 'a'); }
TEST(arg_test, string_arg) {
char str_data[] = "test";
@@ -532,7 +524,7 @@ struct test_format_specs_handler {
fmt::detail::arg_ref<char> width_ref;
int precision = 0;
fmt::detail::arg_ref<char> precision_ref;
char type = 0;
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.
@@ -558,14 +550,14 @@ struct test_format_specs_handler {
constexpr void on_dynamic_precision(string_view) {}
constexpr void end_precision() {}
constexpr void on_type(char t) { type = t; }
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, h);
fmt::detail::parse_format_specs(s, s + N - 1, h);
return h;
}
@@ -583,7 +575,7 @@ TEST(core_test, constexpr_parse_format_specs) {
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 == 'd', "");
static_assert(parse_test_specs("d").type == fmt::presentation_type::dec, "");
static_assert(parse_test_specs("{<").res == handler::error, "");
}
@@ -605,7 +597,7 @@ constexpr fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
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, h);
parse_format_specs(s, s + N - 1, h);
return specs;
}
@@ -623,14 +615,15 @@ TEST(format_test, constexpr_dynamic_specs_handler) {
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 == 'd', "");
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, checker);
parse_format_specs(s, s + N - 1, checker);
return checker;
}
@@ -647,7 +640,7 @@ TEST(format_test, constexpr_specs_checker) {
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 == 'd', "");
static_assert(check_specs("d").type == fmt::presentation_type::dec, "");
static_assert(check_specs("{<").res == handler::error, "");
}
@@ -711,10 +704,110 @@ TEST(core_test, has_formatter) {
"");
}
struct const_formattable {};
struct nonconst_formattable {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<const_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(const const_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
template <> struct formatter<nonconst_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(nonconst_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
FMT_END_NAMESPACE
struct convertible_to_pointer {
operator const int*() const { return nullptr; }
};
struct convertible_to_pointer_formattable {
operator const int*() const { return nullptr; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<convertible_to_pointer_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(convertible_to_pointer_formattable, format_context& ctx) const
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
FMT_END_NAMESPACE
enum class unformattable_scoped_enum {};
namespace test {
enum class formattable_scoped_enum {};
auto format_as(formattable_scoped_enum) -> int { return 42; }
struct convertible_to_enum {
operator formattable_scoped_enum() const { return {}; }
};
} // namespace test
TEST(core_test, is_formattable) {
#if 0
// This should be enabled once corresponding map overloads are gone.
static_assert(fmt::is_formattable<signed char*>::value, "");
static_assert(fmt::is_formattable<unsigned char*>::value, "");
static_assert(fmt::is_formattable<const signed char*>::value, "");
static_assert(fmt::is_formattable<const unsigned char*>::value, "");
#endif
static_assert(!fmt::is_formattable<wchar_t>::value, "");
#ifdef __cpp_char8_t
static_assert(!fmt::is_formattable<char8_t>::value, "");
#endif
static_assert(!fmt::is_formattable<char16_t>::value, "");
static_assert(!fmt::is_formattable<char32_t>::value, "");
static_assert(!fmt::is_formattable<const wchar_t*>::value, "");
static_assert(!fmt::is_formattable<const wchar_t[3]>::value, "");
static_assert(!fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value,
"");
static_assert(fmt::is_formattable<enabled_formatter>::value, "");
static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
static_assert(fmt::is_formattable<const_formattable&>::value, "");
static_assert(fmt::is_formattable<const const_formattable&>::value, "");
static_assert(fmt::is_formattable<nonconst_formattable&>::value, "");
#if !FMT_MSC_VER || FMT_MSC_VER >= 1910
static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, "");
#endif
static_assert(!fmt::is_formattable<convertible_to_pointer>::value, "");
const auto f = convertible_to_pointer_formattable();
EXPECT_EQ(fmt::format("{}", f), "test");
static_assert(!fmt::is_formattable<void (*)()>::value, "");
struct s;
static_assert(!fmt::is_formattable<int(s::*)>::value, "");
static_assert(!fmt::is_formattable<int (s::*)()>::value, "");
static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, "");
static_assert(fmt::is_formattable<test::formattable_scoped_enum>::value, "");
static_assert(!fmt::is_formattable<test::convertible_to_enum>::value, "");
}
TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); }
@@ -725,6 +818,10 @@ TEST(core_test, format_to) {
EXPECT_EQ(s, "42");
}
TEST(core_test, format_as) {
EXPECT_EQ(fmt::format("{}", test::formattable_scoped_enum()), "42");
}
struct convertible_to_int {
operator int() const { return 42; }
};
@@ -793,7 +890,10 @@ struct explicitly_convertible_to_string_view {
};
TEST(core_test, format_explicitly_convertible_to_string_view) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
// Types explicitly convertible to string_view are not formattable by
// default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_string_view>::value, "");
}
# ifdef FMT_USE_STRING_VIEW
@@ -802,8 +902,11 @@ struct explicitly_convertible_to_std_string_view {
};
TEST(core_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("foo",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
// Types explicitly convertible to string_view are not formattable by
// default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
"");
}
# endif
#endif
@@ -840,48 +943,19 @@ TEST(core_test, adl) {
if (fmt::detail::const_check(true)) return;
auto s = adl_test::string();
char buf[10];
fmt::format("{}", s);
(void)fmt::format("{}", s);
fmt::format_to(buf, "{}", s);
fmt::format_to_n(buf, 10, "{}", s);
fmt::formatted_size("{}", s);
(void)fmt::formatted_size("{}", s);
fmt::print("{}", s);
fmt::print(stdout, "{}", s);
}
struct const_formattable {};
struct nonconst_formattable {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<const_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(const const_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
template <> struct formatter<nonconst_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(nonconst_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
FMT_END_NAMESPACE
TEST(core_test, is_const_formattable) {
EXPECT_TRUE((fmt::detail::is_const_formattable<const_formattable,
TEST(core_test, has_const_formatter) {
EXPECT_TRUE((fmt::detail::has_const_formatter<const_formattable,
fmt::format_context>()));
EXPECT_FALSE((fmt::detail::has_const_formatter<nonconst_formattable,
fmt::format_context>()));
EXPECT_FALSE((fmt::detail::is_const_formattable<nonconst_formattable,
fmt::format_context>()));
}
TEST(core_test, format_nonconst) {

View File

@@ -17,12 +17,12 @@
// Exercise the API to verify that everything we expect to can compile.
void test_format_api() {
fmt::format(FMT_STRING("{}"), 42);
fmt::format(FMT_STRING(L"{}"), 42);
fmt::format(FMT_STRING("noop"));
(void)fmt::format(FMT_STRING("{}"), 42);
(void)fmt::format(FMT_STRING(L"{}"), 42);
(void)fmt::format(FMT_STRING("noop"));
fmt::to_string(42);
fmt::to_wstring(42);
(void)fmt::to_string(42);
(void)fmt::to_wstring(42);
std::vector<char> out;
fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42);
@@ -35,13 +35,14 @@ void test_format_api() {
}
void test_chrono() {
fmt::format(FMT_STRING("{}"), std::chrono::seconds(42));
fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42));
(void)fmt::format(FMT_STRING("{}"), std::chrono::seconds(42));
(void)fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42));
}
void test_text_style() {
fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)");
fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)");
(void)fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"),
"rgb(255,20,30)");
fmt::text_style ts = fg(fmt::rgb(255, 20, 30));
std::string out;
@@ -51,7 +52,7 @@ void test_text_style() {
void test_range() {
std::vector<char> hello = {'h', 'e', 'l', 'l', 'o'};
fmt::format(FMT_STRING("{}"), hello);
(void)fmt::format(FMT_STRING("{}"), hello);
}
int main() {

View File

@@ -1,6 +1,5 @@
#include "fmt/format.h"
int main(int argc, char** argv) {
for(int i = 0; i < argc; ++i)
fmt::print("{}: {}\n", i, argv[i]);
for (int i = 0; i < argc; ++i) fmt::print("{}: {}\n", i, argv[i]);
}

856
vendor/Fmt/test/format vendored
View File

@@ -1,856 +0,0 @@
// Formatting library for C++ - the standard API implementation
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_FORMAT_
#define FMT_FORMAT_
#include <algorithm>
#include <cassert>
#include <variant>
#include "fmt/format.h"
// This implementation verifies the correctness of the standard API proposed in
// P0645 Text Formatting and is optimized for copy-pasting from the paper, not
// for efficiency or readability. An efficient implementation should not use
// std::variant and should store packed argument type tags separately from
// values in basic_format_args for small number of arguments.
namespace std {
template<class T>
constexpr bool Integral = is_integral_v<T>;
template <class O>
using iter_difference_t = ptrdiff_t;
}
// https://fmt.dev/Text%20Formatting.html#format.syn
namespace std {
// [format.error], class format_error
class format_error;
// [format.formatter], formatter
template<class charT> class basic_format_parse_context;
using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
template<class Out, class charT> class basic_format_context;
using format_context = basic_format_context<
/* unspecified */ fmt::detail::buffer_appender<char>, char>;
using wformat_context = basic_format_context<
/* unspecified */ fmt::detail::buffer_appender<wchar_t>, wchar_t>;
template<class T, class charT = char> struct formatter {
formatter() = delete;
};
// [format.arguments], arguments
template<class Context> class basic_format_arg;
template<class Visitor, class Context>
/* see below */ auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
template<class Context, class... Args> struct format_arg_store; // exposition only
template<class Context> class basic_format_args;
using format_args = basic_format_args<format_context>;
using wformat_args = basic_format_args<wformat_context>;
template<class Out, class charT>
using format_args_t = basic_format_args<basic_format_context<Out, charT>>;
template<class Context = format_context, class... Args>
format_arg_store<Context, Args...>
make_format_args(const Args&... args);
template<class... Args>
format_arg_store<wformat_context, Args...>
make_wformat_args(const Args&... args);
// [format.functions], formatting functions
template<class... Args>
string format(string_view fmt, const Args&... args);
template<class... Args>
wstring format(wstring_view fmt, const Args&... args);
string vformat(string_view fmt, format_args args);
wstring vformat(wstring_view fmt, wformat_args args);
template<class Out, class... Args>
Out format_to(Out out, string_view fmt, const Args&... args);
template<class Out, class... Args>
Out format_to(Out out, wstring_view fmt, const Args&... args);
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args);
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out>
struct format_to_n_result {
Out out;
iter_difference_t<Out> size;
};
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
string_view fmt, const Args&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
wstring_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(string_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(wstring_view fmt, const Args&... args);
}
// https://fmt.dev/Text%20Formatting.html#format.error
namespace std {
class format_error : public runtime_error {
public:
explicit format_error(const string& what_arg) : runtime_error(what_arg) {}
explicit format_error(const char* what_arg) : runtime_error(what_arg) {}
};
}
namespace std {
namespace detail {
struct error_handler {
// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char* message) {
throw std::format_error(message);
}
};
}
}
// https://fmt.dev/Text%20Formatting.html#format.parse_context
namespace std {
template<class charT>
class basic_format_parse_context {
public:
using char_type = charT;
using const_iterator = typename basic_string_view<charT>::const_iterator;
using iterator = const_iterator;
private:
iterator begin_; // exposition only
iterator end_; // exposition only
enum indexing { unknown, manual, automatic }; // exposition only
indexing indexing_; // exposition only
size_t next_arg_id_; // exposition only
size_t num_args_; // exposition only
public:
explicit constexpr basic_format_parse_context(basic_string_view<charT> fmt,
size_t num_args = 0) noexcept;
basic_format_parse_context(const basic_format_parse_context&) = delete;
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
constexpr void advance_to(const_iterator it);
constexpr size_t next_arg_id();
constexpr void check_arg_id(size_t id);
// Implementation detail:
constexpr void check_arg_id(fmt::string_view) {}
detail::error_handler error_handler() const { return {}; }
void on_error(const char* msg) { error_handler().on_error(msg); }
};
}
namespace std {
template<class charT>
/* explicit */ constexpr basic_format_parse_context<charT>::
basic_format_parse_context(basic_string_view<charT> fmt,
size_t num_args) noexcept
: begin_(fmt.begin()), end_(fmt.end()), indexing_(unknown), next_arg_id_(0), num_args_(num_args) {}
template<class charT>
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::begin() const noexcept { return begin_; }
template<class charT>
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::end() const noexcept { return end_; }
template<class charT>
constexpr void basic_format_parse_context<charT>::advance_to(typename basic_format_parse_context<charT>::iterator it) { begin_ = it; }
template<class charT>
constexpr size_t basic_format_parse_context<charT>::next_arg_id() {
if (indexing_ == manual)
throw format_error("manual to automatic indexing");
if (indexing_ == unknown)
indexing_ = automatic;
return next_arg_id_++;
}
template<class charT>
constexpr void basic_format_parse_context<charT>::check_arg_id(size_t id) {
// clang doesn't support __builtin_is_constant_evaluated yet
//if (!(!__builtin_is_constant_evaluated() || id < num_args_))
// throw format_error(invalid index is out of range");
if (indexing_ == automatic)
throw format_error("automatic to manual indexing");
if (indexing_ == unknown)
indexing_ = manual;
}
}
// https://fmt.dev/Text%20Formatting.html#format.context
namespace std {
template<class Out, class charT>
class basic_format_context {
basic_format_args<basic_format_context> args_; // exposition only
Out out_; // exposition only
public:
using iterator = Out;
using char_type = charT;
template<class T> using formatter_type = formatter<T, charT>;
basic_format_arg<basic_format_context> arg(size_t id) const;
iterator out();
void advance_to(iterator it);
// Implementation details:
using format_arg = basic_format_arg<basic_format_context>;
basic_format_context(Out out, basic_format_args<basic_format_context> args, fmt::detail::locale_ref)
: args_(args), out_(out) {}
detail::error_handler error_handler() const { return {}; }
basic_format_arg<basic_format_context> arg(fmt::basic_string_view<charT>) const {
return {}; // unused: named arguments are not supported yet
}
void on_error(const char* msg) { error_handler().on_error(msg); }
};
}
namespace std {
template<class O, class charT>
basic_format_arg<basic_format_context<O, charT>> basic_format_context<O, charT>::arg(size_t id) const { return args_.get(id); }
template<class O, class charT>
typename basic_format_context<O, charT>::iterator basic_format_context<O, charT>::out() { return out_; }
template<class O, class charT>
void basic_format_context<O, charT>::advance_to(typename basic_format_context<O, charT>::iterator it) { out_ = it; }
}
namespace std {
namespace detail {
template <typename T>
constexpr bool is_standard_integer_v =
std::is_same_v<T, signed char> ||
std::is_same_v<T, short int> ||
std::is_same_v<T, int> ||
std::is_same_v<T, long int> ||
std::is_same_v<T, long long int>;
template <typename T>
constexpr bool is_standard_unsigned_integer_v =
std::is_same_v<T, unsigned char> ||
std::is_same_v<T, unsigned short int> ||
std::is_same_v<T, unsigned int> ||
std::is_same_v<T, unsigned long int> ||
std::is_same_v<T, unsigned long long int>;
template <typename T, typename Char> struct formatter;
}
}
// https://fmt.dev/Text%20Formatting.html#format.arg
namespace std {
template<class Context>
class basic_format_arg {
public:
class handle;
private:
using char_type = typename Context::char_type; // exposition only
variant<monostate, bool, char_type,
int, unsigned int, long long int, unsigned long long int,
double, long double,
const char_type*, basic_string_view<char_type>,
const void*, handle> value; // exposition only
template<typename T,
typename = enable_if_t<
std::is_same_v<T, bool> ||
std::is_same_v<T, char_type> ||
(std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>) ||
detail::is_standard_integer_v<T> ||
detail::is_standard_unsigned_integer_v<T> ||
sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0
>> explicit basic_format_arg(const T& v) noexcept; // exposition only
explicit basic_format_arg(float n) noexcept; // exposition only
explicit basic_format_arg(double n) noexcept; // exposition only
explicit basic_format_arg(long double n) noexcept; // exposition only
explicit basic_format_arg(const char_type* s); // exposition only
template<class traits>
explicit basic_format_arg(
basic_string_view<char_type, traits> s) noexcept; // exposition only
template<class traits, class Allocator>
explicit basic_format_arg(
const basic_string<char_type, traits, Allocator>& s) noexcept; // exposition only
explicit basic_format_arg(nullptr_t) noexcept; // exposition only
template<class T, typename = enable_if_t<is_void_v<T>>>
explicit basic_format_arg(const T* p) noexcept; // exposition only
// Fails due to a bug in clang
//template<class Visitor, class Ctx>
// friend auto visit_format_arg(Visitor&& vis,
// basic_format_arg<Ctx> arg); // exposition only
friend auto get_value(basic_format_arg arg) {
return arg.value;
}
template <typename T, typename Char> friend struct detail::formatter;
template<class Ctx, class... Args>
friend format_arg_store<Ctx, Args...>
make_format_args(const Args&... args); // exposition only
public:
basic_format_arg() noexcept;
explicit operator bool() const noexcept;
};
}
namespace std {
template<class Context>
basic_format_arg<Context>::basic_format_arg() noexcept {}
template<class Context>
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T& v) noexcept {
if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, char_type>)
value = v;
else if constexpr (std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>)
value = static_cast<wchar_t>(v);
else if constexpr (detail::is_standard_integer_v<T> && sizeof(T) <= sizeof(int))
value = static_cast<int>(v);
else if constexpr (detail::is_standard_unsigned_integer_v<T> && sizeof(T) <= sizeof(unsigned))
value = static_cast<unsigned>(v);
else if constexpr (detail::is_standard_integer_v<T>)
value = static_cast<long long int>(v);
else if constexpr (detail::is_standard_unsigned_integer_v<T>)
value = static_cast<unsigned long long int>(v);
else if constexpr (sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0)
value = handle(v);
}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(float n) noexcept
: value(static_cast<double>(n)) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(double n) noexcept
: value(n) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(long double n) noexcept
: value(n) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(const typename basic_format_arg<Context>::char_type* s)
: value(s) {
assert(s != nullptr);
}
template<class Context>
template<class traits>
/* explicit */ basic_format_arg<Context>::basic_format_arg(basic_string_view<char_type, traits> s) noexcept
: value(s) {}
template<class Context>
template<class traits, class Allocator>
/* explicit */ basic_format_arg<Context>::basic_format_arg(
const basic_string<char_type, traits, Allocator>& s) noexcept
: value(basic_string_view<char_type>(s.data(), s.size())) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(nullptr_t) noexcept
: value(static_cast<const void*>(nullptr)) {}
template<class Context>
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T* p) noexcept
: value(p) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::operator bool() const noexcept {
return !holds_alternative<monostate>(value);
}
}
namespace std {
template<class Context>
class basic_format_arg<Context>::handle {
const void* ptr_; // exposition only
void (*format_)(basic_format_parse_context<char_type>&,
Context&, const void*); // exposition only
template<class T> explicit handle(const T& val) noexcept; // exposition only
friend class basic_format_arg<Context>; // exposition only
public:
void format(basic_format_parse_context<char_type>&, Context& ctx) const;
};
}
namespace std {
template<class Context>
template<class T> /* explicit */ basic_format_arg<Context>::handle::handle(const T& val) noexcept
: ptr_(&val), format_([](basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx, const void* ptr) {
typename Context::template formatter_type<T> f;
parse_ctx.advance_to(f.parse(parse_ctx));
format_ctx.advance_to(f.format(*static_cast<const T*>(ptr), format_ctx));
}) {}
template<class Context>
void basic_format_arg<Context>::handle::format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx) const {
format_(parse_ctx, format_ctx, ptr_);
}
// https://fmt.dev/Text%20Formatting.html#format.visit
template<class Visitor, class Context>
auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg) {
return visit(vis, get_value(arg));
}
}
// https://fmt.dev/Text%20Formatting.html#format.store
namespace std {
template<class Context, class... Args>
struct format_arg_store { // exposition only
array<basic_format_arg<Context>, sizeof...(Args)> args;
};
}
// https://fmt.dev/Text%20Formatting.html#format.basic_args
namespace std {
template<class Context>
class basic_format_args {
size_t size_; // exposition only
const basic_format_arg<Context>* data_; // exposition only
public:
basic_format_args() noexcept;
template<class... Args>
basic_format_args(const format_arg_store<Context, Args...>& store) noexcept;
basic_format_arg<Context> get(size_t i) const noexcept;
};
}
namespace std {
template<class Context>
basic_format_args<Context>::basic_format_args() noexcept : size_(0) {}
template<class Context>
template<class... Args>
basic_format_args<Context>::basic_format_args(const format_arg_store<Context, Args...>& store) noexcept
: size_(sizeof...(Args)), data_(store.args.data()) {}
template<class Context>
basic_format_arg<Context> basic_format_args<Context>::get(size_t i) const noexcept {
return i < size_ ? data_[i] : basic_format_arg<Context>();
}
}
namespace std {
// https://fmt.dev/Text%20Formatting.html#format.make_args
template<class Context /*= format_context*/, class... Args>
format_arg_store<Context, Args...> make_format_args(const Args&... args) {
return {basic_format_arg<Context>(args)...};
}
// https://fmt.dev/Text%20Formatting.html#format.make_wargs
template<class... Args>
format_arg_store<wformat_context, Args...> make_wformat_args(const Args&... args) {
return make_format_args<wformat_context>(args...);
}
}
namespace std {
namespace detail {
template <typename OutputIt, typename Char>
class arg_formatter
: public fmt::detail::arg_formatter_base<OutputIt, Char, error_handler> {
private:
using char_type = Char;
using base = fmt::detail::arg_formatter_base<OutputIt, Char, error_handler>;
using format_context = std::basic_format_context<OutputIt, Char>;
using parse_context = basic_format_parse_context<Char>;
parse_context* parse_ctx_;
format_context& ctx_;
public:
using iterator = OutputIt;
using format_specs = typename base::format_specs;
/**
\rst
Constructs an argument formatter object.
*ctx* is a reference to the formatting context,
*spec* contains format specifier information for standard argument types.
\endrst
*/
arg_formatter(format_context& ctx, parse_context* parse_ctx = nullptr, fmt::format_specs* spec = nullptr)
: base(ctx.out(), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {}
using base::operator();
/** Formats an argument of a user-defined type. */
iterator operator()(typename std::basic_format_arg<format_context>::handle handle) {
handle.format(*parse_ctx_, ctx_);
return this->out();
}
iterator operator()(monostate) {
throw format_error("");
}
};
template <typename Context>
inline fmt::detail::type get_type(basic_format_arg<Context> arg) {
return visit_format_arg([&] (auto val) {
using char_type = typename Context::char_type;
using T = decltype(val);
if (std::is_same_v<T, monostate>)
return fmt::detail::type::none_type;
if (std::is_same_v<T, bool>)
return fmt::detail::type::bool_type;
if (std::is_same_v<T, char_type>)
return fmt::detail::type::char_type;
if (std::is_same_v<T, int>)
return fmt::detail::type::int_type;
if (std::is_same_v<T, unsigned int>)
return fmt::detail::type::uint_type;
if (std::is_same_v<T, long long int>)
return fmt::detail::type::long_long_type;
if (std::is_same_v<T, unsigned long long int>)
return fmt::detail::type::ulong_long_type;
if (std::is_same_v<T, double>)
return fmt::detail::type::double_type;
if (std::is_same_v<T, long double>)
return fmt::detail::type::long_double_type;
if (std::is_same_v<T, const char_type*>)
return fmt::detail::type::cstring_type;
if (std::is_same_v<T, basic_string_view<char_type>>)
return fmt::detail::type::string_type;
if (std::is_same_v<T, const void*>)
return fmt::detail::type::pointer_type;
assert(get_value(arg).index() == 12);
return fmt::detail::type::custom_type;
}, arg);
}
template <typename Context>
class custom_formatter {
private:
using parse_context = basic_format_parse_context<typename Context::char_type>;
parse_context& parse_ctx_;
Context& format_ctx_;
public:
custom_formatter(parse_context& parse_ctx, Context& ctx) : parse_ctx_(parse_ctx), format_ctx_(ctx) {}
bool operator()(typename basic_format_arg<Context>::handle h) const {
h.format(parse_ctx_, format_ctx_);
return true;
}
template <typename T> bool operator()(T) const { return false; }
};
template <typename ArgFormatter, typename Char, typename Context>
struct format_handler : detail::error_handler {
using iterator = typename ArgFormatter::iterator;
format_handler(iterator out, basic_string_view<Char> str,
basic_format_args<Context> format_args,
fmt::detail::locale_ref loc)
: parse_ctx(str), context(out, format_args, loc) {}
void on_text(const Char* begin, const Char* end) {
auto size = fmt::detail::to_unsigned(end - begin);
auto out = context.out();
auto&& it = fmt::detail::reserve(out, size);
it = std::copy_n(begin, size, it);
context.advance_to(out);
}
int on_arg_id() { return parse_ctx.next_arg_id(); }
int on_arg_id(unsigned id) { return parse_ctx.check_arg_id(id), id; }
int on_arg_id(fmt::basic_string_view<Char>) { return 0; }
void on_replacement_field(int id, const Char* p) {
auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (!visit_format_arg(f, arg))
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx), arg));
}
const Char* on_format_specs(int id, const Char* begin, const Char* end) {
auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (visit_format_arg(f, arg)) return &*parse_ctx.begin();
fmt::basic_format_specs<Char> specs;
using fmt::detail::specs_handler;
using parse_context = basic_format_parse_context<Char>;
fmt::detail::specs_checker<specs_handler<parse_context, Context>> handler(
specs_handler<parse_context, Context>(specs, parse_ctx, context), get_type(arg));
begin = parse_format_specs(begin, end, handler);
if (begin == end || *begin != '}') on_error("missing '}' in format string");
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx, &specs), arg));
return begin;
}
basic_format_parse_context<Char> parse_ctx;
Context context;
};
template <typename T, typename Char>
struct formatter {
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) {
namespace detail = fmt::detail;
typedef detail::dynamic_specs_handler<ParseContext> handler_type;
auto type = detail::mapped_type_constant<T, fmt::buffer_context<Char>>::value;
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
auto type_spec = specs_.type;
auto eh = ctx.error_handler();
switch (type) {
case detail::type::none_type:
FMT_ASSERT(false, "invalid argument type");
break;
case detail::type::int_type:
case detail::type::uint_type:
case detail::type::long_long_type:
case detail::type::ulong_long_type:
case detail::type::bool_type:
handle_int_type_spec(type_spec,
detail::int_type_checker<decltype(eh)>(eh));
break;
case detail::type::char_type:
handle_char_specs(
&specs_, detail::char_specs_checker<decltype(eh)>(type_spec, eh));
break;
case detail::type::double_type:
case detail::type::long_double_type:
detail::parse_float_type_spec(specs_, eh);
break;
case detail::type::cstring_type:
detail::handle_cstring_type_spec(
type_spec, detail::cstring_type_checker<decltype(eh)>(eh));
break;
case detail::type::string_type:
detail::check_string_type_spec(type_spec, eh);
break;
case detail::type::pointer_type:
detail::check_pointer_type_spec(type_spec, eh);
break;
case detail::type::custom_type:
// Custom format specifiers should be checked in parse functions of
// formatter specializations.
break;
}
return it;
}
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
fmt::detail::handle_dynamic_spec<fmt::detail::width_checker>(
specs_.width, specs_.width_ref, ctx);
fmt::detail::handle_dynamic_spec<fmt::detail::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
using af = arg_formatter<typename FormatContext::iterator,
typename FormatContext::char_type>;
return visit_format_arg(af(ctx, nullptr, &specs_),
basic_format_arg<FormatContext>(val));
}
private:
fmt::detail::dynamic_format_specs<Char> specs_;
};
} // namespace detail
// https://fmt.dev/Text%20Formatting.html#format.functions
template<class... Args>
string format(string_view fmt, const Args&... args) {
return vformat(fmt, make_format_args(args...));
}
template<class... Args>
wstring format(wstring_view fmt, const Args&... args) {
return vformat(fmt, make_wformat_args(args...));
}
string vformat(string_view fmt, format_args args) {
fmt::memory_buffer mbuf;
fmt::detail::buffer<char>& buf = mbuf;
using af = detail::arg_formatter<fmt::format_context::iterator, char>;
detail::format_handler<af, char, format_context>
h(fmt::detail::buffer_appender<char>(buf), fmt, args, {});
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
return to_string(mbuf);
}
wstring vformat(wstring_view fmt, wformat_args args);
template<class Out, class... Args>
Out format_to(Out out, string_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, make_format_args<context>(args...));
}
template<class Out, class... Args>
Out format_to(Out out, wstring_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, make_format_args<context>(args...));
}
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) {
using af = detail::arg_formatter<Out, char>;
detail::format_handler<af, char, basic_format_context<Out, char>>
h(out, fmt, args, {});
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
return h.context.out();
}
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
string_view fmt, const Args&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
wstring_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(string_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(wstring_view fmt, const Args&... args);
#define charT char
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
template<> struct formatter<char, wchar_t>;
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
template<size_t N> struct formatter<const charT[N], charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits, class Allocator>
struct formatter<basic_string<charT, traits, Allocator>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits>
struct formatter<basic_string_view<charT, traits>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
template <> struct formatter<long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
#undef charT
#define charT wchar_t
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
template<> struct formatter<char, wchar_t> : detail::formatter<charT, charT> {};
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
template<size_t N> struct formatter<const charT[N], charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits, class Allocator>
struct formatter<std::basic_string<charT, traits, Allocator>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits>
struct formatter<std::basic_string_view<charT, traits>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
template <> struct formatter<long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
#undef charT
template<> struct formatter<const wchar_t, char> {
formatter() = delete;
};
}
#endif // FMT_FORMAT_

View File

@@ -96,23 +96,6 @@ TEST(bigint_test, multiply) {
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
}
TEST(bigint_test, accumulator) {
fmt::detail::accumulator acc;
EXPECT_EQ(acc.lower, 0);
EXPECT_EQ(acc.upper, 0);
acc.upper = 12;
acc.lower = 34;
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
acc += 56;
EXPECT_EQ(acc.lower, 90);
acc += max_value<uint64_t>();
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
EXPECT_EQ(acc.upper, 0);
EXPECT_EQ(acc.lower, 13 * 0x100000000);
}
TEST(bigint_test, square) {
bigint n0(0);
n0.square();
@@ -207,14 +190,14 @@ TEST(fp_test, get_cached_power) {
using limits = std::numeric_limits<double>;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0;
auto fp = fmt::detail::get_cached_power(exp, dec_exp);
bigint exact, cache(fp.f);
auto power = fmt::detail::get_cached_power(exp, dec_exp);
bigint exact, cache(power.f);
if (dec_exp >= 0) {
exact.assign_pow10(dec_exp);
if (fp.e <= 0)
exact <<= -fp.e;
if (power.e <= 0)
exact <<= -power.e;
else
cache <<= fp.e;
cache <<= power.e;
exact.align(cache);
cache.align(exact);
auto exact_str = fmt::format("{}", exact);
@@ -228,9 +211,9 @@ TEST(fp_test, get_cached_power) {
EXPECT_EQ(diff, 0);
} else {
cache.assign_pow10(-dec_exp);
cache *= fp.f + 1; // Inexact check.
cache *= power.f + 1; // Inexact check.
exact.assign(1);
exact <<= -fp.e;
exact <<= -power.e;
exact.align(cache);
auto exact_str = fmt::format("{}", exact);
auto cache_str = fmt::format("{}", cache);
@@ -243,14 +226,17 @@ TEST(fp_test, get_cached_power) {
TEST(fp_test, dragonbox_max_k) {
using fmt::detail::dragonbox::floor_log10_pow2;
using float_info = fmt::detail::dragonbox::float_info<float>;
EXPECT_EQ(fmt::detail::const_check(float_info::max_k),
float_info::kappa - floor_log10_pow2(float_info::min_exponent -
float_info::significand_bits));
EXPECT_EQ(
fmt::detail::const_check(float_info::max_k),
float_info::kappa -
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(double_info::min_exponent -
double_info::significand_bits));
double_info::kappa -
floor_log10_pow2(std::numeric_limits<double>::min_exponent -
fmt::detail::num_significand_bits<double>() - 1));
}
TEST(fp_test, get_round_direction) {
@@ -278,25 +264,22 @@ TEST(fp_test, get_round_direction) {
}
TEST(fp_test, fixed_handler) {
struct handler : fmt::detail::fixed_handler {
struct handler : fmt::detail::gen_digits_handler {
char buffer[10];
handler(int prec = 0) : fmt::detail::fixed_handler() {
handler(int prec = 0) : fmt::detail::gen_digits_handler() {
buf = buffer;
precision = prec;
}
};
int exp = 0;
handler().on_digit('0', 100, 99, 0, exp, false);
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
assertion_failure);
handler().on_digit('0', 100, 99, 0, false);
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, false), assertion_failure);
namespace digits = fmt::detail::digits;
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::error);
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, false), digits::error);
// Check that divisor - error doesn't overflow.
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, false), digits::error);
// Check that 2 * error doesn't overflow.
uint64_t max = max_value<uint64_t>();
EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, exp, false),
digits::error);
EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, false), digits::error);
}
TEST(fp_test, grisu_format_compiles_with_on_ieee_double) {
@@ -360,14 +343,6 @@ TEST(format_impl_test, count_digits) {
test_count_digits<uint64_t>();
}
TEST(format_impl_test, write_fallback_uintptr) {
std::string s;
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr);
EXPECT_EQ(s, "0xface");
}
#ifdef _WIN32
# include <windows.h>
#endif

View File

@@ -39,6 +39,75 @@ using testing::StrictMock;
enum { buffer_size = 256 };
TEST(uint128_test, ctor) {
using fmt::detail::uint128_fallback;
auto n = uint128_fallback();
EXPECT_EQ(n, 0);
n = uint128_fallback(42);
EXPECT_EQ(n, 42);
EXPECT_EQ(static_cast<uint64_t>(n), 42);
}
TEST(uint128_test, shift) {
auto n = fmt::detail::uint128_fallback(42);
n = n << 64;
EXPECT_EQ(static_cast<uint64_t>(n), 0);
n = n >> 64;
EXPECT_EQ(static_cast<uint64_t>(n), 42);
n = n << 62;
EXPECT_EQ(static_cast<uint64_t>(n >> 64), 0xa);
EXPECT_EQ(static_cast<uint64_t>(n), 0x8000000000000000);
n = n >> 62;
EXPECT_EQ(static_cast<uint64_t>(n), 42);
}
TEST(uint128_test, minus) {
auto n = fmt::detail::uint128_fallback(42);
EXPECT_EQ(n - 2, 40);
}
template <typename Float> void check_isfinite() {
using fmt::detail::isfinite;
EXPECT_TRUE(isfinite(Float(0.0)));
EXPECT_TRUE(isfinite(Float(42.0)));
EXPECT_TRUE(isfinite(Float(-42.0)));
EXPECT_TRUE(isfinite(Float(fmt::detail::max_value<double>())));
// Use double because std::numeric_limits is broken for __float128.
using limits = std::numeric_limits<double>;
EXPECT_FALSE(isfinite(Float(limits::infinity())));
EXPECT_FALSE(isfinite(Float(-limits::infinity())));
EXPECT_FALSE(isfinite(Float(limits::quiet_NaN())));
EXPECT_FALSE(isfinite(Float(-limits::quiet_NaN())));
}
TEST(float_test, isfinite) {
check_isfinite<double>();
#ifdef __SIZEOF_FLOAT128__
check_isfinite<fmt::detail::float128>();
#endif
}
template <typename Float> void check_isnan() {
using fmt::detail::isnan;
EXPECT_FALSE(isnan(Float(0.0)));
EXPECT_FALSE(isnan(Float(42.0)));
EXPECT_FALSE(isnan(Float(-42.0)));
EXPECT_FALSE(isnan(Float(fmt::detail::max_value<double>())));
// Use double because std::numeric_limits is broken for __float128.
using limits = std::numeric_limits<double>;
EXPECT_FALSE(isnan(Float(limits::infinity())));
EXPECT_FALSE(isnan(Float(-limits::infinity())));
EXPECT_TRUE(isnan(Float(limits::quiet_NaN())));
EXPECT_TRUE(isnan(Float(-limits::quiet_NaN())));
}
TEST(float_test, isnan) {
check_isnan<double>();
#ifdef __SIZEOF_FLOAT128__
check_isnan<fmt::detail::float128>();
#endif
}
struct uint32_pair {
uint32_t u[2];
};
@@ -223,8 +292,9 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) {
buffer.push_back('a');
basic_memory_buffer<char, 4, std_allocator> buffer2(std::move(buffer));
// Move should rip the guts of the first buffer.
EXPECT_EQ(inline_buffer_ptr, &buffer[0]);
EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size()));
EXPECT_EQ(&buffer[0], inline_buffer_ptr);
EXPECT_EQ(buffer.size(), 0);
EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), "testa");
EXPECT_GT(buffer2.capacity(), 4u);
}
@@ -325,7 +395,7 @@ template <typename Allocator, size_t MaxSize>
class max_size_allocator : public Allocator {
public:
using typename Allocator::value_type;
size_t max_size() const FMT_NOEXCEPT { return MaxSize; }
size_t max_size() const noexcept { return MaxSize; }
value_type* allocate(size_t n) {
if (n > max_size()) {
throw std::length_error("size > max_size");
@@ -370,11 +440,11 @@ TEST(format_test, escape) {
}
TEST(format_test, unmatched_braces) {
EXPECT_THROW_MSG(fmt::format(runtime("{")), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{")), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("}")), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("}")), format_error,
"unmatched '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0{}")), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0{}")), format_error,
"invalid format string");
}
@@ -391,30 +461,30 @@ TEST(format_test, args_in_different_positions) {
}
TEST(format_test, arg_errors) {
EXPECT_THROW_MSG(fmt::format(runtime("{")), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{")), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{?}")), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{?}")), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0")), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0")), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0}")), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0}")), format_error,
"argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{00}"), 42), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{00}"), 42), format_error,
"invalid format string");
char format_str[buffer_size];
safe_sprintf(format_str, "{%u", INT_MAX);
EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
"invalid format string");
safe_sprintf(format_str, "{%u}", INT_MAX);
EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
"argument not found");
safe_sprintf(format_str, "{%u", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
"invalid format string");
safe_sprintf(format_str, "{%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
"argument not found");
}
@@ -458,24 +528,26 @@ TEST(format_test, named_arg) {
fmt::arg("i", 0), fmt::arg("j", 0), fmt::arg("k", 0),
fmt::arg("l", 0), fmt::arg("m", 0), fmt::arg("n", 0),
fmt::arg("o", 0), fmt::arg("p", 0)));
EXPECT_THROW_MSG(fmt::format(runtime("{a}")), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{a}")), format_error,
"argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{a}"), 42), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{a}"), 42), format_error,
"argument not found");
}
TEST(format_test, auto_arg_index) {
EXPECT_EQ("abc", fmt::format("{}{}{}", 'a', 'b', 'c'));
EXPECT_THROW_MSG(fmt::format(runtime("{0}{}"), 'a', 'b'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0}{}"), 'a', 'b'), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{}{0}"), 'a', 'b'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{}{0}"), 'a', 'b'), format_error,
"cannot switch from automatic to manual argument indexing");
EXPECT_EQ("1.2", fmt::format("{:.{}}", 1.2345, 2));
EXPECT_THROW_MSG(fmt::format(runtime("{0}:.{}"), 1.2345, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0}:.{}"), 1.2345, 2),
format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{:.{0}}"), 1.2345, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.{0}}"), 1.2345, 2),
format_error,
"cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{}")), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{}")), format_error,
"argument not found");
}
@@ -533,9 +605,9 @@ TEST(format_test, center_align) {
}
TEST(format_test, fill) {
EXPECT_THROW_MSG(fmt::format(runtime("{0:{<5}"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{<5}"), 'c'), format_error,
"invalid fill character '{'");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{<5}}"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{<5}}"), 'c'), format_error,
"invalid fill character '{'");
EXPECT_EQ("**42", fmt::format("{0:*>4}", 42));
EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42));
@@ -554,80 +626,86 @@ TEST(format_test, fill) {
EXPECT_EQ(std::string("\0\0\0*", 4),
fmt::format(string_view("{:\0>4}", 6), '*'));
EXPECT_EQ("жж42", fmt::format("{0:ж>4}", 42));
EXPECT_THROW_MSG(fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0),
format_error, "missing '}' in format string");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0),
format_error, "invalid type specifier");
}
TEST(format_test, plus_sign) {
EXPECT_EQ("+42", fmt::format("{0:+}", 42));
EXPECT_EQ("-42", fmt::format("{0:+}", -42));
EXPECT_EQ("+42", fmt::format("{0:+}", 42));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 42u), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42u), format_error,
"format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 42ul), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error,
"format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42ll));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 42ull), format_error,
#if FMT_USE_INT128
EXPECT_EQ("+42", fmt::format("{0:+}", __int128_t(42)));
#endif
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error,
"format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42.0));
EXPECT_EQ("+42", fmt::format("{0:+}", 42.0l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+"), 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), "abc"), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{0:+}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
TEST(format_test, minus_sign) {
EXPECT_EQ("42", fmt::format("{0:-}", 42));
EXPECT_EQ("-42", fmt::format("{0:-}", -42));
EXPECT_EQ("42", fmt::format("{0:-}", 42));
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 42u), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42u), format_error,
"format specifier requires signed argument");
EXPECT_EQ("42", fmt::format("{0:-}", 42l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 42ul), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ul), format_error,
"format specifier requires signed argument");
EXPECT_EQ("42", fmt::format("{0:-}", 42ll));
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 42ull), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ull), format_error,
"format specifier requires signed argument");
EXPECT_EQ("42", fmt::format("{0:-}", 42.0));
EXPECT_EQ("42", fmt::format("{0:-}", 42.0l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:-"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-"), 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), "abc"), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{0:-}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
TEST(format_test, space_sign) {
EXPECT_EQ(" 42", fmt::format("{0: }", 42));
EXPECT_EQ("-42", fmt::format("{0: }", -42));
EXPECT_EQ(" 42", fmt::format("{0: }", 42));
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 42u), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42u), format_error,
"format specifier requires signed argument");
EXPECT_EQ(" 42", fmt::format("{0: }", 42l));
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 42ul), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ul), format_error,
"format specifier requires signed argument");
EXPECT_EQ(" 42", fmt::format("{0: }", 42ll));
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 42ull), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ull), format_error,
"format specifier requires signed argument");
EXPECT_EQ(" 42", fmt::format("{0: }", 42.0));
EXPECT_EQ(" 42", fmt::format("{0: }", 42.0l));
EXPECT_THROW_MSG(fmt::format(runtime("{0: "), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: "), 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), "abc"), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{0: }"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
TEST(format_test, hash_flag) {
@@ -670,14 +748,15 @@ TEST(format_test, hash_flag) {
EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.01));
EXPECT_EQ("0.50", fmt::format("{:#.2g}", 0.5));
EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.5));
EXPECT_THROW_MSG(fmt::format(runtime("{0:#"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#"), 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), "abc"), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{0:#}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
TEST(format_test, zero_flag) {
@@ -690,14 +769,14 @@ TEST(format_test, zero_flag) {
EXPECT_EQ("00042", fmt::format("{0:05}", 42ull));
EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0));
EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:0"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:0"), 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), 'c'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), "abc"), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(
fmt::format(runtime("{0:05}"), reinterpret_cast<void*>(0x42)),
(void)fmt::format(runtime("{0:05}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
}
@@ -705,19 +784,19 @@ TEST(format_test, width) {
char format_str[buffer_size];
safe_sprintf(format_str, "{0:%u", UINT_MAX);
increment(format_str + 3);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big");
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big");
safe_sprintf(format_str, "{0:%u", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big");
safe_sprintf(format_str, "{0:%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big");
EXPECT_EQ(" -42", fmt::format("{0:4}", -42));
EXPECT_EQ(" 42", fmt::format("{0:5}", 42u));
@@ -742,47 +821,47 @@ TEST(format_test, runtime_width) {
char format_str[buffer_size];
safe_sprintf(format_str, "{0:{%u", UINT_MAX);
increment(format_str + 4);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"invalid format string");
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"argument not found");
format_str[size + 1] = '}';
format_str[size + 2] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{"), 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{}"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{}"), 0), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{?}}"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{?}}"), 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0), format_error,
"argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{0:}}"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{0:}}"), 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, -1), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1), format_error,
"negative width");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1u)),
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1u)),
format_error, "number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, -1l), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1l), format_error,
"negative width");
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, (value + 1)),
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (value + 1)),
format_error, "number is too big");
}
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1ul)),
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1ul)),
format_error, "number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, '0'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, '0'), format_error,
"width is not integer");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error,
"width is not integer");
EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42, 4));
@@ -803,53 +882,53 @@ TEST(format_test, precision) {
char format_str[buffer_size];
safe_sprintf(format_str, "{0:.%u", UINT_MAX);
increment(format_str + 4);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big");
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big");
safe_sprintf(format_str, "{0:.%u", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big");
safe_sprintf(format_str, "{0:.%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:."), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:."), 0), format_error,
"missing precision specifier");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.}"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.}"), 0), format_error,
"missing precision specifier");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2"), 0), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42u), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42u), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42u), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42u), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42l), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42l), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42l), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42l), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42ul), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42ul), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42ul), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42ul), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42ll), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42ll), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42ll), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42ll), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42ull), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42ull), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42ull), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42ull), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:3.0}"), 'x'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.0}"), 'x'), format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345));
EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345l));
@@ -866,6 +945,23 @@ TEST(format_test, precision) {
"117102665855668676818703956031062493194527159149245532930545654440112748"
"012970999954193198940908041656332452475714786901472678015935523861155013"
"480352649347201937902681071074917033322268447533357208324319361e-324");
EXPECT_EQ(
fmt::format("{:.1074f}", 1.1125369292536e-308),
"0.0000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000111253692925360019747947051741965785554081512200979"
"355021686109411883779182127659725163430929750364498219730822952552570601"
"152163505899912777129583674906301179059298598412303893909188340988729019"
"014361467448914817838555156840459458527907308695109202499990850735085304"
"478476991912072201449236975063640913461919914396877093174125167509869762"
"482369631100360266123742648159508919592746619553246586039571522788247697"
"156360766271842991667238355464496455107749716934387136380536472531224398"
"559833794807213172371254492216255558078524900147957309382830827524104234"
"530961756787819847850302379672357738807808384667004752163416921762619527"
"462847642037420991432005657440259928195996762610375541867198059294212446"
"81962777939941034720757232455434770912461317493580281734466552734375");
std::string outputs[] = {
"-0X1.41FE3FFE71C9E000000000000000000000000000000000000000000000000000000"
@@ -895,6 +991,11 @@ TEST(format_test, precision) {
EXPECT_THAT(outputs,
testing::Contains(fmt::format("{:.838A}", -2.14001164E+38)));
if (std::numeric_limits<long double>::digits == 64) {
auto ld = (std::numeric_limits<long double>::min)();
EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932");
}
EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0));
EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234));
EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001));
@@ -903,13 +1004,16 @@ TEST(format_test, precision) {
EXPECT_EQ("1.0e-34", fmt::format("{:.1e}", 1e-34));
EXPECT_THROW_MSG(
fmt::format(runtime("{0:.2}"), reinterpret_cast<void*>(0xcafe)),
(void)fmt::format(runtime("{0:.2}"), reinterpret_cast<void*>(0xcafe)),
format_error, "precision not allowed for this argument type");
EXPECT_THROW_MSG(
fmt::format(runtime("{0:.2f}"), reinterpret_cast<void*>(0xcafe)),
(void)fmt::format(runtime("{0:.2f}"), reinterpret_cast<void*>(0xcafe)),
format_error, "precision not allowed for this argument type");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.{}e}"), 42.0,
fmt::detail::max_value<int>()),
format_error, "number is too big");
EXPECT_THROW_MSG(
fmt::format(runtime("{:.{}e}"), 42.0, fmt::detail::max_value<int>()),
(void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304),
format_error, "number is too big");
EXPECT_EQ("st", fmt::format("{0:.2}", "str"));
@@ -919,86 +1023,97 @@ TEST(format_test, runtime_precision) {
char format_str[buffer_size];
safe_sprintf(format_str, "{0:.{%u", UINT_MAX);
increment(format_str + 5);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"invalid format string");
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"argument not found");
format_str[size + 1] = '}';
format_str[size + 2] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{"), 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{}"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{}"), 0), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{?}}"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{?}}"), 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}"), 0, 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}"), 0, 0), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0), format_error,
"argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{0:}}"), 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{0:}}"), 0), format_error,
"invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, -1), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, -1), format_error,
"negative precision");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1u)),
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1u)),
format_error, "number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, -1l), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, -1l), format_error,
"negative precision");
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX;
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, (value + 1)),
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (value + 1)),
format_error, "number is too big");
}
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1ul)),
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1ul)),
format_error, "number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, '0'), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, '0'), format_error,
"precision is not integer");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, 0.0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, 0.0), format_error,
"precision is not integer");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42u, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42u, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42u, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42u, 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42l, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42l, 2), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42l, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42l, 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42ul, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42ul, 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42ul, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42ul, 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42ll, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42ll, 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42ll, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42ll, 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42ull, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42ull, 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42ull, 2), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42ull, 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:3.{1}}"), 'x', 0), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.{1}}"), 'x', 0),
format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2", fmt::format("{0:.{1}}", 1.2345, 2));
EXPECT_EQ("1.2", fmt::format("{1:.{0}}", 2, 1.2345l));
EXPECT_THROW_MSG(
fmt::format(runtime("{0:.{1}}"), reinterpret_cast<void*>(0xcafe), 2),
format_error, "precision not allowed for this argument type");
EXPECT_THROW_MSG(
fmt::format(runtime("{0:.{1}f}"), reinterpret_cast<void*>(0xcafe), 2),
format_error, "precision not allowed for this argument type");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"),
reinterpret_cast<void*>(0xcafe), 2),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"),
reinterpret_cast<void*>(0xcafe), 2),
format_error,
"precision not allowed for this argument type");
EXPECT_EQ("st", fmt::format("{0:.{1}}", "str", 2));
}
@@ -1023,21 +1138,21 @@ TEST(format_test, format_short) {
template <typename T>
void check_unknown_types(const T& value, const char* types, const char*) {
char format_str[buffer_size];
const char* special = ".0123456789L}";
const char* special = ".0123456789L?}";
for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) {
char c = static_cast<char>(i);
if (std::strchr(types, c) || std::strchr(special, c) || !c) continue;
safe_sprintf(format_str, "{0:10%c}", c);
const char* message = "invalid type specifier";
EXPECT_THROW_MSG(fmt::format(runtime(format_str), value), format_error,
message)
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), value),
format_error, message)
<< format_str << " " << message;
}
}
TEST(format_test, format_int) {
EXPECT_THROW_MSG(fmt::format(runtime("{0:v"), 42), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:v"), 42), format_error,
"invalid type specifier");
check_unknown_types(42, "bBdoxXnLc", "integer");
EXPECT_EQ("x", fmt::format("{:c}", static_cast<int>('x')));
}
@@ -1192,32 +1307,30 @@ TEST(format_test, format_float) {
}
TEST(format_test, format_double) {
EXPECT_EQ("0", fmt::format("{}", 0.0));
EXPECT_EQ(fmt::format("{}", 0.0), "0");
check_unknown_types(1.2, "eEfFgGaAnL%", "double");
EXPECT_EQ("0", fmt::format("{:}", 0.0));
EXPECT_EQ("0.000000", fmt::format("{:f}", 0.0));
EXPECT_EQ("0", fmt::format("{:g}", 0.0));
EXPECT_EQ("392.65", fmt::format("{:}", 392.65));
EXPECT_EQ("392.65", fmt::format("{:g}", 392.65));
EXPECT_EQ("392.65", fmt::format("{:G}", 392.65));
EXPECT_EQ("4.9014e+06", fmt::format("{:g}", 4.9014e6));
EXPECT_EQ("392.650000", fmt::format("{:f}", 392.65));
EXPECT_EQ("392.650000", fmt::format("{:F}", 392.65));
EXPECT_EQ("42", fmt::format("{:L}", 42.0));
EXPECT_EQ(" 0x1.0cccccccccccdp+2", fmt::format("{:24a}", 4.2));
EXPECT_EQ("0x1.0cccccccccccdp+2 ", fmt::format("{:<24a}", 4.2));
EXPECT_EQ(fmt::format("{:}", 0.0), "0");
EXPECT_EQ(fmt::format("{:f}", 0.0), "0.000000");
EXPECT_EQ(fmt::format("{:g}", 0.0), "0");
EXPECT_EQ(fmt::format("{:}", 392.65), "392.65");
EXPECT_EQ(fmt::format("{:g}", 392.65), "392.65");
EXPECT_EQ(fmt::format("{:G}", 392.65), "392.65");
EXPECT_EQ(fmt::format("{:g}", 4.9014e6), "4.9014e+06");
EXPECT_EQ(fmt::format("{:f}", 392.65), "392.650000");
EXPECT_EQ(fmt::format("{:F}", 392.65), "392.650000");
EXPECT_EQ(fmt::format("{:L}", 42.0), "42");
EXPECT_EQ(fmt::format("{:24a}", 4.2), " 0x1.0cccccccccccdp+2");
EXPECT_EQ(fmt::format("{:<24a}", 4.2), "0x1.0cccccccccccdp+2 ");
EXPECT_EQ(fmt::format("{0:e}", 392.65), "3.926500e+02");
EXPECT_EQ(fmt::format("{0:E}", 392.65), "3.926500E+02");
EXPECT_EQ(fmt::format("{0:+010.4g}", 392.65), "+0000392.6");
char buffer[buffer_size];
safe_sprintf(buffer, "%e", 392.65);
EXPECT_EQ(buffer, fmt::format("{0:e}", 392.65));
safe_sprintf(buffer, "%E", 392.65);
EXPECT_EQ(buffer, fmt::format("{0:E}", 392.65));
EXPECT_EQ("+0000392.6", fmt::format("{0:+010.4g}", 392.65));
safe_sprintf(buffer, "%a", -42.0);
EXPECT_EQ(buffer, fmt::format("{:a}", -42.0));
EXPECT_EQ(fmt::format("{:a}", -42.0), buffer);
safe_sprintf(buffer, "%A", -42.0);
EXPECT_EQ(buffer, fmt::format("{:A}", -42.0));
EXPECT_EQ("9223372036854775808.000000",
fmt::format("{:f}", 9223372036854775807.0));
EXPECT_EQ(fmt::format("{:A}", -42.0), buffer);
EXPECT_EQ(fmt::format("{:f}", 9223372036854775807.0),
"9223372036854775808.000000");
}
TEST(format_test, precision_rounding) {
@@ -1326,6 +1439,9 @@ TEST(format_test, format_char) {
<< format_str;
}
EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x'));
EXPECT_EQ("\n", fmt::format("{}", '\n'));
EXPECT_EQ("'\\n'", fmt::format("{:?}", '\n'));
}
TEST(format_test, format_volatile_char) {
@@ -1345,26 +1461,10 @@ TEST(format_test, format_cstring) {
char nonconst[] = "nonconst";
EXPECT_EQ("nonconst", fmt::format("{0}", nonconst));
EXPECT_THROW_MSG(
fmt::format(runtime("{0}"), static_cast<const char*>(nullptr)),
(void)fmt::format(runtime("{0}"), static_cast<const char*>(nullptr)),
format_error, "string pointer is null");
}
TEST(format_test, format_schar_string) {
signed char str[] = "test";
EXPECT_EQ("test", fmt::format("{0:s}", str));
const signed char* const_str = str;
EXPECT_EQ("test", fmt::format("{0:s}", const_str));
}
TEST(format_test, format_uchar_string) {
unsigned char str[] = "test";
EXPECT_EQ("test", fmt::format("{0:s}", str));
const unsigned char* const_str = str;
EXPECT_EQ("test", fmt::format("{0:s}", const_str));
unsigned char* ptr = str;
EXPECT_EQ("test", fmt::format("{0:s}", ptr));
}
void function_pointer_test(int, double, std::string) {}
TEST(format_test, format_pointer) {
@@ -1388,14 +1488,43 @@ TEST(format_test, format_pointer) {
EXPECT_EQ("0x0", fmt::format("{}", nullptr));
}
TEST(format_test, write_uintptr_fallback) {
// Test that formatting a pointer by converting it to uint128_fallback works.
// This is needed to support systems without uintptr_t.
auto s = std::string();
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::bit_cast<fmt::detail::uint128_fallback>(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ(s, "0xface");
}
enum class color { red, green, blue };
namespace test_ns {
enum class color { red, green, blue };
using fmt::enums::format_as;
} // namespace test_ns
TEST(format_test, format_enum_class) {
EXPECT_EQ(fmt::format("{}", fmt::underlying(color::red)), "0");
EXPECT_EQ(fmt::format("{}", test_ns::color::red), "0");
}
TEST(format_test, format_string) {
EXPECT_EQ("test", fmt::format("{0}", std::string("test")));
EXPECT_THROW(fmt::format(fmt::runtime("{:x}"), std::string("test")),
EXPECT_EQ(fmt::format("{0}", std::string("test")), "test");
EXPECT_EQ(fmt::format("{0}", std::string("test")), "test");
EXPECT_EQ(fmt::format("{:?}", std::string("test")), "\"test\"");
EXPECT_EQ(fmt::format("{:*^10?}", std::string("test")), "**\"test\"**");
EXPECT_EQ(fmt::format("{:?}", std::string("\test")), "\"\\test\"");
EXPECT_THROW((void)fmt::format(fmt::runtime("{:x}"), std::string("test")),
fmt::format_error);
}
TEST(format_test, format_string_view) {
EXPECT_EQ("test", fmt::format("{}", string_view("test")));
EXPECT_EQ("\"t\\nst\"", fmt::format("{:?}", string_view("t\nst")));
EXPECT_EQ("", fmt::format("{}", string_view()));
}
@@ -1481,7 +1610,7 @@ template <> struct formatter<Answer> : formatter<int> {
FMT_END_NAMESPACE
TEST(format_test, format_custom) {
EXPECT_THROW_MSG(fmt::format(runtime("{:s}"), date(2012, 12, 9)),
EXPECT_THROW_MSG((void)fmt::format(runtime("{:s}"), date(2012, 12, 9)),
format_error, "unknown format specifier");
EXPECT_EQ("42", fmt::format("{0}", Answer()));
EXPECT_EQ("0042", fmt::format("{:04}", Answer()));
@@ -1558,8 +1687,9 @@ TEST(format_test, format_examples) {
fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}", 42));
EXPECT_EQ("The answer is 42", fmt::format("The answer is {}", 42));
EXPECT_THROW_MSG(fmt::format(runtime("The answer is {:d}"), "forty-two"),
format_error, "invalid type specifier");
EXPECT_THROW_MSG(
(void)fmt::format(runtime("The answer is {:d}"), "forty-two"),
format_error, "invalid type specifier");
EXPECT_WRITE(
stdout, fmt::print("{}", std::numeric_limits<double>::infinity()), "inf");
@@ -1595,6 +1725,11 @@ TEST(format_test, bytes) {
EXPECT_EQ(10, s.size());
}
TEST(format_test, group_digits_view) {
EXPECT_EQ(fmt::format("{}", fmt::group_digits(10000000)), "10,000,000");
EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(1000)), " 1,000");
}
enum test_enum { foo, bar };
TEST(format_test, join) {
@@ -1718,6 +1853,21 @@ TEST(format_test, custom_format_compile_time_string) {
using namespace fmt::literals;
# if FMT_GCC_VERSION
# define FMT_CHECK_DEPRECATED_UDL_FORMAT 1
# elif FMT_CLANG_VERSION && defined(__has_warning)
# if __has_warning("-Wdeprecated-declarations")
# define FMT_CHECK_DEPRECATED_UDL_FORMAT 1
# endif
# endif
# ifndef FMT_CHECK_DEPRECATED_UDL_FORMAT
# define FMT_CHECK_DEPRECATED_UDL_FORMAT 0
# endif
# if FMT_CHECK_DEPRECATED_UDL_FORMAT
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
TEST(format_test, format_udl) {
EXPECT_EQ("{}c{}"_format("ab", 1), fmt::format("{}c{}", "ab", 1));
EXPECT_EQ("foo"_format(), "foo");
@@ -1725,6 +1875,9 @@ TEST(format_test, format_udl) {
EXPECT_EQ("{}"_format(date(2015, 10, 21)), "2015-10-21");
}
# pragma GCC diagnostic pop
# endif
TEST(format_test, named_arg_udl) {
auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra",
"second"_a = "cad", "third"_a = 99);
@@ -1777,21 +1930,21 @@ TEST(format_test, dynamic_formatter) {
EXPECT_EQ("42", fmt::format("{:d}", num));
EXPECT_EQ("foo", fmt::format("{:s}", str));
EXPECT_EQ(" 42 foo ", fmt::format("{:{}} {:{}}", num, 3, str, 4));
EXPECT_THROW_MSG(fmt::format(runtime("{0:{}}"), num), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{}}"), num), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{:{0}}"), num), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:{0}}"), num), format_error,
"cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{:+}"), str), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:+}"), str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{:-}"), str), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:-}"), str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{: }"), str), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{: }"), str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{:#}"), str), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:#}"), str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{:0}"), str), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:0}"), str), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{:.2}"), num), format_error,
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.2}"), num), format_error,
"precision not allowed for this argument type");
}
@@ -1815,13 +1968,20 @@ struct formatter<adl_test::fmt::detail::foo> : formatter<std::string> {
};
FMT_END_NAMESPACE
TEST(format_test, to_string) {
EXPECT_EQ("42", fmt::to_string(42));
EXPECT_EQ("0x1234", fmt::to_string(reinterpret_cast<void*>(0x1234)));
EXPECT_EQ("foo", fmt::to_string(adl_test::fmt::detail::foo()));
struct convertible_to_int {
operator int() const { return value; }
enum test_enum2 : unsigned char { test_value };
EXPECT_EQ("0", fmt::to_string(test_value));
int value = 42;
};
TEST(format_test, to_string) {
EXPECT_EQ(fmt::to_string(42), "42");
EXPECT_EQ(fmt::to_string(reinterpret_cast<void*>(0x1234)), "0x1234");
EXPECT_EQ(fmt::to_string(adl_test::fmt::detail::foo()), "foo");
EXPECT_EQ(fmt::to_string(convertible_to_int()), "42");
enum foo : unsigned char { zero };
EXPECT_EQ(fmt::to_string(zero), "0");
}
TEST(format_test, output_iterators) {

View File

@@ -25,6 +25,6 @@ function(add_fuzzer source)
target_compile_features(${name} PRIVATE cxx_generic_lambdas)
endfunction()
foreach (source chrono-duration.cc float.cc named-arg.cc one-arg.cc two-args.cc)
foreach (source chrono-duration.cc chrono-timepoint.cc float.cc named-arg.cc one-arg.cc two-args.cc)
add_fuzzer(${source})
endforeach ()

View File

@@ -22,6 +22,8 @@ here=$(pwd)
CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17"
CLANG=clang++-11
# For performance analysis of the fuzzers.
builddir=$here/build-fuzzers-perfanalysis
mkdir -p $builddir
@@ -37,7 +39,7 @@ cmake --build $builddir
builddir=$here/build-fuzzers-ossfuzz
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXX=$CLANG \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \
@@ -50,7 +52,7 @@ cmake --build $builddir
builddir=$here/build-fuzzers-libfuzzer
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXX=$CLANG \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,address,undefined" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \
@@ -62,7 +64,7 @@ cmake --build $builddir
builddir=$here/build-fuzzers-fast
mkdir -p $builddir
cd $builddir
CXX="clang++" \
CXX=$CLANG \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link -O3" cmake \
cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \

View File

@@ -1,9 +1,10 @@
// Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h.
#include <cstdint>
#include <fmt/chrono.h>
#include <cstdint>
#include "fuzzer-common.h"
template <typename Period, typename Rep>
@@ -31,7 +32,7 @@ void invoke_outer(const uint8_t* data, size_t size, int period) {
data += fixed_size;
size -= fixed_size;
// data is already allocated separately in libFuzzer so reading past the end
// data is already allocated separately in libFuzzer so reading past the end
// will most likely be detected anyway.
const auto format_str = fmt::string_view(as_chars(data), size);
@@ -86,7 +87,7 @@ void invoke_outer(const uint8_t* data, size_t size, int period) {
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size <= 4) return 0;
if (size <= 4) return 0;
const auto representation = data[0];
const auto period = data[1];

View File

@@ -0,0 +1,32 @@
// Copyright (c) 2021, Paul Dreik
// For license information refer to format.h.
#include <fmt/chrono.h>
#include "fuzzer-common.h"
/*
* a fuzzer for the chrono timepoints formatters
* C is a clock (std::chrono::system_clock etc)
*/
template <typename C> void doit(const uint8_t* data, size_t size) {
using Rep = typename C::time_point::rep;
constexpr auto N = sizeof(Rep);
if (size < N) return;
const auto x = assign_from_buf<Rep>(data);
typename C::duration dur{x};
typename C::time_point timepoint{dur};
data += N;
size -= N;
data_to_string format_str(data, size);
std::string message = fmt::format(format_str.get(), timepoint);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
try {
doit<std::chrono::system_clock>(data, size);
} catch (...) {
}
return 0;
}

View File

@@ -1,17 +1,18 @@
// A fuzzer for floating-point formatter.
// For the license information refer to format.h.
#include <fmt/format.h>
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
#include <limits>
#include <fmt/format.h>
#include <stdexcept>
#include "fuzzer-common.h"
void check_round_trip(fmt::string_view format_str, double value) {
auto buffer = fmt::memory_buffer();
fmt::format_to(buffer, format_str, value);
fmt::format_to(std::back_inserter(buffer), format_str, value);
if (std::isnan(value)) {
auto nan = std::signbit(value) ? "-nan" : "nan";
@@ -24,8 +25,7 @@ void check_round_trip(fmt::string_view format_str, double value) {
char* ptr = nullptr;
if (std::strtod(buffer.data(), &ptr) != value)
throw std::runtime_error("round trip failure");
if (ptr + 1 != buffer.end())
throw std::runtime_error("unparsed output");
if (ptr + 1 != buffer.end()) throw std::runtime_error("unparsed output");
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {

View File

@@ -4,12 +4,12 @@
#ifndef FUZZER_COMMON_H
#define FUZZER_COMMON_H
#include <cstdint> // std::uint8_t
#include <cstring> // memcpy
#include <vector>
#include <fmt/core.h>
#include <cstdint> // std::uint8_t
#include <cstring> // memcpy
#include <vector>
// One can format to either a string, or a buffer. The latter is faster, but
// one may be interested in formatting to a string instead to verify it works
// as intended. To avoid a combinatoric explosion, select this at compile time
@@ -56,7 +56,9 @@ struct data_to_string {
data_to_string(const uint8_t* data, size_t size, bool add_terminator = false)
: buffer(size + (add_terminator ? 1 : 0)) {
std::memcpy(buffer.data(), data, size);
if (size) {
std::memcpy(buffer.data(), data, size);
}
}
fmt::string_view get() const { return {buffer.data(), buffer.size()}; }

View File

@@ -1,10 +1,11 @@
// Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h.
#include <fmt/chrono.h>
#include <cstdint>
#include <type_traits>
#include <vector>
#include <fmt/chrono.h>
#include "fuzzer-common.h"
@@ -25,10 +26,11 @@ void invoke_fmt(const uint8_t* data, size_t size, unsigned arg_name_size) {
try {
#if FMT_FUZZ_FORMAT_TO_STRING
std::string message =
fmt::format(format_str.get(), fmt::arg(arg_name.data(), value));
fmt::format(format_str.get(), fmt::arg(arg_name.data(), value));
#else
fmt::memory_buffer out;
fmt::format_to(out, format_str.get(), fmt::arg(arg_name.data(), value));
fmt::format_to(std::back_inserter(out), format_str.get(),
fmt::arg(arg_name.data(), value));
#endif
} catch (std::exception&) {
}

View File

@@ -1,17 +1,18 @@
// Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h.
#include <fmt/chrono.h>
#include <cstdint>
#include <exception>
#include <fmt/chrono.h>
#include "fuzzer-common.h"
template <typename T, typename Repr>
const T* from_repr(const Repr& r) { return &r; }
template <typename T, typename Repr> const T* from_repr(const Repr& r) {
return &r;
}
template <>
const std::tm* from_repr<std::tm>(const std::time_t& t) {
template <> const std::tm* from_repr<std::tm>(const std::time_t& t) {
return std::localtime(&t);
}

View File

@@ -1,10 +1,11 @@
// Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h.
#include <fmt/format.h>
#include <cstdint>
#include <exception>
#include <string>
#include <fmt/format.h>
#include "fuzzer-common.h"

View File

@@ -23,7 +23,7 @@ output_redirect::output_redirect(FILE* f) : file_(f) {
write_end.dup2(fd);
}
output_redirect::~output_redirect() FMT_NOEXCEPT {
output_redirect::~output_redirect() noexcept {
try {
restore();
} catch (const std::exception& e) {

View File

@@ -15,8 +15,8 @@
#ifdef FMT_MODULE_TEST
import fmt;
#else
#include "fmt/os.h"
#endif // FMG_MODULE_TEST
# include "fmt/os.h"
#endif // FMG_MODULE_TEST
#include "gmock/gmock.h"
@@ -83,7 +83,7 @@ class output_redirect {
public:
explicit output_redirect(FILE* file);
~output_redirect() FMT_NOEXCEPT;
~output_redirect() noexcept;
output_redirect(const output_redirect&) = delete;
void operator=(const output_redirect&) = delete;

View File

@@ -17,6 +17,13 @@ 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)

View File

@@ -1,7 +1,11 @@
// Header-only configuration test
#include "fmt/core.h"
#include "fmt/ostream.h"
#include "gtest/gtest.h"
#ifndef FMT_HEADER_ONLY
# error "Not in the header-only mode."
#endif
TEST(header_only_test, format) { EXPECT_EQ(fmt::format("foo"), "foo"); }

View File

@@ -36,7 +36,6 @@
#else
# define FMT_USE_FCNTL 0
#endif
#define FMT_NOEXCEPT noexcept
#if defined(_WIN32) && !defined(__MINGW32__)
# define FMT_POSIX(call) _##call
#else
@@ -76,8 +75,10 @@ bool oops_detail_namespace_is_visible;
namespace fmt {
bool namespace_detail_invisible() {
#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \
_MSC_FULL_VER <= 192930129
// bug in msvc up to 16.11-pre1:
((_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:
// the namespace is visible even when it is neither
// implicitly nor explicitly exported
return true;
@@ -97,8 +98,8 @@ TEST(module_test, detail_namespace) {
// macros must not be imported from a *named* module [cpp.import]/5.1
TEST(module_test, macros) {
#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \
_MSC_FULL_VER <= 192930129
// bug in msvc up to 16.11-pre1:
_MSC_FULL_VER <= 192930130
// bug in msvc up to 16.11-pre2:
// include-guard macros leak from BMI
// and even worse: they cannot be #undef-ined
macro_leaked = false;
@@ -194,13 +195,6 @@ TEST(module_test, wformat_args) {
EXPECT_TRUE(args.get(0));
}
TEST(module_test, checked_format_args) {
fmt::basic_format_args args = fmt::make_args_checked<int>("{}", 42);
EXPECT_TRUE(args.get(0));
fmt::basic_format_args wargs = fmt::make_args_checked<int>(L"{}", 42);
EXPECT_TRUE(wargs.get(0));
}
TEST(module_test, dynamic_format_args) {
fmt::dynamic_format_arg_store<fmt::format_context> dyn_store;
dyn_store.push_back(fmt::arg("a42", 42));
@@ -456,8 +450,7 @@ TEST(module_test, time_duration) {
}
TEST(module_test, weekday) {
EXPECT_EQ("Monday",
std::format(std::locale::classic(), "{:%A}", fmt::weekday(1)));
EXPECT_EQ("Mon", fmt::format(std::locale::classic(), "{}", fmt::weekday(1)));
}
TEST(module_test, to_string_view) {

18
vendor/Fmt/test/noexception-test.cc vendored Normal file
View File

@@ -0,0 +1,18 @@
// Formatting library for C++ - Noexception tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/args.h"
#include "fmt/chrono.h"
#include "fmt/color.h"
#include "fmt/compile.h"
#include "fmt/core.h"
#include "fmt/format.h"
#include "fmt/os.h"
#include "fmt/ostream.h"
#include "fmt/printf.h"
#include "fmt/ranges.h"
#include "fmt/xchar.h"

View File

@@ -548,13 +548,4 @@ TEST(file_test, fdopen) {
int read_fd = read_end.descriptor();
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
}
# ifdef FMT_LOCALE
TEST(locale_test, strtod) {
fmt::locale loc;
const char *start = "4.2", *ptr = start;
EXPECT_EQ(4.2, loc.strtod(ptr));
EXPECT_EQ(start + 3, ptr);
}
# endif
#endif // FMT_USE_FCNTL

View File

@@ -23,6 +23,7 @@ template <> struct formatter<test> : formatter<int> {
#include <sstream>
#include "fmt/compile.h"
#include "fmt/ostream.h"
#include "fmt/ranges.h"
#include "gmock/gmock.h"
@@ -52,6 +53,16 @@ std::ostream& operator<<(std::ostream& os, streamable_enum) {
enum unstreamable_enum {};
struct empty_test {};
std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; }
namespace fmt {
template <> struct formatter<test_string> : ostream_formatter {};
template <> struct formatter<date> : ostream_formatter {};
template <> struct formatter<streamable_enum> : ostream_formatter {};
template <> struct formatter<empty_test> : ostream_formatter {};
} // namespace fmt
TEST(ostream_test, enum) {
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum()));
EXPECT_EQ("0", fmt::format("{}", unstreamable_enum()));
@@ -69,25 +80,22 @@ 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_EQ("def**", fmt::format("{0:*<5}", test_string("def")));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), test_string()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), test_string()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), test_string()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), test_string()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), test_string()), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), test_string()),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), test_string()),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), test_string()),
format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), test_string()),
format_error, "format specifier requires numeric argument");
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")));
EXPECT_EQ("test ", fmt::format("{0:{1}}", test_string("test"), 13));
EXPECT_EQ("te", fmt::format("{0:.2}", test_string("test")));
EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2));
}
struct empty_test {};
std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; }
TEST(ostream_test, empty_custom_output) {
EXPECT_EQ("", fmt::format("{}", empty_test()));
}
@@ -183,6 +191,8 @@ template <typename T> struct formatter<test_template<T>> : formatter<int> {
return formatter<int>::format(2, ctx);
}
};
template <> struct formatter<fmt_test::abc> : ostream_formatter {};
} // namespace fmt
TEST(ostream_test, template) {
@@ -213,41 +223,6 @@ TEST(ostream_test, disable_builtin_ostream_operators) {
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
}
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(ostream_test, format_explicitly_convertible_to_string_like) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator fmt::detail::std_string_view<char>() const {
return {"foo", 3u};
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_std_string_view) {
return os << "bar";
}
TEST(ostream_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#endif // FMT_USE_STRING_VIEW
struct streamable_and_convertible_to_bool {
operator bool() const { return true; }
};
@@ -257,7 +232,23 @@ std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) {
}
TEST(ostream_test, format_convertible_to_bool) {
EXPECT_EQ("foo", fmt::format("{}", streamable_and_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 {};
@@ -268,6 +259,10 @@ std::ostream& operator<<(std::ostream& os, copyfmt_test) {
return os << "foo";
}
namespace fmt {
template <> struct formatter<copyfmt_test> : ostream_formatter {};
} // namespace fmt
TEST(ostream_test, copyfmt) {
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
}
@@ -280,3 +275,24 @@ TEST(ostream_test, range) {
auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")};
EXPECT_EQ("[foo, bar]", fmt::format("{}", strs));
}
struct abstract {
virtual ~abstract() = default;
virtual void f() = 0;
friend std::ostream& operator<<(std::ostream& os, const abstract&) {
return os;
}
};
namespace fmt {
template <> struct formatter<abstract> : ostream_formatter {};
} // namespace fmt
void format_abstract_compiles(const abstract& a) {
fmt::format(FMT_COMPILE("{}"), a);
}
TEST(ostream_test, is_formattable) {
EXPECT_TRUE(fmt::is_formattable<std::string>());
EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>());
}

View File

@@ -457,110 +457,3 @@ TEST(scoped_mock, scope) {
}
EXPECT_EQ(nullptr, test_mock::instance);
}
#ifdef FMT_LOCALE
using locale_type = fmt::locale::type;
struct locale_mock {
static locale_mock* instance;
MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
locale_type base));
MOCK_METHOD1(freelocale, void(locale_type locale));
MOCK_METHOD3(strtod_l,
double(const char* nptr, char** endptr, locale_type locale));
} * locale_mock::instance;
# ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4273)
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Winconsistent-dllimport"
# endif
_locale_t _create_locale(int category, const char* locale) {
return locale_mock::instance->newlocale(category, locale, 0);
}
void _free_locale(_locale_t locale) {
locale_mock::instance->freelocale(locale);
}
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# ifdef __clang__
# pragma clang diagnostic pop
# endif
# pragma warning(pop)
# endif
# if defined(__THROW) && \
((FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408) || defined(__e2k__))
# define FMT_LOCALE_THROW __THROW
# else
# define FMT_LOCALE_THROW
# endif
# if defined(__APPLE__) || \
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
typedef int FreeLocaleResult;
# else
typedef void FreeLocaleResult;
# endif
FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW {
locale_mock::instance->freelocale(locale);
return FreeLocaleResult();
}
double strtod_l(const char* nptr, char** endptr,
locale_type locale) FMT_LOCALE_THROW {
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# undef FMT_LOCALE_THROW
# ifndef _WIN32
locale_t test::newlocale(int category_mask, const char* locale, locale_t base) {
return locale_mock::instance->newlocale(category_mask, locale, base);
}
TEST(locale_test, locale_mock) {
scoped_mock<locale_mock> mock;
auto locale = reinterpret_cast<locale_type>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
FMT_SYSTEM(newlocale(222, "foo", locale));
}
# endif
TEST(locale_test, locale) {
# ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC };
# endif
scoped_mock<locale_mock> mock;
auto impl = reinterpret_cast<locale_type>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
.WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl));
fmt::locale loc;
EXPECT_EQ(impl, loc.get());
}
TEST(locale_test, strtod) {
scoped_mock<locale_mock> mock;
EXPECT_CALL(mock, newlocale(_, _, _))
.WillOnce(Return(reinterpret_cast<locale_type>(42)));
EXPECT_CALL(mock, freelocale(_));
fmt::locale loc;
const char* str = "4.2";
char end = 'x';
EXPECT_CALL(mock, strtod_l(str, _, loc.get()))
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
EXPECT_EQ(777, loc.strtod(str));
EXPECT_EQ(&end, str);
}
#endif // FMT_LOCALE

View File

@@ -11,7 +11,6 @@
#include <climits>
#include <cstring>
#include "fmt/ostream.h"
#include "fmt/xchar.h"
#include "gtest-extra.h"
#include "util.h"
@@ -481,12 +480,6 @@ TEST(printf_test, string) {
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
}
TEST(printf_test, uchar_string) {
unsigned char str[] = "test";
unsigned char* pstr = str;
EXPECT_EQ("test", fmt::sprintf("%s", pstr));
}
TEST(printf_test, pointer) {
int n;
void* p = &n;
@@ -539,10 +532,6 @@ TEST(printf_test, wide_string) {
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
}
TEST(printf_test, printf_custom) {
EXPECT_EQ("abc", test_sprintf("%s", test_string("abc")));
}
TEST(printf_test, vprintf) {
fmt::format_arg_store<fmt::printf_context, int> as{42};
fmt::basic_format_args<fmt::printf_context> args(as);

17
vendor/Fmt/test/ranges-odr-test.cc vendored Normal file
View File

@@ -0,0 +1,17 @@
// Formatting library for C++ - the core API
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include <vector>
#include "fmt/ranges.h"
#include "gtest/gtest.h"
// call fmt::format from another translation unit to test ODR
TEST(ranges_odr_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]");
}

View File

@@ -46,16 +46,42 @@ TEST(ranges_test, format_array_of_literals) {
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]");
}
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]]");
}
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("{}", m), "{\"one\": 1, \"two\": 2}");
}
TEST(ranges_test, format_set) {
EXPECT_EQ(fmt::format("{}", std::set<std::string>{"one", "two"}),
"{\"one\", \"two\"}");
}
namespace adl {
struct box {
int value;
};
auto begin(const box& b) -> const int* {
return &b.value;
}
auto end(const box& b) -> const int* {
return &b.value + 1;
}
} // namespace adl
TEST(ranges_test, format_adl_begin_end) {
auto b = adl::box{42};
EXPECT_EQ(fmt::format("{}", b), "[42]");
}
TEST(ranges_test, format_pair) {
@@ -126,8 +152,8 @@ TEST(ranges_test, path_like) {
struct string_like {
const char* begin();
const char* end();
explicit operator fmt::string_view() const { return "foo"; }
explicit operator std::string_view() const { return "foo"; }
operator fmt::string_view() const { return "foo"; }
operator std::string_view() const { return "foo"; }
};
TEST(ranges_test, format_string_like) {
@@ -190,7 +216,14 @@ TEST(ranges_test, range) {
EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]");
}
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
enum test_enum { foo };
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 {};
TEST(ranges_test, unformattable_range) {
@@ -217,6 +250,21 @@ TEST(ranges_test, join_tuple) {
// Single element tuple.
auto t4 = std::tuple<float>(4.0f);
EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4");
# if FMT_TUPLE_JOIN_SPECIFIERS
// Specs applied to each element.
auto t5 = std::tuple<int, int, long>(-3, 100, 1);
EXPECT_EQ(fmt::format("{:+03}", fmt::join(t5, ", ")), "-03, +100, +01");
auto t6 = std::tuple<float, double, long double>(3, 3.14, 3.1415);
EXPECT_EQ(fmt::format("{:5.5f}", fmt::join(t6, ", ")),
"3.00000, 3.14000, 3.14150");
// Testing lvalue tuple args.
int y = -1;
auto t7 = std::tuple<int, int&, const int&>(3, y, y);
EXPECT_EQ(fmt::format("{:03}", fmt::join(t7, ", ")), "003, -01, -01");
# endif
}
TEST(ranges_test, join_initializer_list) {
@@ -236,9 +284,40 @@ struct zstring {
zstring_sentinel end() const { return {}; }
};
# ifdef __cpp_lib_ranges
struct cpp20_only_range {
struct iterator {
int val = 0;
using value_type = int;
using difference_type = std::ptrdiff_t;
using iterator_concept = std::input_iterator_tag;
iterator() = default;
iterator(int i) : val(i) {}
int operator*() const { return val; }
iterator& operator++() {
++val;
return *this;
}
void operator++(int) { ++*this; }
bool operator==(const iterator& rhs) const { return val == rhs.val; }
};
int lo;
int hi;
iterator begin() const { return iterator(lo); }
iterator end() const { return iterator(hi); }
};
static_assert(std::input_iterator<cpp20_only_range::iterator>);
# endif
TEST(ranges_test, join_sentinel) {
auto hello = zstring{"hello"};
EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']");
EXPECT_EQ(fmt::format("{::}", hello), "[h, e, l, l, o]");
EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o");
}
@@ -260,18 +339,62 @@ TEST(ranges_test, join_range) {
const auto z = std::vector<int>(3u, 0);
EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0");
# ifdef __cpp_lib_ranges
EXPECT_EQ(fmt::format("{}", cpp20_only_range{.lo = 0, .hi = 5}),
"[0, 1, 2, 3, 4]");
EXPECT_EQ(
fmt::format("{}", fmt::join(cpp20_only_range{.lo = 0, .hi = 5}, ",")),
"0,1,2,3,4");
# endif
}
#endif // FMT_RANGES_TEST_ENABLE_JOIN
TEST(ranges_test, is_printable) {
using fmt::detail::is_printable;
EXPECT_TRUE(is_printable(0x0323));
EXPECT_FALSE(is_printable(0x0378));
EXPECT_FALSE(is_printable(0x110000));
}
TEST(ranges_test, escape_string) {
using vec = std::vector<std::string>;
EXPECT_EQ(fmt::format("{}", vec{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]");
EXPECT_EQ(fmt::format("{}", vec{"\x07"}), "[\"\\x07\"]");
EXPECT_EQ(fmt::format("{}", vec{"\x7f"}), "[\"\\x7f\"]");
EXPECT_EQ(fmt::format("{}", vec{"n\xcc\x83"}), "[\"n\xcc\x83\"]");
// Unassigned Unicode code points.
if (fmt::detail::is_utf8()) {
EXPECT_EQ(fmt::format("{}", vec{"\xcd\xb8"}), "[\"\\u0378\"]");
// Unassigned Unicode code points.
EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xbf"}), "[\"\\U0010ffff\"]");
EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}),
"[\"\\xf4\\x8f\\xbf\\xc0\"]");
}
}
#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\"]");
}
#endif // FMT_USE_STRING_VIEW
template <typename R> struct fmt_ref_view {
R* r;
auto begin() const -> decltype(r->begin()) { return r->begin(); }
auto end() const -> decltype(r->end()) { return r->end(); }
};
TEST(ranges_test, range_of_range_of_mixed_const) {
std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]");
fmt_ref_view<decltype(v)> r{&v};
EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]");
}

View File

@@ -1,161 +0,0 @@
#include <format>
#include "gtest/gtest.h"
TEST(std_format_test, escaping) {
using namespace std;
string s = format("{0}-{{", 8); // s == "8-{"
EXPECT_EQ(s, "8-{");
}
TEST(std_format_test, indexing) {
using namespace std;
string s0 = format("{} to {}", "a", "b"); // OK: automatic indexing
string s1 = format("{1} to {0}", "a", "b"); // OK: manual indexing
EXPECT_EQ(s0, "a to b");
EXPECT_EQ(s1, "b to a");
// Error: mixing automatic and manual indexing
EXPECT_THROW(string s2 = format("{0} to {}", "a", "b"), std::format_error);
// Error: mixing automatic and manual indexing
EXPECT_THROW(string s3 = format("{} to {1}", "a", "b"), std::format_error);
}
TEST(std_format_test, alignment) {
using namespace std;
char c = 120;
string s0 = format("{:6}", 42); // s0 == " 42"
string s1 = format("{:6}", 'x'); // s1 == "x "
string s2 = format("{:*<6}", 'x'); // s2 == "x*****"
string s3 = format("{:*>6}", 'x'); // s3 == "*****x"
string s4 = format("{:*^6}", 'x'); // s4 == "**x***"
// Error: '=' with charT and no integer presentation type
EXPECT_THROW(string s5 = format("{:=6}", 'x'), std::format_error);
string s6 = format("{:6d}", c); // s6 == " 120"
string s7 = format("{:6}", true); // s9 == "true "
EXPECT_EQ(s0, " 42");
EXPECT_EQ(s1, "x ");
EXPECT_EQ(s2, "x*****");
EXPECT_EQ(s3, "*****x");
EXPECT_EQ(s4, "**x***");
EXPECT_EQ(s6, " 120");
EXPECT_EQ(s7, "true ");
}
TEST(std_format_test, float) {
using namespace std;
double inf = numeric_limits<double>::infinity();
double nan = numeric_limits<double>::quiet_NaN();
string s0 = format("{0:} {0:+} {0:-} {0: }", 1); // s0 == "1 +1 1 1"
string s1 = format("{0:} {0:+} {0:-} {0: }", -1); // s1 == "-1 -1 -1 -1"
string s2 =
format("{0:} {0:+} {0:-} {0: }", inf); // s2 == "inf +inf inf inf"
string s3 =
format("{0:} {0:+} {0:-} {0: }", nan); // s3 == "nan +nan nan nan"
EXPECT_EQ(s0, "1 +1 1 1");
EXPECT_EQ(s1, "-1 -1 -1 -1");
EXPECT_EQ(s2, "inf +inf inf inf");
EXPECT_EQ(s3, "nan +nan nan nan");
}
TEST(std_format_test, int) {
using namespace std;
string s0 = format("{}", 42); // s0 == "42"
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
string s3 = format("{:L}", 1234); // s3 == "1234" (depends on the locale)
EXPECT_EQ(s0, "42");
EXPECT_EQ(s1, "101010 42 52 2a");
EXPECT_EQ(s2, "0x2a 0X2A");
EXPECT_EQ(s3, "1234");
}
#include <format>
enum color { red, green, blue };
const char* color_names[] = {"red", "green", "blue"};
template <> struct std::formatter<color> : std::formatter<const char*> {
auto format(color c, format_context& ctx) {
return formatter<const char*>::format(color_names[c], ctx);
}
};
struct err {};
TEST(std_format_test, formatter) {
std::string s0 = std::format("{}", 42); // OK: library-provided formatter
// std::string s1 = std::format("{}", L"foo"); // Ill-formed: disabled
// formatter
std::string s2 = std::format("{}", red); // OK: user-provided formatter
// std::string s3 = std::format("{}", err{}); // Ill-formed: disabled
// formatter
EXPECT_EQ(s0, "42");
EXPECT_EQ(s2, "red");
}
struct S {
int value;
};
template <> struct std::formatter<S> {
size_t width_arg_id = 0;
// Parses a width argument id in the format { <digit> }.
constexpr auto parse(format_parse_context& ctx) {
constexpr auto is_ascii_digit = [](const char c) {
return c >= '0' && c <= '9';
};
auto iter = ctx.begin();
// auto get_char = [&]() { return iter != ctx.end() ? *iter : 0; };
auto get_char = [&]() { return iter != ctx.end() ? *iter : '\0'; };
if (get_char() != '{') return iter;
++iter;
char c = get_char();
if (!is_ascii_digit(c) || (++iter, get_char()) != '}')
throw format_error("invalid format");
width_arg_id = fmt::detail::to_unsigned(c - '0');
ctx.check_arg_id(width_arg_id);
return ++iter;
}
// Formats S with width given by the argument width_arg_id.
auto format(S s, format_context& ctx) {
int width = visit_format_arg(
[](auto value) -> int {
using type = decltype(value);
if constexpr (!is_integral_v<type> || is_same_v<type, bool>)
throw format_error("width is not integral");
// else if (value < 0 || value > numeric_limits<int>::max())
else if (fmt::detail::is_negative(value) ||
value > numeric_limits<int>::max())
throw format_error("invalid width");
else
return static_cast<int>(value);
},
ctx.arg(width_arg_id));
return format_to(ctx.out(), "{0:{1}}", s.value, width);
}
};
TEST(std_format_test, parsing) {
std::string s = std::format("{0:{1}}", S{42}, 10); // s == " 42"
EXPECT_EQ(s, " 42");
}
#if FMT_USE_INT128
template <> struct std::formatter<__int128_t> : std::formatter<long long> {
auto format(__int128_t n, format_context& ctx) {
// Format as a long long since we only want to check if it is possible to
// specialize formatter for __int128_t.
return formatter<long long>::format(static_cast<long long>(n), ctx);
}
};
TEST(std_format_test, int128) {
__int128_t n = 42;
auto s = std::format("{}", n);
EXPECT_EQ(s, "42");
}
#endif // FMT_USE_INT128

View File

@@ -18,7 +18,7 @@ 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.1251");
auto loc = get_locale("ru_RU.CP1251", "Russian_Russia.1251");
if (loc == std::locale::classic()) return;
auto s = std::string();

View File

@@ -13,8 +13,8 @@
#ifdef FMT_MODULE_TEST
import fmt;
#else
#include "fmt/os.h"
#endif // FMT_MODULE_TEST
# include "fmt/os.h"
#endif // FMT_MODULE_TEST
#ifdef _MSC_VER
# define FMT_VSNPRINTF vsprintf_s

View File

@@ -8,14 +8,18 @@
#include "fmt/xchar.h"
#include <complex>
#include <cwchar>
#include <vector>
#include "fmt/chrono.h"
#include "fmt/color.h"
#include "fmt/ostream.h"
#include "fmt/ranges.h"
#include "gtest/gtest.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 {
@@ -68,8 +72,10 @@ struct explicitly_convertible_to_wstring_view {
};
TEST(xchar_test, format_explicitly_convertible_to_wstring_view) {
EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
// Types explicitly convertible to wstring_view are not formattable by
// default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_wstring_view>::value, "");
}
#endif
@@ -87,6 +93,10 @@ TEST(xchar_test, format) {
EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1));
}
TEST(xchar_test, is_formattable) {
static_assert(!fmt::is_formattable<const wchar_t*>::value, "");
}
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));
@@ -210,6 +220,12 @@ std::wostream& operator<<(std::wostream& os, streamable_enum) {
return os << L"streamable_enum";
}
namespace fmt {
template <>
struct formatter<streamable_enum, wchar_t> : basic_ostream_formatter<wchar_t> {
};
} // namespace fmt
enum unstreamable_enum {};
TEST(xchar_test, enum) {
@@ -228,14 +244,14 @@ namespace fake_qt {
class QString {
public:
QString(const wchar_t* s) : s_(s) {}
const wchar_t* utf16() const FMT_NOEXCEPT { return s_.data(); }
int size() const FMT_NOEXCEPT { return static_cast<int>(s_.size()); }
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) FMT_NOEXCEPT {
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
@@ -257,6 +273,55 @@ TEST(xchar_test, chrono) {
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is 2016-04-25 11:22:33.");
EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42)));
EXPECT_EQ(fmt::format(L"{:%F}", tm), L"2016-04-25");
EXPECT_EQ(fmt::format(L"{:%T}", tm), L"11:22:33");
}
std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr,
std::locale* locptr = nullptr) {
auto loc = locptr ? *locptr : std::locale::classic();
auto& facet = std::use_facet<std::time_put<wchar_t>>(loc);
std::wostringstream os;
os.imbue(loc);
facet.put(os, os, L' ', timeptr, format.c_str(),
format.c_str() + format.size());
#ifdef _WIN32
// Workaround a bug in older versions of Universal CRT.
auto str = os.str();
if (str == L"-0000") str = L"+0000";
return str;
#else
return os.str();
#endif
}
TEST(chrono_test, time_point) {
auto t1 = 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",
L"%EC", L"%G", L"%g", L"%b", L"%h", L"%B", L"%m", L"%Om", L"%U",
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");
#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"});
#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_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));
}
}
TEST(xchar_test, color) {
@@ -405,7 +470,7 @@ template <class charT> struct formatter<std::complex<double>, charT> {
specs_.precision, specs_.precision_ref, ctx);
auto specs = std::string();
if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision);
if (specs_.type) specs += specs_.type;
if (specs_.type == presentation_type::fixed_lower) specs += 'f';
auto real = fmt::format(ctx.locale().template get<std::locale>(),
fmt::runtime("{:" + specs + "}"), c.real());
auto imag = fmt::format(ctx.locale().template get<std::locale>(),
@@ -426,4 +491,20 @@ TEST(locale_test, complex) {
EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)");
}
TEST(locale_test, chrono_weekday) {
auto loc = get_locale("ru_RU.UTF-8", "Russian_Russia.1251");
auto loc_old = std::locale::global(loc);
auto mon = fmt::weekday(1);
EXPECT_EQ(fmt::format(L"{}", mon), L"Mon");
if (loc != std::locale::classic()) {
// {L"\x43F\x43D", L"\x41F\x43D", L"\x43F\x43D\x434", L"\x41F\x43D\x434"}
// {L"пн", L"Пн", L"пнд", L"Пнд"}
EXPECT_THAT(
(std::vector<std::wstring>{L"\x43F\x43D", L"\x41F\x43D",
L"\x43F\x43D\x434", L"\x41F\x43D\x434"}),
Contains(fmt::format(loc, L"{:L}", mon)));
}
std::locale::global(loc_old);
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR