| //===----------------------------------------------------------------------===// |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 |
| // UNSUPPORTED: libcpp-has-no-incomplete-format |
| |
| // TODO FMT This test should not require std::to_chars(floating-point) |
| // XFAIL: availability-fp_to_chars-missing |
| |
| // Force unicode to be disabled. |
| // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_HAS_NO_UNICODE |
| |
| // <format> |
| |
| // This test the debug string type for the formatter specializations for char |
| // and string types. This tests ASCII strings, the tests assume every char32_t value is valid ASCII. |
| |
| #include <cassert> |
| #include <concepts> |
| #include <iterator> |
| #include <list> |
| #include <vector> |
| |
| #include "test_macros.h" |
| #include "make_string.h" |
| #include "test_format_string.h" |
| #include "assert_macros.h" |
| #include "concat_macros.h" |
| |
| #ifndef TEST_HAS_NO_LOCALIZATION |
| # include <iostream> |
| #endif |
| |
| #define SV(S) MAKE_STRING_VIEW(CharT, S) |
| |
| auto test_format = []<class CharT, class... Args>( |
| std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) { |
| { |
| std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...); |
| TEST_REQUIRE(out == expected, |
| TEST_WRITE_CONCATENATED( |
| "\nFormat string ", fmt.get(), "\nExpected output ", expected, "\nActual output ", out, '\n')); |
| } |
| #ifndef TEST_HAS_NO_LOCALIZATION |
| { |
| std::basic_string<CharT> out = std::format(std::locale(), fmt, std::forward<Args>(args)...); |
| assert(out == expected); |
| } |
| #endif // TEST_HAS_NO_LOCALIZATION |
| }; |
| |
| auto test_format_to = |
| []<class CharT, class... Args>( |
| std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) { |
| { |
| std::basic_string<CharT> out(expected.size(), CharT(' ')); |
| auto it = std::format_to(out.begin(), fmt, std::forward<Args>(args)...); |
| assert(it == out.end()); |
| assert(out == expected); |
| } |
| #ifndef TEST_HAS_NO_LOCALIZATION |
| { |
| std::basic_string<CharT> out(expected.size(), CharT(' ')); |
| auto it = std::format_to(out.begin(), std::locale(), fmt, std::forward<Args>(args)...); |
| assert(it == out.end()); |
| assert(out == expected); |
| } |
| #endif // TEST_HAS_NO_LOCALIZATION |
| { |
| std::list<CharT> out; |
| std::format_to(std::back_inserter(out), fmt, std::forward<Args>(args)...); |
| assert(std::equal(out.begin(), out.end(), expected.begin(), expected.end())); |
| } |
| { |
| std::vector<CharT> out; |
| std::format_to(std::back_inserter(out), fmt, std::forward<Args>(args)...); |
| assert(std::equal(out.begin(), out.end(), expected.begin(), expected.end())); |
| } |
| { |
| assert(expected.size() < 4096 && "Update the size of the buffer."); |
| CharT out[4096]; |
| CharT* it = std::format_to(out, fmt, std::forward<Args>(args)...); |
| assert(std::distance(out, it) == int(expected.size())); |
| // Convert to std::string since output contains '\0' for boolean tests. |
| assert(std::basic_string<CharT>(out, it) == expected); |
| } |
| }; |
| |
| auto test_formatted_size = |
| []<class CharT, class... Args>( |
| std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) { |
| { |
| std::size_t size = std::formatted_size(fmt, std::forward<Args>(args)...); |
| assert(size == expected.size()); |
| } |
| #ifndef TEST_HAS_NO_LOCALIZATION |
| { |
| std::size_t size = std::formatted_size(std::locale(), fmt, std::forward<Args>(args)...); |
| assert(size == expected.size()); |
| } |
| #endif // TEST_HAS_NO_LOCALIZATION |
| }; |
| |
| auto test_format_to_n = |
| []<class CharT, class... Args>( |
| std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) { |
| { |
| std::size_t n = expected.size(); |
| std::basic_string<CharT> out(n, CharT(' ')); |
| std::format_to_n_result result = std::format_to_n(out.begin(), n, fmt, std::forward<Args>(args)...); |
| assert(result.size == static_cast<std::ptrdiff_t>(expected.size())); |
| assert(result.out == out.end()); |
| assert(out == expected); |
| } |
| #ifndef TEST_HAS_NO_LOCALIZATION |
| { |
| std::size_t n = expected.size(); |
| std::basic_string<CharT> out(n, CharT(' ')); |
| std::format_to_n_result result = |
| std::format_to_n(out.begin(), n, std::locale(), fmt, std::forward<Args>(args)...); |
| assert(result.size == static_cast<std::ptrdiff_t>(expected.size())); |
| assert(result.out == out.end()); |
| assert(out == expected); |
| } |
| #endif // TEST_HAS_NO_LOCALIZATION |
| { |
| std::ptrdiff_t n = 0; |
| std::basic_string<CharT> out; |
| std::format_to_n_result result = std::format_to_n(out.begin(), n, fmt, std::forward<Args>(args)...); |
| assert(result.size == static_cast<std::ptrdiff_t>(expected.size())); |
| assert(result.out == out.end()); |
| assert(out.empty()); |
| } |
| { |
| std::ptrdiff_t n = expected.size() / 2; |
| std::basic_string<CharT> out(n, CharT(' ')); |
| std::format_to_n_result result = std::format_to_n(out.begin(), n, fmt, std::forward<Args>(args)...); |
| assert(result.size == static_cast<std::ptrdiff_t>(expected.size())); |
| assert(result.out == out.end()); |
| assert(out == expected.substr(0, n)); |
| } |
| }; |
| |
| template <class CharT> |
| void test_char() { |
| // *** P2286 examples *** |
| test_format(SV("['\\'', '\"']"), SV("[{:?}, {:?}]"), CharT('\''), CharT('"')); |
| |
| // *** Specical cases *** |
| test_format(SV("'\\t'"), SV("{:?}"), CharT('\t')); |
| test_format(SV("'\\n'"), SV("{:?}"), CharT('\n')); |
| test_format(SV("'\\r'"), SV("{:?}"), CharT('\r')); |
| test_format(SV("'\\\\'"), SV("{:?}"), CharT('\\')); |
| |
| test_format(SV("'\\\''"), SV("{:?}"), CharT('\'')); |
| test_format(SV("'\"'"), SV("{:?}"), CharT('"')); // only special for string |
| |
| test_format(SV("' '"), SV("{:?}"), CharT(' ')); |
| |
| // *** Printable *** |
| test_format(SV("'a'"), SV("{:?}"), CharT('a')); |
| test_format(SV("'b'"), SV("{:?}"), CharT('b')); |
| test_format(SV("'c'"), SV("{:?}"), CharT('c')); |
| |
| // *** Non-printable *** |
| |
| // Control |
| test_format(SV("'\\u{0}'"), SV("{:?}"), CharT('\0')); |
| test_format(SV("'\\u{1f}'"), SV("{:?}"), CharT('\x1f')); |
| |
| // Ill-formed |
| if constexpr (sizeof(CharT) == 1) |
| test_format(SV("'\x80'"), SV("{:?}"), CharT('\x80')); |
| |
| #ifndef TEST_HAS_NO_WIDE_CHARACTERS |
| if constexpr (sizeof(CharT) > 1) { |
| using V = std::basic_string_view<CharT>; |
| |
| // Unicode fitting in a 16-bit wchar_t |
| |
| // *** Non-printable *** |
| |
| // Space_Separator |
| test_format(V{L"'\xa0'"}, L"{:?}", L'\xa0'); // NO-BREAK SPACE |
| test_format(V{L"'\x3000'"}, L"{:?}", L'\x3000'); // IDEOGRAPHIC SPACE |
| |
| // Line_Separator |
| test_format(V{L"'\x2028'"}, L"{:?}", L'\x2028'); // LINE SEPARATOR |
| |
| // Paragraph_Separator |
| test_format(V{L"'\x2029'"}, L"{:?}", L'\x2029'); // PARAGRAPH SEPARATOR |
| |
| // Format |
| test_format(V{L"'\xad'"}, L"{:?}", L'\xad'); // SOFT HYPHEN |
| test_format(V{L"'\x600'"}, L"{:?}", L'\x600'); // ARABIC NUMBER SIGN |
| test_format(V{L"'\xfeff'"}, L"{:?}", L'\xfeff'); // ZERO WIDTH NO-BREAK SPACE |
| |
| if constexpr (sizeof(CharT) == 2) { |
| // Incomplete surrogate pair in UTF-16 |
| test_format(V{L"'\xd800'"}, L"{:?}", L'\xd800'); // <surrogate-D800> |
| test_format(V{L"'\xdfff'"}, L"{:?}", L'\xdfff'); // <surrogate-DFFF> |
| } else { |
| test_format(V{L"'\xd800'"}, L"{:?}", L'\xd800'); // <surrogate-D800> |
| test_format(V{L"'\xdfff'"}, L"{:?}", L'\xdfff'); // <surrogate-DFFF> |
| } |
| |
| // Private_Use |
| test_format(V{L"'\xe000'"}, L"{:?}", L'\xe000'); // <private-use-E000> |
| test_format(V{L"'\xf8ff'"}, L"{:?}", L'\xf8ff'); // <private-use-F8FF> |
| |
| // Unassigned |
| test_format(V{L"'\x378'"}, L"{:?}", L'\x378'); // <reserved-0378> |
| test_format(V{L"'\x1774'"}, L"{:?}", L'\x1774'); // <reserved-1774> |
| test_format(V{L"'\xffff'"}, L"{:?}", L'\xffff'); // <noncharacter-FFFF> |
| |
| // Grapheme Extended |
| test_format(V{L"'\x300'"}, L"{:?}", L'\x300'); // COMBINING GRAVE ACCENT |
| test_format(V{L"'\xfe20'"}, L"{:?}", L'\xfe20'); // VARIATION SELECTOR-1 |
| } |
| # ifndef TEST_SHORT_WCHAR |
| if constexpr (sizeof(CharT) > 2) { |
| static_assert(sizeof(CharT) == 4, "add support for unexpected size"); |
| // Unicode fitting in a 32-bit wchar_t |
| |
| constexpr wchar_t x = 0x1ffff; |
| constexpr std::uint32_t y = 0x1ffff; |
| static_assert(x == y); |
| |
| using V = std::basic_string_view<CharT>; |
| |
| // *** Non-printable *** |
| // Format |
| test_format(V{L"'\x110bd'"}, L"{:?}", L'\x110bd'); // KAITHI NUMBER SIGN |
| test_format(V{L"'\xe007f'"}, L"{:?}", L'\xe007f'); // CANCEL TAG |
| |
| // Private_Use |
| test_format(V{L"'\xf0000'"}, L"{:?}", L'\xf0000'); // <private-use-F0000> |
| test_format(V{L"'\xffffd'"}, L"{:?}", L'\xffffd'); // <private-use-FFFFD> |
| |
| test_format(V{L"'\x100000'"}, L"{:?}", L'\x100000'); // <private-use-100000> |
| test_format(V{L"'\x10fffd'"}, L"{:?}", L'\x10fffd'); // <private-use-10FFFD> |
| |
| // Unassigned |
| test_format(V{L"'\x1000c'"}, L"{:?}", L'\x1000c'); // <reserved-1000c> |
| test_format(V{L"'\xfffff'"}, L"{:?}", L'\xfffff'); // <noncharacter-FFFFF> |
| test_format(V{L"'\x10fffe'"}, L"{:?}", L'\x10fffe'); // <noncharacter-10FFFE> |
| |
| // Grapheme Extended |
| test_format(V{L"'\x101fd'"}, L"{:?}", L'\x101fd'); // COMBINING OLD PERMIC LETTER AN |
| test_format(V{L"'\xe0100'"}, L"{:?}", L'\xe0100'); // VARIATION SELECTOR-17 |
| |
| // Ill-formed |
| test_format(V{L"'\x110000'"}, L"{:?}", L'\x110000'); |
| test_format(V{L"'\xffffffff'"}, L"{:?}", L'\xffffffff'); |
| } |
| # endif // TEST_SHORT_WCHAR |
| #endif // TEST_HAS_NO_WIDE_CHARACTERS |
| } |
| |
| template <class CharT> |
| void test_string() { |
| // *** P2286 examples *** |
| test_format(SV("[h\tllo]"), SV("[{}]"), SV("h\tllo")); |
| test_format(SV(R"(["h\tllo"])"), SV("[{:?}]"), SV("h\tllo")); |
| test_format(SV(R"(["Спасибо, Виктор ♥!"])"), SV("[{:?}]"), SV("Спасибо, Виктор ♥!")); |
| |
| test_format(SV(R"(["\u{0} \n \t \u{2} \u{1b}"])"), SV("[{:?}]"), SV("\0 \n \t \x02 \x1b")); |
| |
| if constexpr (sizeof(CharT) == 1) { |
| // Ill-formend UTF-8, but valid as ASCII |
| test_format(SV("[\"\xc3\"]"), SV("[{:?}]"), SV("\xc3")); |
| test_format(SV("[\"\xc3\x28\"]"), SV("[{:?}]"), SV("\xc3\x28")); |
| } else { |
| // Valid UTF-16 and UTF-32 |
| test_format(SV("[\"\u00c3\"]"), SV("[{:?}]"), L"\xc3"); // LATIN CAPITAL LETTER A WITH TILDE |
| test_format(SV("[\"\u00c3(\"]"), SV("[{:?}]"), L"\xc3\x28"); |
| } |
| |
| test_format(SV("[\"🤷🏻\u200d♂\ufe0f\"]"), SV("[{:?}]"), SV("🤷🏻♂️")); |
| |
| // *** Specical cases *** |
| test_format(SV(R"("\t\n\r\\'\" ")"), SV("{:?}"), SV("\t\n\r\\'\" ")); |
| |
| // *** Printable *** |
| test_format(SV(R"("abcdefg")"), SV("{:?}"), SV("abcdefg")); |
| |
| // *** Non-printable *** |
| |
| // Control |
| test_format(SV(R"("\u{0}\u{1f}")"), SV("{:?}"), SV("\0\x1f")); |
| |
| // Ill-formed UTF-8, valid ASCII |
| test_format(SV("\"\x80\""), SV("{:?}"), SV("\x80")); |
| } |
| |
| template <class CharT, class TestFunction> |
| void test_format_functions(TestFunction check) { |
| // LATIN SMALL LETTER O WITH DIAERESIS is encoded in two chars or 1 wchar_t |
| // due to the range of the value. |
| // 8 + sizeof(CharT) == 1 is not considered an constant expression |
| |
| // *** align-fill & width *** |
| check(SV(R"(***"hellö")"), |
| SV("{:*>{}?}"), |
| SV("hellö"), |
| sizeof(CharT) == 1 ? 11 : 10); // ö is LATIN SMALL LETTER O WITH DIAERESIS |
| check(SV(R"(*"hellö"**)"), SV("{:*^{}?}"), SV("hellö"), sizeof(CharT) == 1 ? 11 : 10); |
| check(SV(R"("hellö"***)"), SV("{:*<{}?}"), SV("hellö"), sizeof(CharT) == 1 ? 11 : 10); |
| |
| check(SV("\"hello\u0308\""), SV("{:*>{}?}"), SV("hello\u0308"), sizeof(CharT) == 1 ? 9 : 8); |
| check(SV("***\"hello\u0308\""), SV("{:*>{}?}"), SV("hello\u0308"), sizeof(CharT) == 1 ? 12 : 11); |
| check(SV("*\"hello\u0308\"**"), SV("{:*^{}?}"), SV("hello\u0308"), sizeof(CharT) == 1 ? 12 : 11); |
| check(SV("\"hello\u0308\"***"), SV("{:*<{}?}"), SV("hello\u0308"), sizeof(CharT) == 1 ? 12 : 11); |
| |
| // *** width *** |
| check(SV(R"("hello" )"), SV("{:10?}"), SV("hello")); |
| |
| // *** precision *** |
| check(SV(R"("hell)"), SV("{:.5?}"), SV("hello")); |
| check(SV(R"("hello)"), SV("{:.6?}"), SV("hello")); |
| check(SV(R"("hello")"), SV("{:.7?}"), SV("hello")); |
| |
| // *** width & precision *** |
| check(SV(R"("hell#########################)"), SV("{:#<30.5?}"), SV("hello")); |
| check(SV(R"("hello########################)"), SV("{:#<30.6?}"), SV("hello")); |
| check(SV(R"("hello"#######################)"), SV("{:#<30.7?}"), SV("hello")); |
| } |
| |
| template <class CharT> |
| void test() { |
| test_char<CharT>(); |
| |
| test_string<CharT>(); |
| |
| test_format_functions<CharT>(test_format); |
| test_format_functions<CharT>(test_format_to); |
| test_format_functions<CharT>(test_formatted_size); |
| test_format_functions<CharT>(test_format_to_n); |
| } |
| |
| int main(int, char**) { |
| test<char>(); |
| #ifndef TEST_HAS_NO_WIDE_CHARACTERS |
| test<wchar_t>(); |
| #endif |
| |
| return 0; |
| } |