From 19fcec84d8d7d21e796c7624e521b60d28ee21ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:45:59 +0200 Subject: Adding upstream version 16.2.11+ds. Signed-off-by: Daniel Baumann --- src/fmt/test/CMakeLists.txt | 236 + src/fmt/test/add-subdirectory-test/CMakeLists.txt | 17 + src/fmt/test/add-subdirectory-test/main.cc | 6 + src/fmt/test/assert-test.cc | 28 + src/fmt/test/chrono-test.cc | 387 + src/fmt/test/color-test.cc | 86 + src/fmt/test/compile-error-test/CMakeLists.txt | 79 + src/fmt/test/compile-test.cc | 144 + src/fmt/test/core-test.cc | 786 + src/fmt/test/cuda-test/CMakeLists.txt | 73 + src/fmt/test/cuda-test/cpp14.cc | 11 + src/fmt/test/cuda-test/cuda-cpp14.cu | 28 + src/fmt/test/custom-formatter-test.cc | 56 + src/fmt/test/find-package-test/CMakeLists.txt | 17 + src/fmt/test/find-package-test/main.cc | 6 + src/fmt/test/format | 860 + src/fmt/test/format-dyn-args-test.cc | 6 + src/fmt/test/format-impl-test.cc | 456 + src/fmt/test/format-test.cc | 2446 +++ src/fmt/test/fuzzing/.gitignore | 3 + src/fmt/test/fuzzing/CMakeLists.txt | 38 + src/fmt/test/fuzzing/README.md | 48 + src/fmt/test/fuzzing/build.sh | 110 + src/fmt/test/fuzzing/chrono_duration.cpp | 152 + src/fmt/test/fuzzing/fuzzer_common.h | 67 + src/fmt/test/fuzzing/main.cpp | 21 + src/fmt/test/fuzzing/named_arg.cpp | 128 + src/fmt/test/fuzzing/one_arg.cpp | 131 + src/fmt/test/fuzzing/sprintf.cpp | 116 + src/fmt/test/fuzzing/two_args.cpp | 112 + src/fmt/test/gmock-gtest-all.cc | 11244 +++++++++++ src/fmt/test/gmock/gmock.h | 14204 ++++++++++++++ src/fmt/test/grisu-test.cc | 75 + src/fmt/test/gtest-extra-test.cc | 439 + src/fmt/test/gtest-extra.cc | 87 + src/fmt/test/gtest-extra.h | 155 + src/fmt/test/gtest/gtest-spi.h | 232 + src/fmt/test/gtest/gtest.h | 20065 ++++++++++++++++++++ src/fmt/test/header-only-test.cc | 3 + src/fmt/test/header-only-test2.cc | 3 + src/fmt/test/locale-test.cc | 90 + src/fmt/test/mock-allocator.h | 60 + src/fmt/test/os-test.cc | 500 + src/fmt/test/ostream-test.cc | 308 + src/fmt/test/posix-mock-test.cc | 558 + src/fmt/test/posix-mock.h | 77 + src/fmt/test/printf-test.cc | 657 + src/fmt/test/ranges-test.cc | 155 + src/fmt/test/scan-test.cc | 116 + src/fmt/test/scan.h | 237 + src/fmt/test/std-format-test.cc | 157 + src/fmt/test/test-assert.h | 32 + src/fmt/test/test-main.cc | 43 + src/fmt/test/util.cc | 50 + src/fmt/test/util.h | 84 + 55 files changed, 56285 insertions(+) create mode 100644 src/fmt/test/CMakeLists.txt create mode 100644 src/fmt/test/add-subdirectory-test/CMakeLists.txt create mode 100644 src/fmt/test/add-subdirectory-test/main.cc create mode 100644 src/fmt/test/assert-test.cc create mode 100644 src/fmt/test/chrono-test.cc create mode 100644 src/fmt/test/color-test.cc create mode 100644 src/fmt/test/compile-error-test/CMakeLists.txt create mode 100644 src/fmt/test/compile-test.cc create mode 100644 src/fmt/test/core-test.cc create mode 100644 src/fmt/test/cuda-test/CMakeLists.txt create mode 100644 src/fmt/test/cuda-test/cpp14.cc create mode 100644 src/fmt/test/cuda-test/cuda-cpp14.cu create mode 100644 src/fmt/test/custom-formatter-test.cc create mode 100644 src/fmt/test/find-package-test/CMakeLists.txt create mode 100644 src/fmt/test/find-package-test/main.cc create mode 100644 src/fmt/test/format create mode 100644 src/fmt/test/format-dyn-args-test.cc create mode 100644 src/fmt/test/format-impl-test.cc create mode 100644 src/fmt/test/format-test.cc create mode 100644 src/fmt/test/fuzzing/.gitignore create mode 100644 src/fmt/test/fuzzing/CMakeLists.txt create mode 100644 src/fmt/test/fuzzing/README.md create mode 100755 src/fmt/test/fuzzing/build.sh create mode 100644 src/fmt/test/fuzzing/chrono_duration.cpp create mode 100644 src/fmt/test/fuzzing/fuzzer_common.h create mode 100644 src/fmt/test/fuzzing/main.cpp create mode 100644 src/fmt/test/fuzzing/named_arg.cpp create mode 100644 src/fmt/test/fuzzing/one_arg.cpp create mode 100644 src/fmt/test/fuzzing/sprintf.cpp create mode 100644 src/fmt/test/fuzzing/two_args.cpp create mode 100644 src/fmt/test/gmock-gtest-all.cc create mode 100644 src/fmt/test/gmock/gmock.h create mode 100644 src/fmt/test/grisu-test.cc create mode 100644 src/fmt/test/gtest-extra-test.cc create mode 100644 src/fmt/test/gtest-extra.cc create mode 100644 src/fmt/test/gtest-extra.h create mode 100644 src/fmt/test/gtest/gtest-spi.h create mode 100644 src/fmt/test/gtest/gtest.h create mode 100644 src/fmt/test/header-only-test.cc create mode 100644 src/fmt/test/header-only-test2.cc create mode 100644 src/fmt/test/locale-test.cc create mode 100644 src/fmt/test/mock-allocator.h create mode 100644 src/fmt/test/os-test.cc create mode 100644 src/fmt/test/ostream-test.cc create mode 100644 src/fmt/test/posix-mock-test.cc create mode 100644 src/fmt/test/posix-mock.h create mode 100644 src/fmt/test/printf-test.cc create mode 100644 src/fmt/test/ranges-test.cc create mode 100644 src/fmt/test/scan-test.cc create mode 100644 src/fmt/test/scan.h create mode 100644 src/fmt/test/std-format-test.cc create mode 100644 src/fmt/test/test-assert.h create mode 100644 src/fmt/test/test-main.cc create mode 100644 src/fmt/test/util.cc create mode 100644 src/fmt/test/util.h (limited to 'src/fmt/test') diff --git a/src/fmt/test/CMakeLists.txt b/src/fmt/test/CMakeLists.txt new file mode 100644 index 000000000..891766331 --- /dev/null +++ b/src/fmt/test/CMakeLists.txt @@ -0,0 +1,236 @@ +#------------------------------------------------------------------------------ +# Build the google test library + +# We compile Google Test ourselves instead of using pre-compiled libraries. +# See the Google Test FAQ "Why is it not recommended to install a +# pre-compiled copy of Google Test (for example, into /usr/local)?" +# at http://code.google.com/p/googletest/wiki/FAQ for more details. +add_library(gmock STATIC + gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h) +target_compile_definitions(gmock PUBLIC GTEST_HAS_STD_WSTRING=1) +target_include_directories(gmock SYSTEM PUBLIC . gmock gtest) + +find_package(Threads) +if (Threads_FOUND) + target_link_libraries(gmock ${CMAKE_THREAD_LIBS_INIT}) +else () + target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0) +endif () + +target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0) + +if (MSVC) + # Workaround a bug in implementation of variadic templates in MSVC11. + target_compile_definitions(gmock PUBLIC _VARIADIC_MAX=10) + + # Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions. + target_compile_definitions(gmock PRIVATE _CRT_SECURE_NO_WARNINGS) + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Disable MSVC warnings of POSIX functions. + target_compile_options(gmock PUBLIC -Wno-deprecated-declarations) + endif () +endif () + +# GTest doesn't detect with clang. +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_definitions(gmock PUBLIC GTEST_USE_OWN_TR1_TUPLE=1) +endif () + +# Silence MSVC tr1 deprecation warning in gmock. +target_compile_definitions(gmock + PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1) + +#------------------------------------------------------------------------------ +# Build the actual library tests + +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 SYSTEM PUBLIC gtest gmock) +target_link_libraries(test-main gmock 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) + target_link_libraries(${name} -static-libgcc -static-libstdc++) + endif () +endfunction() + +# Adds a test. +# Usage: add_fmt_test(name srcs...) +function(add_fmt_test name) + add_fmt_executable(${name} ${name}.cc ${ARGN}) + target_link_libraries(${name} test-main) + + # Define if certain C++ features can be used. + if (FMT_PEDANTIC) + target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS}) + endif () + if (FMT_WERROR) + target_compile_options(${name} PRIVATE ${WERROR_FLAG}) + endif () + target_include_directories(${name} SYSTEM PUBLIC gtest gmock) + add_test(NAME ${name} COMMAND ${name}) +endfunction() + +add_fmt_test(assert-test) +add_fmt_test(chrono-test) +add_fmt_test(color-test) +add_fmt_test(core-test) +add_fmt_test(grisu-test) +target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1) +add_fmt_test(gtest-extra-test) +add_fmt_test(format-test mock-allocator.h) +if (MSVC) + target_compile_options(format-test PRIVATE /bigobj) +endif () +if (NOT (MSVC AND BUILD_SHARED_LIBS)) + add_fmt_test(format-impl-test) +endif () +add_fmt_test(locale-test) +add_fmt_test(ostream-test) +add_fmt_test(compile-test) +add_fmt_test(printf-test) +add_fmt_test(custom-formatter-test) +add_fmt_test(ranges-test) +add_fmt_test(scan-test) + +if (NOT MSVC_BUILD_STATIC) + add_fmt_executable(posix-mock-test + posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC}) + target_include_directories( + posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include) + target_link_libraries(posix-mock-test gmock) + target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock) + 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 () + +add_fmt_executable(header-only-test + header-only-test.cc header-only-test2.cc test-main.cc) +target_link_libraries(header-only-test gmock) +target_include_directories(header-only-test SYSTEM PUBLIC gtest gmock) +if (TARGET fmt-header-only) + target_link_libraries(header-only-test fmt-header-only) +else () + target_include_directories( + header-only-test PRIVATE ${PROJECT_SOURCE_DIR}/include) + target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1) +endif () + +message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}") + +if (FMT_PEDANTIC) + # 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 () + + # 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) + target_include_directories( + noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include) + target_compile_options(noexception-test PRIVATE -fno-exceptions) + if (FMT_PEDANTIC) + target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) + endif () + endif () + + # Test that the library compiles without locale. + add_library(nolocale-test ../src/format.cc) + target_include_directories( + nolocale-test PRIVATE ${PROJECT_SOURCE_DIR}/include) + target_compile_definitions( + nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1) + + add_test(compile-error-test ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test" + "${CMAKE_CURRENT_BINARY_DIR}/compile-error-test" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-options + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}" + "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" + "-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} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/find-package-test" + "${CMAKE_CURRENT_BINARY_DIR}/find-package-test" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-options + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-DFMT_DIR=${PROJECT_BINARY_DIR}" + "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") + + # Test if the targets are found when add_subdirectory is used. + add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND} + -C ${CMAKE_BUILD_TYPE} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/add-subdirectory-test" + "${CMAKE_CURRENT_BINARY_DIR}/add-subdirectory-test" + --build-generator ${CMAKE_GENERATOR} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-options + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") +endif () + +# Activate optional CUDA tests if CUDA is found. For version selection see +# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features +if (FMT_CUDA_TEST) + if (${CMAKE_VERSION} VERSION_LESS 3.15) + find_package(CUDA 9.0) + else () + include(CheckLanguage) + check_language(CUDA) + if (CMAKE_CUDA_COMPILER) + enable_language(CUDA OPTIONAL) + set(CUDA_FOUND TRUE) + endif () + endif () + + if (CUDA_FOUND) + add_subdirectory(cuda-test) + add_test(NAME cuda-test COMMAND fmt-in-cuda-test) + endif () +endif () diff --git a/src/fmt/test/add-subdirectory-test/CMakeLists.txt b/src/fmt/test/add-subdirectory-test/CMakeLists.txt new file mode 100644 index 000000000..db7054bd8 --- /dev/null +++ b/src/fmt/test/add-subdirectory-test/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.1.0) + +project(fmt-test) + +add_subdirectory(../.. fmt) + +add_executable(library-test "main.cc") +target_link_libraries(library-test fmt::fmt) +target_compile_options(library-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) +target_include_directories(library-test PUBLIC SYSTEM .) + +if (TARGET fmt::fmt-header-only) + add_executable(header-only-test "main.cc") + target_link_libraries(header-only-test fmt::fmt-header-only) + target_compile_options(header-only-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) + target_include_directories(header-only-test PUBLIC SYSTEM .) +endif () diff --git a/src/fmt/test/add-subdirectory-test/main.cc b/src/fmt/test/add-subdirectory-test/main.cc new file mode 100644 index 000000000..f39f377cc --- /dev/null +++ b/src/fmt/test/add-subdirectory-test/main.cc @@ -0,0 +1,6 @@ +#include "fmt/format.h" + +int main(int argc, char** argv) { + for(int i = 0; i < argc; ++i) + fmt::print("{}: {}\n", i, argv[i]); +} diff --git a/src/fmt/test/assert-test.cc b/src/fmt/test/assert-test.cc new file mode 100644 index 000000000..bc728b53e --- /dev/null +++ b/src/fmt/test/assert-test.cc @@ -0,0 +1,28 @@ +// Formatting library for C++ - assertion tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/core.h" +#include "gtest.h" + +TEST(AssertTest, Fail) { +#if GTEST_HAS_DEATH_TEST + EXPECT_DEBUG_DEATH(FMT_ASSERT(false, "don't panic!"), "don't panic!"); +#else + fmt::print("warning: death tests are not supported\n"); +#endif +} + +bool test_condition = false; + +TEST(AssertTest, DanglingElse) { + bool executed_else = false; + if (test_condition) + FMT_ASSERT(true, ""); + else + executed_else = true; + EXPECT_TRUE(executed_else); +} diff --git a/src/fmt/test/chrono-test.cc b/src/fmt/test/chrono-test.cc new file mode 100644 index 000000000..b876c151a --- /dev/null +++ b/src/fmt/test/chrono-test.cc @@ -0,0 +1,387 @@ +// Formatting library for C++ - time formatting tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifdef WIN32 +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include "fmt/chrono.h" + +#include + +#include "gtest-extra.h" + +std::tm make_tm() { + auto time = std::tm(); + time.tm_mday = 1; + return time; +} + +std::tm make_hour(int h) { + auto time = make_tm(); + time.tm_hour = h; + return time; +} + +std::tm make_minute(int m) { + auto time = make_tm(); + time.tm_min = m; + return time; +} + +std::tm make_second(int s) { + auto time = make_tm(); + time.tm_sec = s; + return time; +} + +std::string format_tm(const std::tm& time, const char* spec, + const std::locale& loc) { + auto& facet = std::use_facet>(loc); + std::ostringstream os; + os.imbue(loc); + facet.put(os, os, ' ', &time, spec, spec + std::strlen(spec)); + return os.str(); +} + +TEST(TimeTest, Format) { + std::tm tm = std::tm(); + tm.tm_year = 116; + tm.tm_mon = 3; + tm.tm_mday = 25; + EXPECT_EQ("The date is 2016-04-25.", + fmt::format("The date is {:%Y-%m-%d}.", tm)); +} + +TEST(TimeTest, GrowBuffer) { + std::string s = "{:"; + for (int i = 0; i < 30; ++i) s += "%c"; + s += "}\n"; + std::time_t t = std::time(nullptr); + fmt::format(s, *std::localtime(&t)); +} + +TEST(TimeTest, FormatToEmptyContainer) { + std::string s; + auto time = std::tm(); + time.tm_sec = 42; + fmt::format_to(std::back_inserter(s), "{:%S}", time); + EXPECT_EQ(s, "42"); +} + +TEST(TimeTest, EmptyResult) { EXPECT_EQ("", fmt::format("{}", std::tm())); } + +static bool EqualTime(const std::tm& lhs, const std::tm& rhs) { + return lhs.tm_sec == rhs.tm_sec && lhs.tm_min == rhs.tm_min && + lhs.tm_hour == rhs.tm_hour && lhs.tm_mday == rhs.tm_mday && + lhs.tm_mon == rhs.tm_mon && lhs.tm_year == rhs.tm_year && + lhs.tm_wday == rhs.tm_wday && lhs.tm_yday == rhs.tm_yday && + lhs.tm_isdst == rhs.tm_isdst; +} + +TEST(TimeTest, LocalTime) { + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + EXPECT_TRUE(EqualTime(tm, fmt::localtime(t))); +} + +TEST(TimeTest, GMTime) { + std::time_t t = std::time(nullptr); + std::tm tm = *std::gmtime(&t); + EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t))); +} + +#define EXPECT_TIME(spec, time, duration) \ + { \ + std::locale loc("ja_JP.utf8"); \ + EXPECT_EQ(format_tm(time, spec, loc), \ + fmt::format(loc, "{:" spec "}", duration)); \ + } + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR + +TEST(ChronoTest, FormatDefault) { + EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42))); + EXPECT_EQ("42as", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42fs", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42ps", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42ns", fmt::format("{}", std::chrono::nanoseconds(42))); + EXPECT_EQ("42µs", fmt::format("{}", std::chrono::microseconds(42))); + EXPECT_EQ("42ms", fmt::format("{}", std::chrono::milliseconds(42))); + EXPECT_EQ("42cs", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42ds", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42))); + EXPECT_EQ("42das", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42hs", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42ks", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42Ms", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42Gs", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42Ts", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42Ps", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42Es", + fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42))); + EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42))); + EXPECT_EQ( + "42[15]s", + fmt::format("{}", std::chrono::duration>(42))); + EXPECT_EQ( + "42[15/4]s", + fmt::format("{}", std::chrono::duration>(42))); +} + +TEST(ChronoTest, FormatWide) { + EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); + EXPECT_EQ(L"42as", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42fs", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42ps", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42ns", fmt::format(L"{}", std::chrono::nanoseconds(42))); + EXPECT_EQ(L"42\u00B5s", fmt::format(L"{}", std::chrono::microseconds(42))); + EXPECT_EQ(L"42ms", fmt::format(L"{}", std::chrono::milliseconds(42))); + EXPECT_EQ(L"42cs", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42ds", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); + EXPECT_EQ(L"42das", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42hs", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42ks", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Ms", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Gs", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Ts", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Ps", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42Es", + fmt::format(L"{}", std::chrono::duration(42))); + EXPECT_EQ(L"42m", fmt::format(L"{}", std::chrono::minutes(42))); + EXPECT_EQ(L"42h", fmt::format(L"{}", std::chrono::hours(42))); + EXPECT_EQ( + L"42[15]s", + fmt::format(L"{}", std::chrono::duration>(42))); + EXPECT_EQ( + L"42[15/4]s", + fmt::format(L"{}", std::chrono::duration>(42))); +} + +TEST(ChronoTest, Align) { + auto s = std::chrono::seconds(42); + EXPECT_EQ("42s ", fmt::format("{:5}", s)); + EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5)); + EXPECT_EQ(" 42s", fmt::format("{:>5}", s)); + EXPECT_EQ("**42s**", fmt::format("{:*^7}", s)); + EXPECT_EQ("03:25:45 ", + fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345))); + EXPECT_EQ(" 03:25:45", + fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345))); + EXPECT_EQ("~~03:25:45~~", + fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345))); + EXPECT_EQ("03:25:45 ", + fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12)); +} + +TEST(ChronoTest, FormatSpecs) { + EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0))); + EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0))); + EXPECT_EQ("\t", fmt::format("{:%t}", std::chrono::seconds(0))); + EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(0))); + EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(60))); + EXPECT_EQ("42", fmt::format("{:%S}", std::chrono::seconds(42))); + EXPECT_EQ("01.234", fmt::format("{:%S}", std::chrono::milliseconds(1234))); + EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(0))); + EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(60))); + EXPECT_EQ("42", fmt::format("{:%M}", std::chrono::minutes(42))); + EXPECT_EQ("01", fmt::format("{:%M}", std::chrono::seconds(61))); + EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(0))); + EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(24))); + EXPECT_EQ("14", fmt::format("{:%H}", std::chrono::hours(14))); + EXPECT_EQ("01", fmt::format("{:%H}", std::chrono::minutes(61))); + EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(0))); + EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(12))); + EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24))); + EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4))); + EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14))); + EXPECT_EQ("03:25:45", + fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345))); + EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345))); + EXPECT_EQ("03:25:45", fmt::format("{:%T}", std::chrono::seconds(12345))); + EXPECT_EQ("12345", fmt::format("{:%Q}", std::chrono::seconds(12345))); + EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345))); +} + +TEST(ChronoTest, InvalidSpecs) { + auto sec = std::chrono::seconds(0); + EXPECT_THROW_MSG(fmt::format("{:%a}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%A}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%c}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%x}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%Ex}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%X}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%EX}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%D}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%F}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%Ec}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%w}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%u}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%b}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%B}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%z}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%Z}", sec), fmt::format_error, "no date"); + EXPECT_THROW_MSG(fmt::format("{:%Eq}", sec), fmt::format_error, + "invalid format"); + EXPECT_THROW_MSG(fmt::format("{:%Oq}", sec), fmt::format_error, + "invalid format"); +} + +TEST(ChronoTest, Locale) { + const char* loc_name = "ja_JP.utf8"; + bool has_locale = false; + std::locale loc; + try { + loc = std::locale(loc_name); + has_locale = true; + } catch (const std::runtime_error&) { + } + if (!has_locale) { + fmt::print("{} locale is missing.\n", loc_name); + return; + } + EXPECT_TIME("%OH", make_hour(14), std::chrono::hours(14)); + EXPECT_TIME("%OI", make_hour(14), std::chrono::hours(14)); + EXPECT_TIME("%OM", make_minute(42), std::chrono::minutes(42)); + EXPECT_TIME("%OS", make_second(42), std::chrono::seconds(42)); + auto time = make_tm(); + time.tm_hour = 3; + time.tm_min = 25; + time.tm_sec = 45; + auto sec = std::chrono::seconds(12345); + EXPECT_TIME("%r", time, sec); + EXPECT_TIME("%p", time, sec); +} + +typedef std::chrono::duration dms; + +TEST(ChronoTest, FormatDefaultFP) { + typedef std::chrono::duration fs; + EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234))); + typedef std::chrono::duration fms; + EXPECT_EQ("1.234ms", fmt::format("{}", fms(1.234))); + typedef std::chrono::duration ds; + EXPECT_EQ("1.234s", fmt::format("{}", ds(1.234))); + EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234))); +} + +TEST(ChronoTest, FormatPrecision) { + EXPECT_THROW_MSG(fmt::format("{:.2}", std::chrono::seconds(42)), + fmt::format_error, + "precision not allowed for this argument type"); + EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234))); + EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2)); +} + +TEST(ChronoTest, FormatFullSpecs) { + 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))); +} + +TEST(ChronoTest, FormatSimpleQq) { + typedef std::chrono::duration fs; + EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234))); + typedef std::chrono::duration fms; + EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", fms(1.234))); + typedef std::chrono::duration ds; + EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", ds(1.234))); + EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234))); +} + +TEST(ChronoTest, FormatPrecisionQq) { + EXPECT_THROW_MSG(fmt::format("{:.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(ChronoTest, FormatFullSpecsQq) { + 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))); +} + +TEST(ChronoTest, InvalidWidthId) { + EXPECT_THROW(fmt::format("{:{o}", std::chrono::seconds(0)), + fmt::format_error); +} + +TEST(ChronoTest, InvalidColons) { + EXPECT_THROW(fmt::format("{0}=:{0::", std::chrono::seconds(0)), + fmt::format_error); +} + +TEST(ChronoTest, NegativeDurations) { + EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345))); + EXPECT_EQ("-03:25:45", + fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345))); + EXPECT_EQ("-00:01", + fmt::format("{:%M:%S}", std::chrono::duration(-1))); + EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345))); + EXPECT_EQ("-00.127", + fmt::format("{:%S}", + std::chrono::duration{-127})); + auto min = std::numeric_limits::min(); + EXPECT_EQ(fmt::format("{}", min), + fmt::format("{:%Q}", std::chrono::duration(min))); +} + +TEST(ChronoTest, SpecialDurations) { + EXPECT_EQ( + "40.", + fmt::format("{:%S}", std::chrono::duration(1e20)).substr(0, 3)); + auto nan = std::numeric_limits::quiet_NaN(); + EXPECT_EQ( + "nan nan nan nan nan:nan nan", + fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration(nan))); + fmt::format("{:%S}", + std::chrono::duration(1.79400457e+31f)); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(1)), + "1Es"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(1)), + "1as"); + EXPECT_EQ(fmt::format("{:%R}", std::chrono::duration{2}), + "03:33"); + EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration{2}), + "03:33:20"); +} + +#endif // FMT_STATIC_THOUSANDS_SEPARATOR diff --git a/src/fmt/test/color-test.cc b/src/fmt/test/color-test.cc new file mode 100644 index 000000000..454a0660d --- /dev/null +++ b/src/fmt/test/color-test.cc @@ -0,0 +1,86 @@ +// Formatting library for C++ - color tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/color.h" + +#include "gtest-extra.h" + +TEST(ColorsTest, ColorsPrint) { + EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fg(fmt::color::blue), "blue"), + "\x1b[38;2;000;000;255mblue\x1b[0m"); + EXPECT_WRITE( + stdout, + fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), "two color"), + "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::bold, "bold"), + "\x1b[1mbold\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::italic, "italic"), + "\x1b[3mitalic\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::underline, "underline"), + "\x1b[4munderline\x1b[0m"); + EXPECT_WRITE(stdout, + fmt::print(fmt::emphasis::strikethrough, "strikethrough"), + "\x1b[9mstrikethrough\x1b[0m"); + EXPECT_WRITE( + stdout, + fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); + EXPECT_WRITE(stderr, fmt::print(stderr, fmt::emphasis::bold, "bold error"), + "\x1b[1mbold error\x1b[0m"); + EXPECT_WRITE(stderr, fmt::print(stderr, fg(fmt::color::blue), "blue log"), + "\x1b[38;2;000;000;255mblue log\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), "hi"), "hi"); + EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), "tred"), + "\x1b[31mtred\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(bg(fmt::terminal_color::cyan), "tcyan"), + "\x1b[46mtcyan\x1b[0m"); + EXPECT_WRITE(stdout, + fmt::print(fg(fmt::terminal_color::bright_green), "tbgreen"), + "\x1b[92mtbgreen\x1b[0m"); + EXPECT_WRITE(stdout, + fmt::print(bg(fmt::terminal_color::bright_magenta), "tbmagenta"), + "\x1b[105mtbmagenta\x1b[0m"); +} + +TEST(ColorsTest, Format) { + EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"), + L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"), + "\x1b[38;2;000;000;255mblue\x1b[0m"); + EXPECT_EQ( + fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), "two color"), + "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold"), "\x1b[1mbold\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::italic, "italic"), + "\x1b[3mitalic\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::underline, "underline"), + "\x1b[4munderline\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::strikethrough, "strikethrough"), + "\x1b[9mstrikethrough\x1b[0m"); + EXPECT_EQ( + fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"), + "\x1b[1mbold error\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue log"), + "\x1b[38;2;000;000;255mblue log\x1b[0m"); + EXPECT_EQ(fmt::format(fmt::text_style(), "hi"), "hi"); + EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "tred"), + "\x1b[31mtred\x1b[0m"); + EXPECT_EQ(fmt::format(bg(fmt::terminal_color::cyan), "tcyan"), + "\x1b[46mtcyan\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::terminal_color::bright_green), "tbgreen"), + "\x1b[92mtbgreen\x1b[0m"); + EXPECT_EQ(fmt::format(bg(fmt::terminal_color::bright_magenta), "tbmagenta"), + "\x1b[105mtbmagenta\x1b[0m"); + EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"), + "\x1b[31mfoo\x1b[0m"); +} diff --git a/src/fmt/test/compile-error-test/CMakeLists.txt b/src/fmt/test/compile-error-test/CMakeLists.txt new file mode 100644 index 000000000..75a0c5a5e --- /dev/null +++ b/src/fmt/test/compile-error-test/CMakeLists.txt @@ -0,0 +1,79 @@ +# Test if compile errors are produced where necessary. + +cmake_minimum_required(VERSION 3.1.0) + +include(CheckCXXSourceCompiles) +include(CheckCXXCompilerFlag) + +set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../../include) +set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG} ${PEDANTIC_COMPILE_FLAGS}) + +function (generate_source result fragment) + set(${result} " + #define FMT_HEADER_ONLY 1 + #include \"fmt/format.h\" + int main() { + ${fragment} + } + " PARENT_SCOPE) +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}) + 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("") + +# Formatting a wide character with a narrow format string is forbidden. +expect_compile_error("fmt::format(\"{}\", L'a');") + +# Formatting a wide string with a narrow format string is forbidden. +expect_compile_error("fmt::format(\"{}\", L\"foo\");") + +# 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\");") + +# Formatting a wide string with a narrow format string is forbidden. +expect_compile_error(" + struct S { + operator std::string() const { return std::string(); } + }; + fmt::format(\"{}\", S()); +") + +# Make sure that compiler features detected in the header +# match the features detected in CMake. +if (SUPPORTS_USER_DEFINED_LITERALS) + set(supports_udl 1) +else () + set(supports_udl 0) +endif () +expect_compile("#if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl} + # error + #endif") diff --git a/src/fmt/test/compile-test.cc b/src/fmt/test/compile-test.cc new file mode 100644 index 000000000..c93a24677 --- /dev/null +++ b/src/fmt/test/compile-test.cc @@ -0,0 +1,144 @@ +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Check if fmt/compile.h compiles with windows.h included before it. +#ifdef _WIN32 +# include +#endif + +#include "fmt/compile.h" +#include "gmock.h" +#include "gtest-extra.h" +#include "mock-allocator.h" +#include "util.h" + +#undef ERROR +#undef min +#undef max + +using testing::Return; +using testing::StrictMock; + +// compiletime_prepared_parts_type_provider is useful only with relaxed +// constexpr. +#if FMT_USE_CONSTEXPR +template +void check_prepared_parts_type(Format format) { + typedef fmt::detail::compiled_format_base provider; + typedef fmt::detail::format_part + expected_parts_type[EXPECTED_PARTS_COUNT]; + static_assert(std::is_same::value, + "CompileTimePreparedPartsTypeProvider test failed"); +} + +TEST(CompileTest, CompileTimePreparedPartsTypeProvider) { + check_prepared_parts_type<1u>(FMT_STRING("text")); + check_prepared_parts_type<1u>(FMT_STRING("{}")); + check_prepared_parts_type<2u>(FMT_STRING("text{}")); + check_prepared_parts_type<2u>(FMT_STRING("{}text")); + check_prepared_parts_type<3u>(FMT_STRING("text{}text")); + check_prepared_parts_type<3u>(FMT_STRING("{:{}.{}} {:{}}")); + + check_prepared_parts_type<3u>(FMT_STRING("{{{}}}")); // '{', 'argument', '}' + check_prepared_parts_type<2u>(FMT_STRING("text{{")); // 'text', '{' + check_prepared_parts_type<3u>(FMT_STRING("text{{ ")); // 'text', '{', ' ' + check_prepared_parts_type<2u>(FMT_STRING("}}text")); // '}', text + check_prepared_parts_type<2u>(FMT_STRING("text}}text")); // 'text}', 'text' + check_prepared_parts_type<4u>( + FMT_STRING("text{{}}text")); // 'text', '{', '}', 'text' +} +#endif + +TEST(CompileTest, PassStringLiteralFormat) { + const auto prepared = fmt::compile("test {}"); + EXPECT_EQ("test 42", fmt::format(prepared, 42)); + const auto wprepared = fmt::compile(L"test {}"); + EXPECT_EQ(L"test 42", fmt::format(wprepared, 42)); +} + +#if FMT_USE_CONSTEXPR +TEST(CompileTest, PassCompileString) { + const auto prepared = fmt::compile(FMT_STRING("test {}")); + EXPECT_EQ("test 42", fmt::format(prepared, 42)); + const auto wprepared = fmt::compile(FMT_STRING(L"test {}")); + EXPECT_EQ(L"test 42", fmt::format(wprepared, 42)); +} +#endif + +TEST(CompileTest, FormatToArrayOfChars) { + char buffer[32] = {0}; + const auto prepared = fmt::compile("4{}"); + fmt::format_to(fmt::detail::make_checked(buffer, 32), prepared, 2); + EXPECT_EQ(std::string("42"), buffer); + wchar_t wbuffer[32] = {0}; + const auto wprepared = fmt::compile(L"4{}"); + fmt::format_to(fmt::detail::make_checked(wbuffer, 32), wprepared, 2); + EXPECT_EQ(std::wstring(L"42"), wbuffer); +} + +TEST(CompileTest, FormatToIterator) { + std::string s(2, ' '); + const auto prepared = fmt::compile("4{}"); + fmt::format_to(s.begin(), prepared, 2); + EXPECT_EQ("42", s); + std::wstring ws(2, L' '); + const auto wprepared = fmt::compile(L"4{}"); + fmt::format_to(ws.begin(), wprepared, 2); + EXPECT_EQ(L"42", ws); +} + +TEST(CompileTest, FormatToN) { + char buf[5]; + auto f = fmt::compile("{:10}"); + auto result = fmt::format_to_n(buf, 5, f, 42); + EXPECT_EQ(result.size, 10); + EXPECT_EQ(result.out, buf + 5); + EXPECT_EQ(fmt::string_view(buf, 5), " "); +} + +TEST(CompileTest, FormattedSize) { + auto f = fmt::compile("{:10}"); + EXPECT_EQ(fmt::formatted_size(f, 42), 10); +} + +TEST(CompileTest, MultipleTypes) { + auto f = fmt::compile("{} {}"); + EXPECT_EQ(fmt::format(f, 42, 42), "42 42"); +} + +struct formattable {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter : formatter { + auto format(formattable, format_context& ctx) -> decltype(ctx.out()) { + return formatter::format("foo", ctx); + } +}; +FMT_END_NAMESPACE + +TEST(CompileTest, FormatUserDefinedType) { + auto f = fmt::compile("{}"); + EXPECT_EQ(fmt::format(f, formattable()), "foo"); +} + +TEST(CompileTest, EmptyFormatString) { + auto f = fmt::compile<>(""); + EXPECT_EQ(fmt::format(f), ""); +} diff --git a/src/fmt/test/core-test.cc b/src/fmt/test/core-test.cc new file mode 100644 index 000000000..8a1ea83b2 --- /dev/null +++ b/src/fmt/test/core-test.cc @@ -0,0 +1,786 @@ +// Formatting library for C++ - core tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock.h" +#include "test-assert.h" + +// Check if fmt/core.h compiles with windows.h included before it. +#ifdef _WIN32 +# include +#endif + +#include "fmt/core.h" + +#undef min +#undef max + +using fmt::basic_format_arg; +using fmt::string_view; +using fmt::detail::buffer; +using fmt::detail::value; + +using testing::_; +using testing::StrictMock; + +namespace { + +struct test_struct {}; + +template +basic_format_arg make_arg(const T& value) { + return fmt::detail::make_arg(value); +} +} // namespace + +FMT_BEGIN_NAMESPACE +template struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + typedef std::back_insert_iterator> iterator; + + auto format(test_struct, basic_format_context& ctx) + -> decltype(ctx.out()) { + const Char* test = "test"; + return std::copy_n(test, std::strlen(test), ctx.out()); + } +}; +FMT_END_NAMESPACE + +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470 +TEST(BufferTest, Noncopyable) { + EXPECT_FALSE(std::is_copy_constructible>::value); +# if !FMT_MSC_VER + // std::is_copy_assignable is broken in MSVC2013. + EXPECT_FALSE(std::is_copy_assignable>::value); +# endif +} + +TEST(BufferTest, Nonmoveable) { + EXPECT_FALSE(std::is_move_constructible>::value); +# if !FMT_MSC_VER + // std::is_move_assignable is broken in MSVC2013. + EXPECT_FALSE(std::is_move_assignable>::value); +# endif +} +#endif + +// A test buffer with a dummy grow method. +template struct test_buffer : buffer { + void grow(size_t capacity) { this->set(nullptr, capacity); } +}; + +template struct mock_buffer : buffer { + MOCK_METHOD1(do_grow, void(size_t capacity)); + + void grow(size_t capacity) { + this->set(this->data(), capacity); + do_grow(capacity); + } + + mock_buffer() {} + mock_buffer(T* data) { this->set(data, 0); } + mock_buffer(T* data, size_t capacity) { this->set(data, capacity); } +}; + +TEST(BufferTest, Ctor) { + { + mock_buffer buffer; + EXPECT_EQ(nullptr, buffer.data()); + EXPECT_EQ(static_cast(0), buffer.size()); + EXPECT_EQ(static_cast(0), buffer.capacity()); + } + { + int dummy; + mock_buffer buffer(&dummy); + EXPECT_EQ(&dummy, &buffer[0]); + EXPECT_EQ(static_cast(0), buffer.size()); + EXPECT_EQ(static_cast(0), buffer.capacity()); + } + { + int dummy; + size_t capacity = std::numeric_limits::max(); + mock_buffer buffer(&dummy, capacity); + EXPECT_EQ(&dummy, &buffer[0]); + EXPECT_EQ(static_cast(0), buffer.size()); + EXPECT_EQ(capacity, buffer.capacity()); + } +} + +struct dying_buffer : test_buffer { + MOCK_METHOD0(die, void()); + ~dying_buffer() { die(); } + + private: + virtual void avoid_weak_vtable(); +}; + +void dying_buffer::avoid_weak_vtable() {} + +TEST(BufferTest, VirtualDtor) { + typedef StrictMock stict_mock_buffer; + stict_mock_buffer* mock_buffer = new stict_mock_buffer(); + EXPECT_CALL(*mock_buffer, die()); + buffer* buffer = mock_buffer; + delete buffer; +} + +TEST(BufferTest, Access) { + char data[10]; + mock_buffer buffer(data, sizeof(data)); + buffer[0] = 11; + EXPECT_EQ(11, buffer[0]); + buffer[3] = 42; + EXPECT_EQ(42, *(&buffer[0] + 3)); + const fmt::detail::buffer& const_buffer = buffer; + EXPECT_EQ(42, const_buffer[3]); +} + +TEST(BufferTest, Resize) { + char data[123]; + mock_buffer buffer(data, sizeof(data)); + buffer[10] = 42; + EXPECT_EQ(42, buffer[10]); + buffer.resize(20); + EXPECT_EQ(20u, buffer.size()); + EXPECT_EQ(123u, buffer.capacity()); + EXPECT_EQ(42, buffer[10]); + buffer.resize(5); + EXPECT_EQ(5u, buffer.size()); + EXPECT_EQ(123u, buffer.capacity()); + EXPECT_EQ(42, buffer[10]); + // Check if resize calls grow. + EXPECT_CALL(buffer, do_grow(124)); + buffer.resize(124); + EXPECT_CALL(buffer, do_grow(200)); + buffer.resize(200); +} + +TEST(BufferTest, Clear) { + test_buffer buffer; + buffer.resize(20); + buffer.resize(0); + EXPECT_EQ(static_cast(0), buffer.size()); + EXPECT_EQ(20u, buffer.capacity()); +} + +TEST(BufferTest, Append) { + char data[15]; + mock_buffer buffer(data, 10); + const char* test = "test"; + buffer.append(test, test + 5); + EXPECT_STREQ(test, &buffer[0]); + EXPECT_EQ(5u, buffer.size()); + buffer.resize(10); + EXPECT_CALL(buffer, do_grow(12)); + buffer.append(test, test + 2); + EXPECT_EQ('t', buffer[10]); + EXPECT_EQ('e', buffer[11]); + EXPECT_EQ(12u, buffer.size()); +} + +TEST(BufferTest, AppendAllocatesEnoughStorage) { + char data[19]; + mock_buffer buffer(data, 10); + const char* test = "abcdefgh"; + buffer.resize(10); + EXPECT_CALL(buffer, do_grow(19)); + buffer.append(test, test + 9); +} + +TEST(ArgTest, FormatArgs) { + fmt::format_args args; + EXPECT_FALSE(args.get(1)); +} + +struct custom_context { + using char_type = char; + using parse_context_type = fmt::format_parse_context; + + template struct formatter_type { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + const char* format(const T&, custom_context& ctx) { + ctx.called = true; + return nullptr; + } + }; + + bool called; + fmt::format_parse_context ctx; + + fmt::format_parse_context& parse_context() { return ctx; } + void advance_to(const char*) {} +}; + +TEST(ArgTest, MakeValueWithCustomContext) { + test_struct t; + fmt::detail::value arg( + fmt::detail::arg_mapper().map(t)); + custom_context ctx = {false, fmt::format_parse_context("")}; + arg.custom.format(&t, ctx.parse_context(), ctx); + EXPECT_TRUE(ctx.called); +} + +FMT_BEGIN_NAMESPACE +namespace detail { +template +bool operator==(custom_value lhs, custom_value rhs) { + return lhs.value == rhs.value; +} +} // namespace detail +FMT_END_NAMESPACE + +// Use a unique result type to make sure that there are no undesirable +// conversions. +struct test_result {}; + +template struct mock_visitor { + template struct result { typedef test_result type; }; + + mock_visitor() { + ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result())); + } + + MOCK_METHOD1_T(visit, test_result(T value)); + MOCK_METHOD0_T(unexpected, void()); + + test_result operator()(T value) { return visit(value); } + + template test_result operator()(U) { + unexpected(); + return test_result(); + } +}; + +template struct visit_type { typedef T Type; }; + +#define VISIT_TYPE(Type_, visit_type_) \ + template <> struct visit_type { typedef visit_type_ Type; } + +VISIT_TYPE(signed char, int); +VISIT_TYPE(unsigned char, unsigned); +VISIT_TYPE(short, int); +VISIT_TYPE(unsigned short, unsigned); + +#if LONG_MAX == INT_MAX +VISIT_TYPE(long, int); +VISIT_TYPE(unsigned long, unsigned); +#else +VISIT_TYPE(long, long long); +VISIT_TYPE(unsigned long, unsigned long long); +#endif + +#define CHECK_ARG_(Char, expected, value) \ + { \ + testing::StrictMock> visitor; \ + EXPECT_CALL(visitor, visit(expected)); \ + typedef std::back_insert_iterator> iterator; \ + fmt::visit_format_arg( \ + visitor, make_arg>(value)); \ + } + +#define CHECK_ARG(value, typename_) \ + { \ + typedef decltype(value) value_type; \ + typename_ visit_type::Type expected = value; \ + CHECK_ARG_(char, expected, value) \ + CHECK_ARG_(wchar_t, expected, value) \ + } + +template class NumericArgTest : public testing::Test {}; + +typedef ::testing::Types + Types; +TYPED_TEST_CASE(NumericArgTest, Types); + +template +typename std::enable_if::value, T>::type test_value() { + return static_cast(42); +} + +template +typename std::enable_if::value, T>::type +test_value() { + return static_cast(4.2); +} + +TYPED_TEST(NumericArgTest, MakeAndVisit) { + CHECK_ARG(test_value(), typename); + CHECK_ARG(std::numeric_limits::min(), typename); + CHECK_ARG(std::numeric_limits::max(), typename); +} + +TEST(ArgTest, CharArg) { + CHECK_ARG_(char, 'a', 'a'); + CHECK_ARG_(wchar_t, L'a', 'a'); + CHECK_ARG_(wchar_t, L'a', L'a'); +} + +TEST(ArgTest, StringArg) { + char str_data[] = "test"; + char* str = str_data; + const char* cstr = str; + CHECK_ARG_(char, cstr, str); + + string_view sref(str); + CHECK_ARG_(char, sref, std::string(str)); +} + +TEST(ArgTest, WStringArg) { + wchar_t str_data[] = L"test"; + wchar_t* str = str_data; + const wchar_t* cstr = str; + + fmt::wstring_view sref(str); + CHECK_ARG_(wchar_t, cstr, str); + CHECK_ARG_(wchar_t, cstr, cstr); + CHECK_ARG_(wchar_t, sref, std::wstring(str)); + CHECK_ARG_(wchar_t, sref, fmt::wstring_view(str)); +} + +TEST(ArgTest, PointerArg) { + void* p = nullptr; + const void* cp = nullptr; + CHECK_ARG_(char, cp, p); + CHECK_ARG_(wchar_t, cp, p); + CHECK_ARG(cp, ); +} + +struct check_custom { + test_result operator()( + fmt::basic_format_arg::handle h) const { + struct test_buffer : fmt::detail::buffer { + char data[10]; + test_buffer() : fmt::detail::buffer(data, 0, 10) {} + void grow(size_t) {} + } buffer; + fmt::detail::buffer& base = buffer; + fmt::format_parse_context parse_ctx(""); + fmt::format_context ctx(std::back_inserter(base), fmt::format_args()); + h.format(parse_ctx, ctx); + EXPECT_EQ("test", std::string(buffer.data, buffer.size())); + return test_result(); + } +}; + +TEST(ArgTest, CustomArg) { + test_struct test; + typedef mock_visitor::handle> + visitor; + testing::StrictMock v; + EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom())); + fmt::visit_format_arg(v, make_arg(test)); +} + +TEST(ArgTest, VisitInvalidArg) { + testing::StrictMock> visitor; + EXPECT_CALL(visitor, visit(_)); + fmt::basic_format_arg arg; + fmt::visit_format_arg(visitor, arg); +} + +TEST(FormatDynArgsTest, Basic) { + fmt::dynamic_format_arg_store store; + store.push_back(42); + store.push_back("abc1"); + store.push_back(1.5f); + + std::string result = fmt::vformat("{} and {} and {}", store); + EXPECT_EQ("42 and abc1 and 1.5", result); +} + +TEST(FormatDynArgsTest, StringsAndRefs) { + // Unfortunately the tests are compiled with old ABI so strings use COW. + fmt::dynamic_format_arg_store store; + char str[] = "1234567890"; + store.push_back(str); + store.push_back(std::cref(str)); + store.push_back(fmt::string_view{str}); + str[0] = 'X'; + + std::string result = fmt::vformat("{} and {} and {}", store); + EXPECT_EQ("1234567890 and X234567890 and X234567890", result); +} + +struct custom_type { + int i = 0; +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter { + auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const custom_type& p, FormatContext& ctx) -> decltype(ctx.out()) { + return format_to(ctx.out(), "cust={}", p.i); + } +}; +FMT_END_NAMESPACE + +TEST(FormatDynArgsTest, CustomFormat) { + fmt::dynamic_format_arg_store store; + custom_type c{}; + store.push_back(c); + ++c.i; + store.push_back(c); + ++c.i; + store.push_back(std::cref(c)); + ++c.i; + + std::string result = fmt::vformat("{} and {} and {}", store); + EXPECT_EQ("cust=0 and cust=1 and cust=3", result); +} + +TEST(FormatDynArgsTest, NamedInt) { + fmt::dynamic_format_arg_store store; + store.push_back(fmt::arg("a1", 42)); + std::string result = fmt::vformat("{a1}", store); + EXPECT_EQ("42", result); +} + +TEST(FormatDynArgsTest, NamedStrings) { + fmt::dynamic_format_arg_store store; + char str[]{"1234567890"}; + store.push_back(fmt::arg("a1", str)); + store.push_back(fmt::arg("a2", std::cref(str))); + str[0] = 'X'; + + std::string result = fmt::vformat("{a1} and {a2}", store); + + EXPECT_EQ("1234567890 and X234567890", result); +} + +TEST(FormatDynArgsTest, NamedArgByRef) { + fmt::dynamic_format_arg_store store; + + // Note: fmt::arg() constructs an object which holds a reference + // to its value. It's not an aggregate, so it doesn't extend the + // reference lifetime. As a result, it's a very bad idea passing temporary + // as a named argument value. Only GCC with optimization level >0 + // complains about this. + // + // A real life usecase is when you have both name and value alive + // guarantee their lifetime and thus don't want them to be copied into + // storages. + int a1_val{42}; + auto a1 = fmt::arg("a1_", a1_val); + store.push_back("abc"); + store.push_back(1.5f); + store.push_back(std::cref(a1)); + + std::string result = fmt::vformat("{a1_} and {} and {} and {}", store); + + EXPECT_EQ("42 and abc and 1.5 and 42", result); +} + +TEST(FormatDynArgsTest, NamedCustomFormat) { + fmt::dynamic_format_arg_store store; + custom_type c{}; + store.push_back(fmt::arg("c1", c)); + ++c.i; + store.push_back(fmt::arg("c2", c)); + ++c.i; + store.push_back(fmt::arg("c_ref", std::cref(c))); + ++c.i; + + std::string result = fmt::vformat("{c1} and {c2} and {c_ref}", store); + EXPECT_EQ("cust=0 and cust=1 and cust=3", result); +} + +TEST(FormatDynArgsTest, Clear) { + fmt::dynamic_format_arg_store store; + store.push_back(42); + + std::string result = fmt::vformat("{}", store); + EXPECT_EQ("42", result); + + store.push_back(43); + result = fmt::vformat("{} and {}", store); + EXPECT_EQ("42 and 43", result); + + store.clear(); + store.push_back(44); + result = fmt::vformat("{}", store); + EXPECT_EQ("44", result); +} + +TEST(FormatDynArgsTest, Reserve) { + fmt::dynamic_format_arg_store store; + store.reserve(2, 1); + store.push_back(1.5f); + store.push_back(fmt::arg("a1", 42)); + std::string result = fmt::vformat("{a1} and {}", store); + EXPECT_EQ("42 and 1.5", result); +} + +struct copy_throwable { + copy_throwable() {} + copy_throwable(const copy_throwable&) { throw "deal with it"; } +}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter { + auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) { + return ctx.begin(); + } + auto format(copy_throwable, format_context& ctx) -> decltype(ctx.out()) { + return ctx.out(); + } +}; +FMT_END_NAMESPACE + +TEST(FormatDynArgsTest, ThrowOnCopy) { + fmt::dynamic_format_arg_store store; + store.push_back(std::string("foo")); + try { + store.push_back(copy_throwable()); + } catch (...) { + } + EXPECT_EQ(fmt::vformat("{}", store), "foo"); +} + +TEST(StringViewTest, ValueType) { + static_assert(std::is_same::value, ""); +} + +TEST(StringViewTest, Length) { + // Test that string_view::size() returns string length, not buffer size. + char str[100] = "some string"; + EXPECT_EQ(std::strlen(str), string_view(str).size()); + EXPECT_LT(std::strlen(str), sizeof(str)); +} + +// Check string_view's comparison operator. +template