| 1 | //===----------------------------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 |
| 10 | // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME |
| 11 | |
| 12 | // This version runs the test when the platform has Unicode support. |
| 13 | // UNSUPPORTED: libcpp-has-no-unicode |
| 14 | |
| 15 | // TODO FMT This test should not require std::to_chars(floating-point) |
| 16 | // XFAIL: availability-fp_to_chars-missing |
| 17 | |
| 18 | // <format> |
| 19 | |
| 20 | // This test the debug string type for the formatter specializations for char |
| 21 | // and string types. This tests Unicode strings. |
| 22 | |
| 23 | #include <format> |
| 24 | |
| 25 | #include <cassert> |
| 26 | #include <concepts> |
| 27 | #include <cstdint> |
| 28 | #include <iterator> |
| 29 | #include <list> |
| 30 | #include <vector> |
| 31 | |
| 32 | #include "test_macros.h" |
| 33 | #include "make_string.h" |
| 34 | #include "test_format_string.h" |
| 35 | #include "assert_macros.h" |
| 36 | #include "concat_macros.h" |
| 37 | |
| 38 | #ifndef TEST_HAS_NO_LOCALIZATION |
| 39 | # include <iostream> |
| 40 | #endif |
| 41 | |
| 42 | #define SV(S) MAKE_STRING_VIEW(CharT, S) |
| 43 | |
| 44 | auto test_format = []<class CharT, class... Args>( |
| 45 | std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) { |
| 46 | { |
| 47 | std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...); |
| 48 | TEST_REQUIRE(out == expected, |
| 49 | TEST_WRITE_CONCATENATED( |
| 50 | "\nFormat string " , fmt.get(), "\nExpected output " , expected, "\nActual output " , out, '\n')); |
| 51 | } |
| 52 | #ifndef TEST_HAS_NO_LOCALIZATION |
| 53 | { |
| 54 | std::basic_string<CharT> out = std::format(std::locale(), fmt, std::forward<Args>(args)...); |
| 55 | assert(out == expected); |
| 56 | } |
| 57 | #endif // TEST_HAS_NO_LOCALIZATION |
| 58 | }; |
| 59 | |
| 60 | auto test_format_to = |
| 61 | []<class CharT, class... Args>( |
| 62 | std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) { |
| 63 | { |
| 64 | std::basic_string<CharT> out(expected.size(), CharT(' ')); |
| 65 | auto it = std::format_to(out.begin(), fmt, std::forward<Args>(args)...); |
| 66 | assert(it == out.end()); |
| 67 | assert(out == expected); |
| 68 | } |
| 69 | #ifndef TEST_HAS_NO_LOCALIZATION |
| 70 | { |
| 71 | std::basic_string<CharT> out(expected.size(), CharT(' ')); |
| 72 | auto it = std::format_to(out.begin(), std::locale(), fmt, std::forward<Args>(args)...); |
| 73 | assert(it == out.end()); |
| 74 | assert(out == expected); |
| 75 | } |
| 76 | #endif // TEST_HAS_NO_LOCALIZATION |
| 77 | { |
| 78 | std::list<CharT> out; |
| 79 | std::format_to(std::back_inserter(out), fmt, std::forward<Args>(args)...); |
| 80 | assert(std::equal(out.begin(), out.end(), expected.begin(), expected.end())); |
| 81 | } |
| 82 | { |
| 83 | std::vector<CharT> out; |
| 84 | std::format_to(std::back_inserter(out), fmt, std::forward<Args>(args)...); |
| 85 | assert(std::equal(out.begin(), out.end(), expected.begin(), expected.end())); |
| 86 | } |
| 87 | { |
| 88 | assert(expected.size() < 4096 && "Update the size of the buffer." ); |
| 89 | CharT out[4096]; |
| 90 | CharT* it = std::format_to(out, fmt, std::forward<Args>(args)...); |
| 91 | assert(std::distance(out, it) == int(expected.size())); |
| 92 | // Convert to std::string since output contains '\0' for boolean tests. |
| 93 | assert(std::basic_string<CharT>(out, it) == expected); |
| 94 | } |
| 95 | }; |
| 96 | |
| 97 | auto test_formatted_size = |
| 98 | []<class CharT, class... Args>( |
| 99 | std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) { |
| 100 | { |
| 101 | std::size_t size = std::formatted_size(fmt, std::forward<Args>(args)...); |
| 102 | assert(size == expected.size()); |
| 103 | } |
| 104 | #ifndef TEST_HAS_NO_LOCALIZATION |
| 105 | { |
| 106 | std::size_t size = std::formatted_size(std::locale(), fmt, std::forward<Args>(args)...); |
| 107 | assert(size == expected.size()); |
| 108 | } |
| 109 | #endif // TEST_HAS_NO_LOCALIZATION |
| 110 | }; |
| 111 | |
| 112 | auto test_format_to_n = |
| 113 | []<class CharT, class... Args>( |
| 114 | std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) { |
| 115 | { |
| 116 | std::size_t n = expected.size(); |
| 117 | std::basic_string<CharT> out(n, CharT(' ')); |
| 118 | std::format_to_n_result result = std::format_to_n(out.begin(), n, fmt, std::forward<Args>(args)...); |
| 119 | assert(result.size == static_cast<std::ptrdiff_t>(expected.size())); |
| 120 | assert(result.out == out.end()); |
| 121 | assert(out == expected); |
| 122 | } |
| 123 | #ifndef TEST_HAS_NO_LOCALIZATION |
| 124 | { |
| 125 | std::size_t n = expected.size(); |
| 126 | std::basic_string<CharT> out(n, CharT(' ')); |
| 127 | std::format_to_n_result result = |
| 128 | std::format_to_n(out.begin(), n, std::locale(), fmt, std::forward<Args>(args)...); |
| 129 | assert(result.size == static_cast<std::ptrdiff_t>(expected.size())); |
| 130 | assert(result.out == out.end()); |
| 131 | assert(out == expected); |
| 132 | } |
| 133 | #endif // TEST_HAS_NO_LOCALIZATION |
| 134 | { |
| 135 | std::ptrdiff_t n = 0; |
| 136 | std::basic_string<CharT> out; |
| 137 | std::format_to_n_result result = std::format_to_n(out.begin(), n, fmt, std::forward<Args>(args)...); |
| 138 | assert(result.size == static_cast<std::ptrdiff_t>(expected.size())); |
| 139 | assert(result.out == out.end()); |
| 140 | assert(out.empty()); |
| 141 | } |
| 142 | { |
| 143 | std::ptrdiff_t n = expected.size() / 2; |
| 144 | std::basic_string<CharT> out(n, CharT(' ')); |
| 145 | std::format_to_n_result result = std::format_to_n(out.begin(), n, fmt, std::forward<Args>(args)...); |
| 146 | assert(result.size == static_cast<std::ptrdiff_t>(expected.size())); |
| 147 | assert(result.out == out.end()); |
| 148 | assert(out == expected.substr(0, n)); |
| 149 | } |
| 150 | }; |
| 151 | |
| 152 | template <class CharT> |
| 153 | void test_char() { |
| 154 | // *** P2286 examples *** |
| 155 | test_format(SV("['\\'', '\"']" ), SV("[{:?}, {:?}]" ), CharT('\''), CharT('"')); |
| 156 | |
| 157 | // *** Special cases *** |
| 158 | test_format(SV("'\\t'" ), SV("{:?}" ), CharT('\t')); |
| 159 | test_format(SV("'\\n'" ), SV("{:?}" ), CharT('\n')); |
| 160 | test_format(SV("'\\r'" ), SV("{:?}" ), CharT('\r')); |
| 161 | test_format(SV("'\\\\'" ), SV("{:?}" ), CharT('\\')); |
| 162 | |
| 163 | test_format(SV("'\\\''" ), SV("{:?}" ), CharT('\'')); |
| 164 | test_format(SV("'\"'" ), SV("{:?}" ), CharT('"')); // only special for string |
| 165 | |
| 166 | test_format(SV("' '" ), SV("{:?}" ), CharT(' ')); |
| 167 | |
| 168 | // *** Printable *** |
| 169 | test_format(SV("'a'" ), SV("{:?}" ), CharT('a')); |
| 170 | test_format(SV("'b'" ), SV("{:?}" ), CharT('b')); |
| 171 | test_format(SV("'c'" ), SV("{:?}" ), CharT('c')); |
| 172 | |
| 173 | // *** Non-printable *** |
| 174 | |
| 175 | // Control |
| 176 | test_format(SV("'\\u{0}'" ), SV("{:?}" ), CharT('\0')); |
| 177 | test_format(SV("'\\u{1f}'" ), SV("{:?}" ), CharT('\x1f')); |
| 178 | |
| 179 | // Ill-formed |
| 180 | if constexpr (sizeof(CharT) == 1) |
| 181 | test_format(SV("'\\x{80}'" ), SV("{:?}" ), CharT('\x80')); |
| 182 | |
| 183 | #ifndef TEST_HAS_NO_WIDE_CHARACTERS |
| 184 | if constexpr (sizeof(CharT) > 1) { |
| 185 | using V = std::basic_string_view<CharT>; |
| 186 | |
| 187 | // Unicode fitting in a 16-bit wchar_t |
| 188 | |
| 189 | // *** Non-printable *** |
| 190 | |
| 191 | // Space_Separator |
| 192 | test_format(V{L"'\\u{a0}'" }, L"{:?}" , L'\xa0'); // NO-BREAK SPACE |
| 193 | test_format(V{L"'\\u{3000}'" }, L"{:?}" , L'\x3000'); // IDEOGRAPHIC SPACE |
| 194 | |
| 195 | // Line_Separator |
| 196 | test_format(V{L"'\\u{2028}'" }, L"{:?}" , L'\x2028'); // LINE SEPARATOR |
| 197 | |
| 198 | // Paragraph_Separator |
| 199 | test_format(V{L"'\\u{2029}'" }, L"{:?}" , L'\x2029'); // PARAGRAPH SEPARATOR |
| 200 | |
| 201 | // Format |
| 202 | test_format(V{L"'\\u{ad}'" }, L"{:?}" , L'\xad'); // SOFT HYPHEN |
| 203 | test_format(V{L"'\\u{600}'" }, L"{:?}" , L'\x600'); // ARABIC NUMBER SIGN |
| 204 | test_format(V{L"'\\u{feff}'" }, L"{:?}" , L'\xfeff'); // ZERO WIDTH NO-BREAK SPACE |
| 205 | |
| 206 | // Incomplete surrogate pair in UTF-16 |
| 207 | test_format(V{L"'\\x{d800}'" }, L"{:?}" , L'\xd800'); // <surrogate-D800> |
| 208 | test_format(V{L"'\\x{dfff}'" }, L"{:?}" , L'\xdfff'); // <surrogate-DFFF> |
| 209 | |
| 210 | // Private_Use |
| 211 | test_format(V{L"'\\u{e000}'" }, L"{:?}" , L'\xe000'); // <private-use-E000> |
| 212 | test_format(V{L"'\\u{f8ff}'" }, L"{:?}" , L'\xf8ff'); // <private-use-F8FF> |
| 213 | |
| 214 | // Unassigned |
| 215 | test_format(V{L"'\\u{378}'" }, L"{:?}" , L'\x378'); // <reserved-0378> |
| 216 | test_format(V{L"'\\u{1774}'" }, L"{:?}" , L'\x1774'); // <reserved-1774> |
| 217 | test_format(V{L"'\\u{ffff}'" }, L"{:?}" , L'\xffff'); // <noncharacter-FFFF> |
| 218 | |
| 219 | // Grapheme Extended |
| 220 | test_format(V{L"'\\u{300}'" }, L"{:?}" , L'\x300'); // COMBINING GRAVE ACCENT |
| 221 | test_format(V{L"'\\u{fe20}'" }, L"{:?}" , L'\xfe20'); // VARIATION SELECTOR-1 |
| 222 | } |
| 223 | # ifndef TEST_SHORT_WCHAR |
| 224 | if constexpr (sizeof(CharT) > 2) { |
| 225 | static_assert(sizeof(CharT) == 4, "add support for unexpected size" ); |
| 226 | // Unicode fitting in a 32-bit wchar_t |
| 227 | |
| 228 | constexpr wchar_t x = 0x1ffff; |
| 229 | constexpr std::uint32_t y = 0x1ffff; |
| 230 | static_assert(x == y); |
| 231 | |
| 232 | using V = std::basic_string_view<CharT>; |
| 233 | |
| 234 | // *** Non-printable *** |
| 235 | // Format |
| 236 | test_format(V{L"'\\u{110bd}'" }, L"{:?}" , L'\x110bd'); // KAITHI NUMBER SIGN |
| 237 | test_format(V{L"'\\u{e007f}'" }, L"{:?}" , L'\xe007f'); // CANCEL TAG |
| 238 | |
| 239 | // Private_Use |
| 240 | test_format(V{L"'\\u{f0000}'" }, L"{:?}" , L'\xf0000'); // <private-use-F0000> |
| 241 | test_format(V{L"'\\u{ffffd}'" }, L"{:?}" , L'\xffffd'); // <private-use-FFFFD> |
| 242 | |
| 243 | test_format(V{L"'\\u{100000}'" }, L"{:?}" , L'\x100000'); // <private-use-100000> |
| 244 | test_format(V{L"'\\u{10fffd}'" }, L"{:?}" , L'\x10fffd'); // <private-use-10FFFD> |
| 245 | |
| 246 | // Unassigned |
| 247 | test_format(V{L"'\\u{1000c}'" }, L"{:?}" , L'\x1000c'); // <reserved-1000c> |
| 248 | test_format(V{L"'\\u{fffff}'" }, L"{:?}" , L'\xfffff'); // <noncharacter-FFFFF> |
| 249 | test_format(V{L"'\\u{10fffe}'" }, L"{:?}" , L'\x10fffe'); // <noncharacter-10FFFE> |
| 250 | |
| 251 | // Grapheme Extended |
| 252 | test_format(V{L"'\\u{101fd}'" }, L"{:?}" , L'\x101fd'); // COMBINING OLD PERMIC LETTER AN |
| 253 | test_format(V{L"'\\u{e0100}'" }, L"{:?}" , L'\xe0100'); // VARIATION SELECTOR-17 |
| 254 | |
| 255 | // Ill-formed |
| 256 | test_format(V{L"'\\x{110000}'" }, L"{:?}" , L'\x110000'); |
| 257 | test_format(V{L"'\\x{ffffffff}'" }, L"{:?}" , L'\xffffffff'); |
| 258 | } |
| 259 | # endif // TEST_SHORT_WCHAR |
| 260 | #endif // TEST_HAS_NO_WIDE_CHARACTERS |
| 261 | } |
| 262 | |
| 263 | template <class CharT> |
| 264 | void test_string() { |
| 265 | // *** P2286 examples *** |
| 266 | test_format(SV("[h\tllo]" ), SV("[{}]" ), SV("h\tllo" )); |
| 267 | test_format(SV(R"(["h\tllo"])" ), SV("[{:?}]" ), SV("h\tllo" )); |
| 268 | test_format(SV(R"(["Спасибо, Виктор ♥!"])" ), SV("[{:?}]" ), SV("Спасибо, Виктор ♥!" )); |
| 269 | |
| 270 | test_format(SV(R"(["\u{0} \n \t \u{2} \u{1b}"])" ), SV("[{:?}]" ), SV("\0 \n \t \x02 \x1b" )); |
| 271 | |
| 272 | if constexpr (sizeof(CharT) == 1) { |
| 273 | // Ill-formend UTF-8 |
| 274 | test_format(SV(R"(["\x{c3}"])" ), SV("[{:?}]" ), "\xc3" ); |
| 275 | test_format(SV(R"(["\x{c3}("])" ), SV("[{:?}]" ), "\xc3\x28" ); |
| 276 | |
| 277 | /* U+0000..U+0007F 1 code unit range, encoded in 2 code units. */ |
| 278 | test_format(SV(R"(["\x{c0}\x{80}"])" ), SV("[{:?}]" ), "\xc0\x80" ); // U+0000 |
| 279 | test_format(SV(R"(["\x{c1}\x{bf}"])" ), SV("[{:?}]" ), "\xc1\xbf" ); // U+007F |
| 280 | test_format(SV(R"(["\u{80}"])" ), SV("[{:?}]" ), "\xc2\x80" ); // U+0080 first valid (General_Category=Control) |
| 281 | |
| 282 | /* U+0000..U+07FFF 1 and 2 code unit range, encoded in 3 code units. */ |
| 283 | test_format(SV(R"(["\x{e0}\x{80}\x{80}"])" ), SV("[{:?}]" ), "\xe0\x80\x80" ); // U+0000 |
| 284 | test_format(SV(R"(["\x{e0}\x{81}\x{bf}"])" ), SV("[{:?}]" ), "\xe0\x81\xbf" ); // U+007F |
| 285 | test_format(SV(R"(["\x{e0}\x{82}\x{80}"])" ), SV("[{:?}]" ), "\xe0\x82\x80" ); // U+0080 |
| 286 | test_format(SV(R"(["\x{e0}\x{9f}\x{bf}"])" ), SV("[{:?}]" ), "\xe0\x9f\xbf" ); // U+07FF |
| 287 | test_format(SV("[\"\u0800\"]" ), SV("[{:?}]" ), "\xe0\xa0\x80" ); // U+0800 first valid |
| 288 | |
| 289 | #if 0 |
| 290 | // This code point is in the Hangul Jamo Extended-B block and at the time of writing |
| 291 | // it's unassigned. When it comes defined, this branch might become true. |
| 292 | test_format(SV("[\"\ud7ff\"]" ), SV("[{:?}]" ), "\xed\x9f\xbf" ); // U+D7FF last valid |
| 293 | #else |
| 294 | /* U+D800..D+DFFFF surrogate range */ |
| 295 | test_format(SV(R"(["\u{d7ff}"])" ), SV("[{:?}]" ), "\xed\x9f\xbf" ); // U+D7FF last valid |
| 296 | #endif |
| 297 | test_format(SV(R"(["\x{ed}\x{a0}\x{80}"])" ), SV("[{:?}]" ), "\xed\xa0\x80" ); // U+D800 |
| 298 | test_format(SV(R"(["\x{ed}\x{af}\x{bf}"])" ), SV("[{:?}]" ), "\xed\xaf\xbf" ); // U+DBFF |
| 299 | test_format(SV(R"(["\x{ed}\x{bf}\x{80}"])" ), SV("[{:?}]" ), "\xed\xbf\x80" ); // U+DC00 |
| 300 | test_format(SV(R"(["\x{ed}\x{bf}\x{bf}"])" ), SV("[{:?}]" ), "\xed\xbf\xbf" ); // U+DFFF |
| 301 | test_format(SV(R"(["\u{e000}"])" ), SV("[{:?}]" ), "\xee\x80\x80" ); // U+E000 first valid |
| 302 | // (in the Private Use Area block) |
| 303 | |
| 304 | /* U+0000..U+FFFF 1, 2, and 3 code unit range */ |
| 305 | test_format(SV(R"(["\x{f0}\x{80}\x{80}\x{80}"])" ), SV("[{:?}]" ), "\xf0\x80\x80\x80" ); // U+0000 |
| 306 | test_format(SV(R"(["\x{f0}\x{80}\x{81}\x{bf}"])" ), SV("[{:?}]" ), "\xf0\x80\x81\xbf" ); // U+007F |
| 307 | test_format(SV(R"(["\x{f0}\x{80}\x{82}\x{80}"])" ), SV("[{:?}]" ), "\xf0\x80\x82\x80" ); // U+0080 |
| 308 | test_format(SV(R"(["\x{f0}\x{80}\x{9f}\x{bf}"])" ), SV("[{:?}]" ), "\xf0\x80\x9f\xbf" ); // U+07FF |
| 309 | test_format(SV(R"(["\x{f0}\x{80}\x{a0}\x{80}"])" ), SV("[{:?}]" ), "\xf0\x80\xa0\x80" ); // U+0800 |
| 310 | test_format(SV(R"(["\x{f0}\x{8f}\x{bf}\x{bf}"])" ), SV("[{:?}]" ), "\xf0\x8f\xbf\xbf" ); // U+FFFF |
| 311 | test_format(SV("[\"\U00010000\"]" ), SV("[{:?}]" ), "\xf0\x90\x80\x80" ); // U+10000 first valid |
| 312 | |
| 313 | /* U+10FFFF..U+1FFFFF invalid range */ |
| 314 | test_format(SV(R"(["\u{10ffff}"])" ), SV("[{:?}]" ), "\xf4\x8f\xbf\xbf" ); // U+10FFFF last valid |
| 315 | // (in Supplementary Private Use Area-B) |
| 316 | test_format(SV(R"(["\x{f4}\x{90}\x{80}\x{80}"])" ), SV("[{:?}]" ), "\xf4\x90\x80\x80" ); // U+110000 |
| 317 | test_format(SV(R"(["\x{f4}\x{bf}\x{bf}\x{bf}"])" ), SV("[{:?}]" ), "\xf4\xbf\xbf\xbf" ); // U+11FFFF |
| 318 | } else { |
| 319 | // Valid UTF-16 and UTF-32 |
| 320 | test_format(SV("[\"\u00c3\"]" ), SV("[{:?}]" ), L"\xc3" ); // LATIN CAPITAL LETTER A WITH TILDE |
| 321 | test_format(SV("[\"\u00c3(\"]" ), SV("[{:?}]" ), L"\xc3\x28" ); |
| 322 | } |
| 323 | |
| 324 | // LWG-3965 |
| 325 | test_format(SV(R"(["🤷🏻\u{200d}♂️"])" ), SV("[{:?}]" ), SV("🤷🏻♂️" )); |
| 326 | |
| 327 | // *** Special cases *** |
| 328 | test_format(SV(R"("\t\n\r\\'\" ")" ), SV("{:?}" ), SV("\t\n\r\\'\" " )); |
| 329 | |
| 330 | // *** Printable *** |
| 331 | test_format(SV(R"("abcdefg")" ), SV("{:?}" ), SV("abcdefg" )); |
| 332 | |
| 333 | // *** Non-printable *** |
| 334 | |
| 335 | // Control |
| 336 | test_format(SV(R"("\u{0}\u{1f}")" ), SV("{:?}" ), SV("\0\x1f" )); |
| 337 | |
| 338 | // Ill-formed |
| 339 | if constexpr (sizeof(CharT) == 1) |
| 340 | test_format(SV(R"("\x{80}")" ), SV("{:?}" ), SV("\x80" )); |
| 341 | |
| 342 | // *** P2713R1 examples *** |
| 343 | test_format(SV(R"(["\u{301}"])" ), SV("[{:?}]" ), SV("\u0301" )); |
| 344 | test_format(SV(R"(["\\\u{301}"])" ), SV("[{:?}]" ), SV("\\\u0301" )); |
| 345 | test_format(SV(R"(["ẹ́"])" ), SV("[{:?}]" ), SV("e\u0301\u0323" )); |
| 346 | |
| 347 | #ifndef TEST_HAS_NO_WIDE_CHARACTERS |
| 348 | if constexpr (sizeof(CharT) > 1) { |
| 349 | using V = std::basic_string_view<CharT>; |
| 350 | |
| 351 | // Unicode fitting in a 16-bit wchar_t |
| 352 | |
| 353 | // *** Non-printable *** |
| 354 | |
| 355 | // Space_Separator |
| 356 | test_format(V{LR"("\u{a0}\u{3000}")" }, L"{:?}" , L"\xa0\x3000" ); |
| 357 | |
| 358 | // Line_Separator |
| 359 | test_format(V{LR"("\u{2028}")" }, L"{:?}" , L"\x2028" ); // LINE SEPARATOR |
| 360 | |
| 361 | // Paragraph_Separator |
| 362 | test_format(V{LR"("\u{2029}")" }, L"{:?}" , L"\x2029" ); // PARAGRAPH SEPARATOR |
| 363 | |
| 364 | // Format |
| 365 | test_format(V{LR"("\u{ad}\u{600}\u{feff}")" }, L"{:?}" , L"\xad\x600\xfeff" ); |
| 366 | |
| 367 | // Incomplete surrogate pair in UTF-16 |
| 368 | test_format(V{LR"("\x{d800}")" }, L"{:?}" , L"\xd800" ); |
| 369 | |
| 370 | // Private_Use |
| 371 | test_format(V{LR"("\u{e000}\u{f8ff}")" }, L"{:?}" , L"\xe000\xf8ff" ); |
| 372 | |
| 373 | // Unassigned |
| 374 | test_format(V{LR"("\u{378}\u{1774}\u{ffff}")" }, L"{:?}" , L"\x378\x1774\xffff" ); |
| 375 | |
| 376 | // Grapheme Extended |
| 377 | test_format(V{LR"("\u{300}\u{fe20}")" }, L"{:?}" , L"\x300\xfe20" ); |
| 378 | } |
| 379 | # ifndef TEST_SHORT_WCHAR |
| 380 | if constexpr (sizeof(CharT) > 2) { |
| 381 | static_assert(sizeof(CharT) == 4, "add support for unexpected size" ); |
| 382 | // Unicode fitting in a 32-bit wchar_t |
| 383 | |
| 384 | constexpr wchar_t x = 0x1ffff; |
| 385 | constexpr std::uint32_t y = 0x1ffff; |
| 386 | static_assert(x == y); |
| 387 | |
| 388 | using V = std::basic_string_view<CharT>; |
| 389 | |
| 390 | // *** Non-printable *** |
| 391 | // Format |
| 392 | test_format(V{LR"("\u{110bd}\u{e007f}")" }, L"{:?}" , L"\x110bd\xe007f" ); |
| 393 | |
| 394 | // Private_Use |
| 395 | test_format(V{LR"("\u{f0000}\u{ffffd}\u{100000}\u{10fffd}")" }, L"{:?}" , L"\xf0000\xffffd\x100000\x10fffd" ); |
| 396 | |
| 397 | // Unassigned |
| 398 | test_format(V{LR"("\u{1000c}\u{fffff}\u{10fffe}")" }, L"{:?}" , L"\x1000c\xfffff\x10fffe" ); |
| 399 | |
| 400 | // Grapheme Extended |
| 401 | test_format(V{LR"("\u{101fd}\u{e0100}")" }, L"{:?}" , L"\x101fd\xe0100" ); |
| 402 | |
| 403 | // Ill-formed |
| 404 | test_format(V{LR"("\x{110000}\x{ffffffff}")" }, L"{:?}" , L"\x110000\xffffffff" ); |
| 405 | } |
| 406 | # endif // TEST_SHORT_WCHAR |
| 407 | #endif // TEST_HAS_NO_WIDE_CHARACTERS |
| 408 | } |
| 409 | |
| 410 | template <class CharT, class TestFunction> |
| 411 | void test_format_functions(TestFunction check) { |
| 412 | // *** align-fill & width *** |
| 413 | check(SV(R"(***"hellö")" ), SV("{:*>10?}" ), SV("hellö" )); // ö is LATIN SMALL LETTER O WITH DIAERESIS |
| 414 | check(SV(R"(*"hellö"**)" ), SV("{:*^10?}" ), SV("hellö" )); |
| 415 | check(SV(R"("hellö"***)" ), SV("{:*<10?}" ), SV("hellö" )); |
| 416 | |
| 417 | check(SV(R"(***"hellö")" ), SV("{:*>10?}" ), SV("hello\u0308" )); |
| 418 | check(SV(R"(*"hellö"**)" ), SV("{:*^10?}" ), SV("hello\u0308" )); |
| 419 | check(SV(R"("hellö"***)" ), SV("{:*<10?}" ), SV("hello\u0308" )); |
| 420 | |
| 421 | check(SV(R"(***"hello 🤷🏻\u{200d}♂️")" ), SV("{:*>22?}" ), SV("hello 🤷🏻♂️" )); |
| 422 | check(SV(R"(*"hello 🤷🏻\u{200d}♂️"**)" ), SV("{:*^22?}" ), SV("hello 🤷🏻♂️" )); |
| 423 | check(SV(R"("hello 🤷🏻\u{200d}♂️"***)" ), SV("{:*<22?}" ), SV("hello 🤷🏻♂️" )); |
| 424 | |
| 425 | // *** width *** |
| 426 | check(SV(R"("hellö" )" ), SV("{:10?}" ), SV("hellö" )); |
| 427 | check(SV(R"("hellö" )" ), SV("{:10?}" ), SV("hello\u0308" )); |
| 428 | check(SV(R"("hello 🤷🏻\u{200d}♂️" )" ), SV("{:22?}" ), SV("hello 🤷🏻♂️" )); |
| 429 | |
| 430 | // *** precision *** |
| 431 | check(SV(R"("hell)" ), SV("{:.5?}" ), SV("hellö" )); |
| 432 | check(SV(R"("hellö)" ), SV("{:.6?}" ), SV("hellö" )); |
| 433 | check(SV(R"("hellö")" ), SV("{:.7?}" ), SV("hellö" )); |
| 434 | |
| 435 | check(SV(R"("hello )" ), SV("{:.7?}" ), SV("hello 🤷🏻♂️" )); |
| 436 | check(SV(R"("hello )" ), SV("{:.8?}" ), SV("hello 🤷🏻♂️" )); // shrug is two columns |
| 437 | check(SV(R"("hello 🤷🏻)" ), SV("{:.9?}" ), SV("hello 🤷🏻♂️" )); |
| 438 | check(SV(R"("hello 🤷🏻\)" ), SV("{:.10?}" ), SV("hello 🤷🏻♂️" )); |
| 439 | check(SV(R"("hello 🤷🏻\u{200d})" ), SV("{:.17?}" ), SV("hello 🤷🏻♂️" )); |
| 440 | check(SV(R"("hello 🤷🏻\u{200d}♂️)" ), SV("{:.18?}" ), SV("hello 🤷🏻♂️" )); |
| 441 | check(SV(R"("hello 🤷🏻\u{200d}♂️")" ), SV("{:.19?}" ), SV("hello 🤷🏻♂️" )); |
| 442 | |
| 443 | // *** width & precision *** |
| 444 | check(SV(R"("hell#########################)" ), SV("{:#<30.5?}" ), SV("hellö" )); |
| 445 | check(SV(R"("hellö########################)" ), SV("{:#<30.6?}" ), SV("hellö" )); |
| 446 | check(SV(R"("hellö"#######################)" ), SV("{:#<30.7?}" ), SV("hellö" )); |
| 447 | |
| 448 | check(SV(R"("hello #######################)" ), SV("{:#<30.7?}" ), SV("hello 🤷🏻♂️" )); |
| 449 | check(SV(R"("hello #######################)" ), SV("{:#<30.8?}" ), SV("hello 🤷🏻♂️" )); |
| 450 | check(SV(R"("hello 🤷🏻#####################)" ), SV("{:#<30.9?}" ), SV("hello 🤷🏻♂️" )); |
| 451 | check(SV(R"("hello 🤷🏻\####################)" ), SV("{:#<30.10?}" ), SV("hello 🤷🏻♂️" )); |
| 452 | check(SV(R"("hello 🤷🏻\u{200d}#############)" ), SV("{:#<30.17?}" ), SV("hello 🤷🏻♂️" )); |
| 453 | check(SV(R"("hello 🤷🏻\u{200d}♂️############)" ), SV("{:#<30.18?}" ), SV("hello 🤷🏻♂️" )); |
| 454 | check(SV(R"("hello 🤷🏻\u{200d}♂️"###########)" ), SV("{:#<30.19?}" ), SV("hello 🤷🏻♂️" )); |
| 455 | } |
| 456 | |
| 457 | template <class CharT> |
| 458 | void test() { |
| 459 | test_char<CharT>(); |
| 460 | test_string<CharT>(); |
| 461 | |
| 462 | test_format_functions<CharT>(test_format); |
| 463 | test_format_functions<CharT>(test_format_to); |
| 464 | test_format_functions<CharT>(test_formatted_size); |
| 465 | test_format_functions<CharT>(test_format_to_n); |
| 466 | } |
| 467 | |
| 468 | static void test_ill_formed_utf8() { |
| 469 | using namespace std::literals; |
| 470 | |
| 471 | // Too few code units |
| 472 | test_format(R"("\x{df}")"sv , "{:?}" , "\xdf" ); |
| 473 | test_format(R"("\x{ef}")"sv , "{:?}" , "\xef" ); |
| 474 | test_format(R"("\x{ef}\x{bf}")"sv , "{:?}" , "\xef\xbf" ); |
| 475 | test_format(R"("\x{f7}")"sv , "{:?}" , "\xf7" ); |
| 476 | test_format(R"("\x{f7}\x{bf}")"sv , "{:?}" , "\xf7\xbf" ); |
| 477 | test_format(R"("\x{f7}\x{bf}\x{bf}")"sv , "{:?}" , "\xf7\xbf\xbf" ); |
| 478 | |
| 479 | // Invalid continuation byte |
| 480 | test_format(R"("\x{df}a")"sv , |
| 481 | "{:?}" , |
| 482 | "\xdf" |
| 483 | "a" ); |
| 484 | test_format(R"("\x{ef}a")"sv , |
| 485 | "{:?}" , |
| 486 | "\xef" |
| 487 | "a" ); |
| 488 | test_format(R"("\x{ef}\x{bf}a")"sv , |
| 489 | "{:?}" , |
| 490 | "\xef\xbf" |
| 491 | "a" ); |
| 492 | test_format(R"("\x{f7}a")"sv , |
| 493 | "{:?}" , |
| 494 | "\xf7" |
| 495 | "a" ); |
| 496 | test_format(R"("\x{f7}\x{bf}a")"sv , |
| 497 | "{:?}" , |
| 498 | "\xf7\xbf" |
| 499 | "a" ); |
| 500 | test_format(R"("\x{f7}\x{bf}\x{bf}a")"sv , |
| 501 | "{:?}" , |
| 502 | "\xf7\xbf\xbf" |
| 503 | "a" ); |
| 504 | |
| 505 | test_format(R"("a\x{f1}\x{80}\x{80}\x{e1}\x{80}\x{c2}b")"sv , |
| 506 | "{:?}" , |
| 507 | "a" |
| 508 | "\xf1\x80\x80\xe1\x80\xc2" |
| 509 | "b" ); |
| 510 | |
| 511 | // Code unit out of range |
| 512 | test_format(R"("\u{10ffff}")"sv , "{:?}" , "\xf4\x8f\xbf\xbf" ); // last valid code point |
| 513 | test_format(R"("\x{f4}\x{90}\x{80}\x{80}")"sv , "{:?}" , "\xf4\x90\x80\x80" ); // first invalid code point |
| 514 | test_format(R"("\x{f5}\x{b1}\x{b2}\x{b3}")"sv , "{:?}" , "\xf5\xb1\xb2\xb3" ); |
| 515 | test_format(R"("\x{f7}\x{bf}\x{bf}\x{bf}")"sv , "{:?}" , "\xf7\xbf\xbf\xbf" ); // largest encoded code point |
| 516 | } |
| 517 | |
| 518 | #ifndef TEST_HAS_NO_WIDE_CHARACTERS |
| 519 | # ifdef TEST_SHORT_WCHAR |
| 520 | static void test_ill_formed_utf16() { |
| 521 | using namespace std::literals; |
| 522 | |
| 523 | // Too few code units |
| 524 | test_format(LR"("\x{d800}")"sv , L"{:?}" , L"\xd800" ); |
| 525 | test_format(LR"("\x{dbff}")"sv , L"{:?}" , L"\xdbff" ); |
| 526 | |
| 527 | // Start with low surrogate pair |
| 528 | test_format(LR"("\x{dc00}a")"sv , |
| 529 | L"{:?}" , |
| 530 | L"\xdc00" |
| 531 | "a" ); |
| 532 | test_format(LR"("\x{dfff}a")"sv , |
| 533 | L"{:?}" , |
| 534 | L"\xdfff" |
| 535 | "a" ); |
| 536 | |
| 537 | // Only high surrogate pair |
| 538 | test_format(LR"("\x{d800}a")"sv , |
| 539 | L"{:?}" , |
| 540 | L"\xd800" |
| 541 | "a" ); |
| 542 | test_format(LR"("\x{dbff}a")"sv , |
| 543 | L"{:?}" , |
| 544 | L"\xdbff" |
| 545 | "a" ); |
| 546 | } |
| 547 | # else // TEST_SHORT_WCHAR |
| 548 | static void test_ill_formed_utf32() { |
| 549 | using namespace std::literals; |
| 550 | |
| 551 | test_format(LR"("\u{10ffff}")"sv , L"{:?}" , L"\x10ffff" ); // last valid code point |
| 552 | test_format(LR"("\x{110000}")"sv , L"{:?}" , L"\x110000" ); // first invalid code point |
| 553 | test_format(LR"("\x{ffffffff}")"sv , L"{:?}" , L"\xffffffff" ); // largest encoded code point |
| 554 | } |
| 555 | |
| 556 | # endif // TEST_SHORT_WCHAR |
| 557 | #endif // TEST_HAS_NO_WIDE_CHARACTERS |
| 558 | |
| 559 | int main(int, char**) { |
| 560 | test<char>(); |
| 561 | #ifndef TEST_HAS_NO_WIDE_CHARACTERS |
| 562 | test<wchar_t>(); |
| 563 | #endif |
| 564 | |
| 565 | test_ill_formed_utf8(); |
| 566 | #ifndef TEST_HAS_NO_WIDE_CHARACTERS |
| 567 | # ifdef TEST_SHORT_WCHAR |
| 568 | test_ill_formed_utf16(); |
| 569 | # else // TEST_SHORT_WCHAR |
| 570 | test_ill_formed_utf32(); |
| 571 | # endif // TEST_SHORT_WCHAR |
| 572 | #endif // TEST_HAS_NO_WIDE_CHARACTERS |
| 573 | |
| 574 | return 0; |
| 575 | } |
| 576 | |