//===----------------------------------------------------------------------===//
//
// 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: GCC-ALWAYS_INLINE-FIXME
// TODO FMT This test should not require std::to_chars(floating-point)
// XFAIL: availability-fp_to_chars-missing
// REQUIRES: 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('"'));
// *** Special 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("🤷🏻♂️"));
// *** Special 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;
}