#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 {
}
// std::is_constructible is broken in MSVC until version 2015.
-#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
+#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
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
EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2));
EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc"));
EXPECT_EQ(L"z", fmt::format(L"{}", L'z'));
- EXPECT_THROW(fmt::format(L"{:*\x343E}", 42), fmt::format_error);
+ EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error);
EXPECT_EQ(L"true", fmt::format(L"{}", true));
EXPECT_EQ(L"a", fmt::format(L"{0}", 'a'));
EXPECT_EQ(L"a", fmt::format(L"{0}", L'a'));
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));
+ EXPECT_EQ(fmt::format(fmt::wformat_string<int>(L"{}"), 42), L"42");
+#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L
+ EXPECT_EQ(fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42), L"42");
#endif
}
-#if __cplusplus > 201103L
+#if FMT_CPLUSPLUS > 201103L
struct custom_char {
int value;
custom_char() = default;
}
#if FMT_USE_USER_DEFINED_LITERALS
-TEST(xchar_test, format_udl) {
- using namespace fmt::literals;
- EXPECT_EQ(L"{}c{}"_format(L"ab", 1), fmt::format(L"{}c{}", L"ab", 1));
-}
-
TEST(xchar_test, named_arg_udl) {
using namespace fmt::literals;
auto udl_a =
return os << L"streamable_enum";
}
+namespace fmt {
+template <>
+struct formatter<streamable_enum, wchar_t> : basic_ostream_formatter<wchar_t> {
+};
+} // namespace fmt
+
enum unstreamable_enum {};
+auto format_as(unstreamable_enum e) -> int { return e; }
TEST(xchar_test, enum) {
EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum()));
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
}
-TEST(xchar_test, sign_not_truncated) {
- wchar_t format_str[] = {
- L'{', L':',
- '+' | static_cast<wchar_t>(1 << fmt::detail::num_bits<char>()), L'}', 0};
- EXPECT_THROW(fmt::format(format_str, 42), fmt::format_error);
-}
-
-namespace fake_qt {
-class QString {
- public:
- QString(const wchar_t* s) : s_(s) {}
- const wchar_t* utf16() const FMT_NOEXCEPT { return s_.data(); }
- int size() const FMT_NOEXCEPT { return static_cast<int>(s_.size()); }
+struct streamable_and_unformattable {};
- private:
- std::wstring s_;
-};
+auto operator<<(std::wostream& os, streamable_and_unformattable)
+ -> std::wostream& {
+ return os << L"foo";
+}
-fmt::basic_string_view<wchar_t> to_string_view(const QString& s) FMT_NOEXCEPT {
- return {s.utf16(), static_cast<size_t>(s.size())};
+TEST(xchar_test, streamed) {
+ EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>());
+ EXPECT_EQ(fmt::format(L"{}", fmt::streamed(streamable_and_unformattable())),
+ L"foo");
}
-} // namespace fake_qt
-TEST(format_test, format_foreign_strings) {
- using fake_qt::QString;
- EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
- EXPECT_EQ(fmt::format(QString(L"{}"), QString(L"42")), L"42");
+TEST(xchar_test, sign_not_truncated) {
+ wchar_t format_str[] = {
+ L'{', L':',
+ '+' | static_cast<wchar_t>(1 << fmt::detail::num_bits<char>()), L'}', 0};
+ EXPECT_THROW(fmt::format(fmt::runtime(format_str), 42), fmt::format_error);
}
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_wchar, 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"};
+#ifndef _WIN32
+ // Disabled on Windows, because these formats is not consistent among
+ // platforms.
+ spec_list.insert(spec_list.end(), {L"%c", L"%Ec", L"%r"});
+#elif defined(__MINGW32__) && !defined(_UCRT)
+ // Only C89 conversion specifiers when using MSVCRT instead of UCRT
+ spec_list = {L"%%", L"%Y", L"%y", L"%b", L"%B", L"%m", L"%U",
+ L"%W", L"%j", L"%d", L"%a", L"%A", L"%w", L"%H",
+ L"%I", L"%M", L"%S", L"%x", L"%X", L"%p", L"%Z"};
+#endif
+ spec_list.push_back(L"%Y-%m-%d %H:%M:%S");
+
+ for (const auto& spec : spec_list) {
+ auto t = std::chrono::system_clock::to_time_t(t1);
+ auto tm = *std::localtime(&t);
+
+ auto sys_output = system_wcsftime(spec, &tm);
+
+ auto fmt_spec = fmt::format(L"{{:{}}}", spec);
+ EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1));
+ EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
+ }
}
TEST(xchar_test, color) {
}
TEST(xchar_test, ostream) {
+#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409
std::wostringstream wos;
fmt::print(wos, L"Don't {}!", L"panic");
- EXPECT_EQ(L"Don't panic!", wos.str());
+ EXPECT_EQ(wos.str(), L"Don't panic!");
+#endif
+}
+
+TEST(xchar_test, format_map) {
+ auto m = std::map<std::wstring, int>{{L"one", 1}, {L"t\"wo", 2}};
+ EXPECT_EQ(fmt::format(L"{}", m), L"{\"one\": 1, \"t\\\"wo\": 2}");
+}
+
+TEST(xchar_test, escape_string) {
+ using vec = std::vector<std::wstring>;
+ EXPECT_EQ(fmt::format(L"{}", vec{L"\n\r\t\"\\"}), L"[\"\\n\\r\\t\\\"\\\\\"]");
+ EXPECT_EQ(fmt::format(L"{}", vec{L"понедельник"}), L"[\"понедельник\"]");
}
TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }
Char do_thousands_sep() const override { return ','; }
};
-TEST(locale_test, double_decimal_point) {
+TEST(locale_test, localized_double) {
auto loc = std::locale(std::locale(), new numpunct<char>());
- EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23));
- EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23));
+ EXPECT_EQ(fmt::format(loc, "{:L}", 1.23), "1?23");
+ EXPECT_EQ(fmt::format(loc, "{:Lf}", 1.23), "1?230000");
+ EXPECT_EQ(fmt::format(loc, "{:L}", 1234.5), "1~234?5");
+ EXPECT_EQ(fmt::format(loc, "{:L}", 12000.0), "12~000");
+ EXPECT_EQ(fmt::format(loc, "{:8L}", 1230.0), " 1~230");
}
TEST(locale_test, format) {
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>(),
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);
+}
+
+TEST(locale_test, sign) {
+ EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50");
+}
+
#endif // FMT_STATIC_THOUSANDS_SEPARATOR