From e05c8fcffe6e0cdde386b50879a7d28d0ebd1136 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 10:45:20 -0800 Subject: [PATCH] Add test --- test/CMakeLists.txt | 4 + test/enforce-compiletime-test.cc | 709 +++++++++++++++++++++++++++++++ 2 files changed, 713 insertions(+) create mode 100644 test/enforce-compiletime-test.cc diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 45721a6d..e6f95733 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -106,6 +106,10 @@ add_fmt_test(printf-test) add_fmt_test(ranges-test) add_fmt_test(scan-test) + +add_fmt_test(enforce-compiletime-test) +target_compile_definitions(enforce-compiletime-test PRIVATE "-DFMT_ENFORCE_COMPILE_STRING") + if (NOT DEFINED MSVC_STATIC_RUNTIME AND MSVC) foreach (flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc new file mode 100644 index 00000000..b39745b0 --- /dev/null +++ b/test/enforce-compiletime-test.cc @@ -0,0 +1,709 @@ +// 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 + +#ifdef WIN32 +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include "fmt/format.h" +#include "fmt/locale.h" +#include "fmt/chrono.h" +#include "fmt/color.h" +#include "fmt/ostream.h" + +#undef index + +#include "gmock.h" +#include "gtest-extra.h" +#include "mock-allocator.h" +#include "util.h" + +#undef ERROR + +using fmt::basic_memory_buffer; +using fmt::format; +using fmt::format_error; +using fmt::memory_buffer; +using fmt::string_view; +using fmt::wmemory_buffer; +using fmt::wstring_view; +using fmt::detail::max_value; + +using testing::Return; +using testing::StrictMock; + +class Answer {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter : formatter { + template + auto format(Answer, FormatContext& ctx) -> decltype(ctx.out()) { + return formatter::format(42, ctx); + } +}; +FMT_END_NAMESPACE + +struct string_like {}; +fmt::string_view to_string_view(string_like) { return "foo"; } + +constexpr char with_null[3] = {'{', '}', '\0'}; +constexpr char no_null[2] = {'{', '}'}; +static FMT_CONSTEXPR_DECL const char static_with_null[3] = {'{', '}', '\0'}; +static FMT_CONSTEXPR_DECL const wchar_t static_with_null_wide[3] = {'{', '}', + '\0'}; +static FMT_CONSTEXPR_DECL const char static_no_null[2] = {'{', '}'}; +static FMT_CONSTEXPR_DECL const wchar_t static_no_null_wide[2] = {'{', '}'}; + +TEST(FormatTest, CompileTimeString) { + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42)); + EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); + + (void)static_with_null; + (void)static_with_null_wide; + (void)static_no_null; + (void)static_no_null_wide; +#if !defined(_MSC_VER) + EXPECT_EQ("42", fmt::format(FMT_STRING(static_with_null), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(static_with_null_wide), 42)); + EXPECT_EQ("42", fmt::format(FMT_STRING(static_no_null), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(static_no_null_wide), 42)); +#endif + + (void)with_null; + (void)no_null; +#if __cplusplus >= 201703L + EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42)); + EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42)); +#endif +#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L + EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42)); +#endif +} + +TEST(FormatTest, CustomFormatCompileTimeString) { + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer())); + Answer answer; + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer)); + char buf[10] = {}; + fmt::format_to(buf, FMT_STRING("{}"), answer); + const Answer const_answer = Answer(); + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer)); +} + +#if FMT_USE_USER_DEFINED_LITERALS +// Passing user-defined literals directly to EXPECT_EQ causes problems +// with macro argument stringification (#) on some versions of GCC. +// Workaround: Assing the UDL result to a variable before the macro. + +using namespace fmt::literals; + +TEST(LiteralsTest, Format) { + auto udl_format = "{}c{}"_format("ab", 1); + EXPECT_EQ("abc1", udl_format); + auto udl_format_w = L"{}c{}"_format(L"ab", 1); + EXPECT_EQ(L"abc1", udl_format_w); +} + +#endif // FMT_USE_USER_DEFINED_LITERALS + +enum TestEnum { A }; + +namespace adl_test { +namespace fmt { +namespace detail { +struct foo {}; +template void write(OutputIt, foo) = delete; +} // namespace detail +} // namespace fmt +} // namespace adl_test + +FMT_BEGIN_NAMESPACE +template <> +struct formatter : formatter { + template + auto format(adl_test::fmt::detail::foo, FormatContext& ctx) + -> decltype(ctx.out()) { + return formatter::format("foo", ctx); + } +}; +FMT_END_NAMESPACE + +TEST(FormatTest, ToString) { + EXPECT_EQ("42", fmt::to_string(42)); + EXPECT_EQ("0x1234", fmt::to_string(reinterpret_cast(0x1234))); + EXPECT_EQ("foo", fmt::to_string(adl_test::fmt::detail::foo())); +} + +//TEST(FormatTest, ToWString) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } + +TEST(FormatTest, OutputIterators) { + std::list out; + fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); + EXPECT_EQ("42", std::string(out.begin(), out.end())); + std::stringstream s; + fmt::format_to(std::ostream_iterator(s), FMT_STRING("{}"), 42); + EXPECT_EQ("42", s.str()); +} + +TEST(FormatTest, FormattedSize) { + //EXPECT_EQ(2u, fmt::formatted_size(FMT_STRING("{}"), 42)); +} + +TEST(FormatTest, FormatTo) { + std::vector v; + fmt::format_to(std::back_inserter(v), FMT_STRING("{}"), "foo"); + EXPECT_EQ(fmt::string_view(v.data(), v.size()), "foo"); +} + +TEST(FormatTest, FormatToN) { + char buffer[4]; + buffer[3] = 'x'; + auto result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); + EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("123x", fmt::string_view(buffer, 4)); + + result = fmt::format_to_n(buffer, 3, FMT_STRING("{:s}"), "foobar"); + EXPECT_EQ(6u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("foox", fmt::string_view(buffer, 4)); + + buffer[0] = 'x'; + buffer[1] = 'x'; + buffer[2] = 'x'; + result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 'A'); + EXPECT_EQ(1u, result.size); + EXPECT_EQ(buffer + 1, result.out); + EXPECT_EQ("Axxx", fmt::string_view(buffer, 4)); + + result = fmt::format_to_n(buffer, 3, FMT_STRING("{}{} "), 'B', 'C'); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("BC x", fmt::string_view(buffer, 4)); + + result = fmt::format_to_n(buffer, 4, FMT_STRING("{}"), "ABCDE"); + EXPECT_EQ(5u, result.size); + EXPECT_EQ("ABCD", fmt::string_view(buffer, 4)); + + buffer[3] = 'x'; + result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), std::string(1000, '*')); + EXPECT_EQ(1000u, result.size); + EXPECT_EQ("***x", fmt::string_view(buffer, 4)); +} + +TEST(FormatTest, WideFormatToN) { + wchar_t buffer[4]; + buffer[3] = L'x'; + auto result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}"), 12345); + EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4)); + buffer[0] = L'x'; + buffer[1] = L'x'; + buffer[2] = L'x'; + result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}"), L'A'); + EXPECT_EQ(1u, result.size); + EXPECT_EQ(buffer + 1, result.out); + EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4)); + result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}{} "), L'B', L'C'); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4)); +} + +struct test_output_iterator { + char* data; + + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = void; + using pointer = void; + using reference = void; + + test_output_iterator& operator++() { + ++data; + return *this; + } + test_output_iterator operator++(int) { + auto tmp = *this; + ++data; + return tmp; + } + char& operator*() { return *data; } +}; + +TEST(FormatTest, FormatToNOutputIterator) { + char buf[10] = {}; + fmt::format_to_n(test_output_iterator{buf}, 10, FMT_STRING("{}"), 42); + EXPECT_STREQ(buf, "42"); +} + +TEST(FormatTest, VFormatTo) { + typedef fmt::format_context context; + fmt::basic_format_arg arg = fmt::detail::make_arg(42); + fmt::basic_format_args args(&arg, 1); + std::string s; + fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args); + EXPECT_EQ("42", s); + + typedef fmt::wformat_context wcontext; + fmt::basic_format_arg warg = fmt::detail::make_arg(42); + fmt::basic_format_args wargs(&warg, 1); + std::wstring w; + fmt::vformat_to(std::back_inserter(w), FMT_STRING(L"{}"), wargs); + EXPECT_EQ(L"42", w); +} + +template static std::string FmtToString(const T& t) { + return fmt::format(FMT_STRING("{}"), t); +} + +TEST(FormatTest, FmtStringInTemplate) { + EXPECT_EQ(FmtToString(1), "1"); + EXPECT_EQ(FmtToString(0), "0"); +} + + + +#include "fmt/chrono.h" + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +/* +TEST(ChronoTest, FormatDefault) { + EXPECT_EQ("42s", fmt::format(FMT_STRING("{}"), std::chrono::seconds(42))); +} + +TEST(ChronoTest, FormatWide) { + EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); +} + +typedef std::chrono::duration dms; + +TEST(ChronoTest, FormatDefaultFP) { + EXPECT_EQ("1.234ms", fmt::format(FMT_STRING("{}"), dms(1.234))); +} + +TEST(ChronoTest, FormatPrecision) { + EXPECT_EQ("1.2ms", fmt::format(FMT_STRING("{:.1}"), dms(1.234))); + EXPECT_EQ("1.23ms", fmt::format(FMT_STRING("{:.{}}"), dms(1.234), 2)); +} + +TEST(ChronoTest, FormatFullSpecs) { + EXPECT_EQ("1.2ms ", fmt::format(FMT_STRING("{:6.1}"), dms(1.234))); + EXPECT_EQ(" 1.2ms ", fmt::format(FMT_STRING("{:^{}.{}}"), dms(1.234), 7, 1)); +} + +TEST(ChronoTest, FormatSimpleQq) { + typedef std::chrono::duration fs; + //EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), fs(1.234))); + typedef std::chrono::duration fms; + EXPECT_EQ("1.234 ms", fmt::format(FMT_STRING("{:%Q %q}"), fms(1.234))); + typedef std::chrono::duration ds; + EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), ds(1.234))); + EXPECT_EQ("1.234 ms", fmt::format(FMT_STRING("{:%Q %q}"), dms(1.234))); +} + +TEST(ChronoTest, FormatPrecisionQq) { + EXPECT_EQ("1.2 ms", fmt::format(FMT_STRING("{:.1%Q %q}"), dms(1.234))); + EXPECT_EQ("1.23 ms", fmt::format(FMT_STRING("{:.{}%Q %q}"), dms(1.234), 2)); +} + +TEST(ChronoTest, FormatFullSpecsQq) { + EXPECT_EQ("1.2 ms ", fmt::format(FMT_STRING("{:7.1%Q %q}"), dms(1.234))); + EXPECT_EQ(" 1.2 ms ", fmt::format(FMT_STRING("{:^{}.{}%Q %q}"), dms(1.234), 8, 1)); +} + +TEST(ChronoTest, UnsignedDuration) { + EXPECT_EQ("42s", fmt::format(FMT_STRING("{}"), std::chrono::duration(42))); +} +*/ +#endif // FMT_STATIC_THOUSANDS_SEPARATOR + + +TEST(ColorsTest, ColorsPrint) { + EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)")), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); + EXPECT_WRITE( + stdout, + fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), FMT_STRING("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, FMT_STRING("bold")), + "\x1b[1mbold\x1b[0m"); + EXPECT_WRITE( + stdout, + fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, FMT_STRING("blue/bold")), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), FMT_STRING("hi")), "hi"); + EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), FMT_STRING("tred")), + "\x1b[31mtred\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan")), + "\x1b[46mtcyan\x1b[0m"); + EXPECT_WRITE(stdout, + fmt::print(bg(fmt::terminal_color::bright_magenta), FMT_STRING("tbmagenta")), + "\x1b[105mtbmagenta\x1b[0m"); +} + +TEST(ColorsTest, Format) { + EXPECT_EQ("\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m", fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)"))); + EXPECT_EQ("\x1b[38;2;000;000;255mblue\x1b[0m", fmt::format(fg(fmt::color::blue), FMT_STRING("blue"))); + EXPECT_EQ( + "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m", + fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), FMT_STRING("two color"))); + EXPECT_EQ("\x1b[1mbold\x1b[0m", fmt::format(fmt::emphasis::bold, FMT_STRING("bold"))); + EXPECT_EQ( + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m", + fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, FMT_STRING("blue/bold"))); + EXPECT_EQ("hi", fmt::format(fmt::text_style(), FMT_STRING("hi"))); + EXPECT_EQ("\x1b[31mtred\x1b[0m", + fmt::format(fg(fmt::terminal_color::red), FMT_STRING("tred"))); + EXPECT_EQ("\x1b[46mtcyan\x1b[0m", + fmt::format(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan"))); + EXPECT_EQ("\x1b[105mtbmagenta\x1b[0m", + fmt::format(bg(fmt::terminal_color::bright_magenta), FMT_STRING("tbmagenta"))); +} + +TEST(ColorsTest, FormatToOutAcceptsTextStyle) { + fmt::text_style ts = fg(fmt::rgb(255, 20, 30)); + std::string out; + fmt::format_to(std::back_inserter(out), ts, FMT_STRING("rgb(255,20,30){}{}{}"), 1, 2, 3); + + EXPECT_EQ(fmt::to_string(out), + "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m"); +} + +// Formatting library for C++ - std::ostream support tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#define FMT_STRING_ALIAS 1 +#include "fmt/format.h" + +struct test {}; + +// Test that there is no issues with specializations when fmt/ostream.h is +// included after fmt/format.h. +namespace fmt { +template <> struct formatter : formatter { + template + typename FormatContext::iterator format(const test&, FormatContext& ctx) { + return formatter::format(42, ctx); + } +}; +} // namespace fmt + +#include + +#include "fmt/ostream.h" + +struct EmptyTest {}; +static std::ostream& operator<<(std::ostream& os, EmptyTest) { + return os << ""; +} + +TEST(OStreamTest, EmptyCustomOutput) { + EXPECT_EQ("", fmt::format(FMT_STRING("{}"), EmptyTest())); +} + +TEST(OStreamTest, Print) { + std::ostringstream os; + fmt::print(os, FMT_STRING("Don't {}!"), "panic"); + EXPECT_EQ("Don't panic!", os.str()); + std::wostringstream wos; + fmt::print(wos, FMT_STRING(L"Don't {}!"), L"panic"); + EXPECT_EQ(L"Don't panic!", wos.str()); +} + +TEST(OStreamTest, Join) { + int v[3] = {1, 2, 3}; + EXPECT_EQ("1, 2, 3", fmt::format(FMT_STRING("{}"), fmt::join(v, v + 3, ", "))); +} + +namespace fmt_test { +struct ABC {}; + +template Output& operator<<(Output& out, ABC) { + out << "ABC"; + return out; +} +} // namespace fmt_test + +template struct TestTemplate {}; + +template +std::ostream& operator<<(std::ostream& os, TestTemplate) { + return os << 1; +} + +namespace fmt { +template struct formatter> : formatter { + template + typename FormatContext::iterator format(TestTemplate, FormatContext& ctx) { + return formatter::format(2, ctx); + } +}; +} // namespace fmt + +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 407 +TEST(OStreamTest, Template) { + EXPECT_EQ("2", fmt::format(FMT_STRING("{}"), TestTemplate())); +} + +TEST(OStreamTest, FormatToN) { + char buffer[4]; + buffer[3] = 'x'; + auto result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), fmt_test::ABC()); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("ABCx", fmt::string_view(buffer, 4)); +} +#endif + +TEST(OStreamTest, CompileTimeString) { + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); +} + +TEST(OStreamTest, ToString) { + EXPECT_EQ("ABC", fmt::to_string(fmt_test::ABC())); +} + + +#include "fmt/ranges.h" + +// Check if 'if constexpr' is supported. +#if (__cplusplus > 201402L) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) + +# include +# include +# include +# include + +TEST(RangesTest, FormatArray) { + int32_t ia[] = {1, 2, 3, 5, 7, 11}; + auto iaf = fmt::format(FMT_STRING("{}"), ia); + EXPECT_EQ("{1, 2, 3, 5, 7, 11}", iaf); +} + +TEST(RangesTest, Format2dArray) { + int32_t ia[][2] = {{1, 2}, {3, 5}, {7, 11}}; + auto iaf = fmt::format(FMT_STRING("{}"), ia); + EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", iaf); +} + +TEST(RangesTest, FormatVector) { + std::vector iv{1, 2, 3, 5, 7, 11}; + auto ivf = fmt::format(FMT_STRING("{}"), iv); + EXPECT_EQ("{1, 2, 3, 5, 7, 11}", ivf); +} + +TEST(RangesTest, FormatVector2) { + std::vector> ivv{{1, 2}, {3, 5}, {7, 11}}; + auto ivf = fmt::format(FMT_STRING("{}"), ivv); + EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf); +} + +TEST(RangesTest, FormatMap) { + std::map simap{{"one", 1}, {"two", 2}}; + EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", fmt::format(FMT_STRING("{}"), simap)); +} + +TEST(RangesTest, FormatPair) { + std::pair pa1{42, 1.5f}; + EXPECT_EQ("(42, 1.5)", fmt::format(FMT_STRING("{}"), pa1)); +} + +TEST(RangesTest, FormatTuple) { + std::tuple t{42, 1.5f, "this is tuple", + 'i'}; + EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format(FMT_STRING("{}"), t)); + EXPECT_EQ("()", fmt::format(FMT_STRING("{}"), std::tuple<>())); +} + +TEST(RangesTest, JoinTuple) { + // Value tuple args + std::tuple t1 = std::make_tuple('a', 1, 2.0f); + EXPECT_EQ("(a, 1, 2)", fmt::format(FMT_STRING("({})"), fmt::join(t1, ", "))); + + // Testing lvalue tuple args + int x = 4; + std::tuple t2{'b', x}; + EXPECT_EQ("b + 4", fmt::format(FMT_STRING("{}"), fmt::join(t2, " + "))); + + // Empty tuple + std::tuple<> t3; + EXPECT_EQ("", fmt::format(FMT_STRING("{}"), fmt::join(t3, "|"))); + + // Single element tuple + std::tuple t4{4.0f}; + EXPECT_EQ("4", fmt::format(FMT_STRING("{}"), fmt::join(t4, "/"))); +} + +TEST(RangesTest, JoinInitializerList) { + EXPECT_EQ("1, 2, 3", fmt::format(FMT_STRING("{}"), fmt::join({1, 2, 3}, ", "))); + EXPECT_EQ("fmt rocks !", + fmt::format(FMT_STRING("{}"), fmt::join({"fmt", "rocks", "!"}, " "))); +} + +struct my_struct { + int32_t i; + std::string str; // can throw + template decltype(auto) get() const noexcept { + if constexpr (N == 0) + return i; + else if constexpr (N == 1) + return fmt::string_view{str}; + } +}; + +template decltype(auto) get(const my_struct& s) noexcept { + return s.get(); +} + +namespace std { + +template <> struct tuple_size : std::integral_constant {}; + +template struct tuple_element { + using type = decltype(std::declval().get()); +}; + +} // namespace std + +TEST(RangesTest, FormatStruct) { + my_struct mst{13, "my struct"}; + EXPECT_EQ("(13, \"my struct\")", fmt::format(FMT_STRING("{}"), mst)); +} + +TEST(RangesTest, FormatTo) { + char buf[10]; + auto end = fmt::format_to(buf, FMT_STRING("{}"), std::vector{1, 2, 3}); + *end = '\0'; + EXPECT_STREQ(buf, "{1, 2, 3}"); +} + +struct path_like { + const path_like* begin() const; + const path_like* end() const; + + operator std::string() const; +}; + +TEST(RangesTest, PathLike) { + EXPECT_FALSE((fmt::is_range::value)); +} + +#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG > + // 201402L && _MSC_VER >= 1910) + +#ifdef FMT_USE_STRING_VIEW +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"; } +}; + +TEST(RangesTest, FormatStringLike) { + EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); +} +#endif // FMT_USE_STRING_VIEW + +struct zstring_sentinel {}; + +bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } +bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } + +struct zstring { + const char* p; + const char* begin() const { return p; } + zstring_sentinel end() const { return {}; } +}; + +// TODO: Fix using zstrings with FMT_STRING +TEST(RangesTest, JoinSentinel) { + zstring hello{"hello"}; + //EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), hello)); + //EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), fmt::join(hello, "_"))); +} + +// A range that provides non-const only begin()/end() to test fmt::join handles +// that +// +// Some ranges (eg those produced by range-v3's views::filter()) can cache +// information during iteration so they only provide non-const begin()/end(). +template class non_const_only_range { + private: + std::vector vec; + + public: + using const_iterator = typename ::std::vector::const_iterator; + + template + explicit non_const_only_range(Args&&... args) + : vec(::std::forward(args)...) {} + + const_iterator begin() { return vec.begin(); } + const_iterator end() { return vec.end(); } +}; + +template class noncopyable_range { + private: + std::vector vec; + + public: + using const_iterator = typename ::std::vector::const_iterator; + + template + explicit noncopyable_range(Args&&... args) + : vec(::std::forward(args)...) {} + + noncopyable_range(noncopyable_range const&) = delete; + noncopyable_range(noncopyable_range&) = delete; + + const_iterator begin() const { return vec.begin(); } + const_iterator end() const { return vec.end(); } +}; + +//TODO: Fixme +TEST(RangesTest, Range) { + noncopyable_range w(3u, 0); + /*EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), w)); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), noncopyable_range(3u, 0))); + + non_const_only_range x(3u, 0); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), x)); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), non_const_only_range(3u, 0))); + + std::vector y(3u, 0); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), y)); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), std::vector(3u, 0))); + + const std::vector z(3u, 0); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), z));*/ +} + +#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 +struct unformattable {}; + +TEST(RangesTest, UnformattableRange) { + EXPECT_FALSE((fmt::has_formatter, + fmt::format_context>::value)); +} +#endif + + +