#include <format>
#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
};
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
{
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()));
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
};
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
{
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() {
test_format(SV("['\\'', '\"']"), SV("[{:?}, {:?}]"), CharT('\''), CharT('"'));
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('"'));
test_format(SV("' '"), SV("{:?}"), CharT(' '));
test_format(SV("'a'"), SV("{:?}"), CharT('a'));
test_format(SV("'b'"), SV("{:?}"), CharT('b'));
test_format(SV("'c'"), SV("{:?}"), CharT('c'));
test_format(SV("'\\u{0}'"), SV("{:?}"), CharT('\0'));
test_format(SV("'\\u{1f}'"), SV("{:?}"), CharT('\x1f'));
if constexpr (sizeof(CharT) == 1)
test_format(SV("'\\x{80}'"), SV("{:?}"), CharT('\x80'));
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
if constexpr (sizeof(CharT) > 1) {
using V = std::basic_string_view<CharT>;
test_format(V{L"'\\u{a0}'"}, L"{:?}", L'\xa0');
test_format(V{L"'\\u{3000}'"}, L"{:?}", L'\x3000');
test_format(V{L"'\\u{2028}'"}, L"{:?}", L'\x2028');
test_format(V{L"'\\u{2029}'"}, L"{:?}", L'\x2029');
test_format(V{L"'\\u{ad}'"}, L"{:?}", L'\xad');
test_format(V{L"'\\u{600}'"}, L"{:?}", L'\x600');
test_format(V{L"'\\u{feff}'"}, L"{:?}", L'\xfeff');
test_format(V{L"'\\x{d800}'"}, L"{:?}", L'\xd800');
test_format(V{L"'\\x{dfff}'"}, L"{:?}", L'\xdfff');
test_format(V{L"'\\u{e000}'"}, L"{:?}", L'\xe000');
test_format(V{L"'\\u{f8ff}'"}, L"{:?}", L'\xf8ff');
test_format(V{L"'\\u{378}'"}, L"{:?}", L'\x378');
test_format(V{L"'\\u{1774}'"}, L"{:?}", L'\x1774');
test_format(V{L"'\\u{ffff}'"}, L"{:?}", L'\xffff');
test_format(V{L"'\\u{300}'"}, L"{:?}", L'\x300');
test_format(V{L"'\\u{fe20}'"}, L"{:?}", L'\xfe20');
}
# ifndef TEST_SHORT_WCHAR
if constexpr (sizeof(CharT) > 2) {
static_assert(sizeof(CharT) == 4, "add support for unexpected size");
constexpr wchar_t x = 0x1ffff;
constexpr std::uint32_t y = 0x1ffff;
static_assert(x == y);
using V = std::basic_string_view<CharT>;
test_format(V{L"'\\u{110bd}'"}, L"{:?}", L'\x110bd');
test_format(V{L"'\\u{e007f}'"}, L"{:?}", L'\xe007f');
test_format(V{L"'\\u{f0000}'"}, L"{:?}", L'\xf0000');
test_format(V{L"'\\u{ffffd}'"}, L"{:?}", L'\xffffd');
test_format(V{L"'\\u{100000}'"}, L"{:?}", L'\x100000');
test_format(V{L"'\\u{10fffd}'"}, L"{:?}", L'\x10fffd');
test_format(V{L"'\\u{1000c}'"}, L"{:?}", L'\x1000c');
test_format(V{L"'\\u{fffff}'"}, L"{:?}", L'\xfffff');
test_format(V{L"'\\u{10fffe}'"}, L"{:?}", L'\x10fffe');
test_format(V{L"'\\u{101fd}'"}, L"{:?}", L'\x101fd');
test_format(V{L"'\\u{e0100}'"}, L"{:?}", L'\xe0100');
test_format(V{L"'\\x{110000}'"}, L"{:?}", L'\x110000');
test_format(V{L"'\\x{ffffffff}'"}, L"{:?}", L'\xffffffff');
}
# endif
#endif
}
template <class CharT>
void test_string() {
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) {
test_format(SV(R"(["\x{c3}"])"), SV("[{:?}]"), "\xc3");
test_format(SV(R"(["\x{c3}("])"), SV("[{:?}]"), "\xc3\x28");
test_format(SV(R"(["\x{c0}\x{80}"])"), SV("[{:?}]"), "\xc0\x80");
test_format(SV(R"(["\x{c1}\x{bf}"])"), SV("[{:?}]"), "\xc1\xbf");
test_format(SV(R"(["\u{80}"])"), SV("[{:?}]"), "\xc2\x80");
test_format(SV(R"(["\x{e0}\x{80}\x{80}"])"), SV("[{:?}]"), "\xe0\x80\x80");
test_format(SV(R"(["\x{e0}\x{81}\x{bf}"])"), SV("[{:?}]"), "\xe0\x81\xbf");
test_format(SV(R"(["\x{e0}\x{82}\x{80}"])"), SV("[{:?}]"), "\xe0\x82\x80");
test_format(SV(R"(["\x{e0}\x{9f}\x{bf}"])"), SV("[{:?}]"), "\xe0\x9f\xbf");
test_format(SV("[\"\u0800\"]"), SV("[{:?}]"), "\xe0\xa0\x80");
#if 0
test_format(SV("[\"\ud7ff\"]"), SV("[{:?}]"), "\xed\x9f\xbf");
#else
test_format(SV(R"(["\u{d7ff}"])"), SV("[{:?}]"), "\xed\x9f\xbf");
#endif
test_format(SV(R"(["\x{ed}\x{a0}\x{80}"])"), SV("[{:?}]"), "\xed\xa0\x80");
test_format(SV(R"(["\x{ed}\x{af}\x{bf}"])"), SV("[{:?}]"), "\xed\xaf\xbf");
test_format(SV(R"(["\x{ed}\x{bf}\x{80}"])"), SV("[{:?}]"), "\xed\xbf\x80");
test_format(SV(R"(["\x{ed}\x{bf}\x{bf}"])"), SV("[{:?}]"), "\xed\xbf\xbf");
test_format(SV(R"(["\u{e000}"])"), SV("[{:?}]"), "\xee\x80\x80");
test_format(SV(R"(["\x{f0}\x{80}\x{80}\x{80}"])"), SV("[{:?}]"), "\xf0\x80\x80\x80");
test_format(SV(R"(["\x{f0}\x{80}\x{81}\x{bf}"])"), SV("[{:?}]"), "\xf0\x80\x81\xbf");
test_format(SV(R"(["\x{f0}\x{80}\x{82}\x{80}"])"), SV("[{:?}]"), "\xf0\x80\x82\x80");
test_format(SV(R"(["\x{f0}\x{80}\x{9f}\x{bf}"])"), SV("[{:?}]"), "\xf0\x80\x9f\xbf");
test_format(SV(R"(["\x{f0}\x{80}\x{a0}\x{80}"])"), SV("[{:?}]"), "\xf0\x80\xa0\x80");
test_format(SV(R"(["\x{f0}\x{8f}\x{bf}\x{bf}"])"), SV("[{:?}]"), "\xf0\x8f\xbf\xbf");
test_format(SV("[\"\U00010000\"]"), SV("[{:?}]"), "\xf0\x90\x80\x80");
test_format(SV(R"(["\u{10ffff}"])"), SV("[{:?}]"), "\xf4\x8f\xbf\xbf");
test_format(SV(R"(["\x{f4}\x{90}\x{80}\x{80}"])"), SV("[{:?}]"), "\xf4\x90\x80\x80");
test_format(SV(R"(["\x{f4}\x{bf}\x{bf}\x{bf}"])"), SV("[{:?}]"), "\xf4\xbf\xbf\xbf");
} else {
test_format(SV("[\"\u00c3\"]"), SV("[{:?}]"), L"\xc3");
test_format(SV("[\"\u00c3(\"]"), SV("[{:?}]"), L"\xc3\x28");
}
test_format(SV(R"(["🤷🏻\u{200d}♂️"])"), SV("[{:?}]"), SV("🤷🏻♂️"));
test_format(SV(R"("\t\n\r\\'\" ")"), SV("{:?}"), SV("\t\n\r\\'\" "));
test_format(SV(R"("abcdefg")"), SV("{:?}"), SV("abcdefg"));
test_format(SV(R"("\u{0}\u{1f}")"), SV("{:?}"), SV("\0\x1f"));
if constexpr (sizeof(CharT) == 1)
test_format(SV(R"("\x{80}")"), SV("{:?}"), SV("\x80"));
test_format(SV(R"(["\u{301}"])"), SV("[{:?}]"), SV("\u0301"));
test_format(SV(R"(["\\\u{301}"])"), SV("[{:?}]"), SV("\\\u0301"));
test_format(SV(R"(["ẹ́"])"), SV("[{:?}]"), SV("e\u0301\u0323"));
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
if constexpr (sizeof(CharT) > 1) {
using V = std::basic_string_view<CharT>;
test_format(V{LR"("\u{a0}\u{3000}")"}, L"{:?}", L"\xa0\x3000");
test_format(V{LR"("\u{2028}")"}, L"{:?}", L"\x2028");
test_format(V{LR"("\u{2029}")"}, L"{:?}", L"\x2029");
test_format(V{LR"("\u{ad}\u{600}\u{feff}")"}, L"{:?}", L"\xad\x600\xfeff");
test_format(V{LR"("\x{d800}")"}, L"{:?}", L"\xd800");
test_format(V{LR"("\u{e000}\u{f8ff}")"}, L"{:?}", L"\xe000\xf8ff");
test_format(V{LR"("\u{378}\u{1774}\u{ffff}")"}, L"{:?}", L"\x378\x1774\xffff");
test_format(V{LR"("\u{300}\u{fe20}")"}, L"{:?}", L"\x300\xfe20");
}
# ifndef TEST_SHORT_WCHAR
if constexpr (sizeof(CharT) > 2) {
static_assert(sizeof(CharT) == 4, "add support for unexpected size");
constexpr wchar_t x = 0x1ffff;
constexpr std::uint32_t y = 0x1ffff;
static_assert(x == y);
using V = std::basic_string_view<CharT>;
test_format(V{LR"("\u{110bd}\u{e007f}")"}, L"{:?}", L"\x110bd\xe007f");
test_format(V{LR"("\u{f0000}\u{ffffd}\u{100000}\u{10fffd}")"}, L"{:?}", L"\xf0000\xffffd\x100000\x10fffd");
test_format(V{LR"("\u{1000c}\u{fffff}\u{10fffe}")"}, L"{:?}", L"\x1000c\xfffff\x10fffe");
test_format(V{LR"("\u{101fd}\u{e0100}")"}, L"{:?}", L"\x101fd\xe0100");
test_format(V{LR"("\x{110000}\x{ffffffff}")"}, L"{:?}", L"\x110000\xffffffff");
}
# endif
#endif
}
template <class CharT, class TestFunction>
void test_format_functions(TestFunction check) {
check(SV(R"(***"hellö")"), SV("{:*>10?}"), SV("hellö"));
check(SV(R"(*"hellö"**)"), SV("{:*^10?}"), SV("hellö"));
check(SV(R"("hellö"***)"), SV("{:*<10?}"), SV("hellö"));
check(SV(R"(***"hellö")"), SV("{:*>10?}"), SV("hello\u0308"));
check(SV(R"(*"hellö"**)"), SV("{:*^10?}"), SV("hello\u0308"));
check(SV(R"("hellö"***)"), SV("{:*<10?}"), SV("hello\u0308"));
check(SV(R"(***"hello 🤷🏻\u{200d}♂️")"), SV("{:*>22?}"), SV("hello 🤷🏻♂️"));
check(SV(R"(*"hello 🤷🏻\u{200d}♂️"**)"), SV("{:*^22?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻\u{200d}♂️"***)"), SV("{:*<22?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hellö" )"), SV("{:10?}"), SV("hellö"));
check(SV(R"("hellö" )"), SV("{:10?}"), SV("hello\u0308"));
check(SV(R"("hello 🤷🏻\u{200d}♂️" )"), SV("{:22?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hell)"), SV("{:.5?}"), SV("hellö"));
check(SV(R"("hellö)"), SV("{:.6?}"), SV("hellö"));
check(SV(R"("hellö")"), SV("{:.7?}"), SV("hellö"));
check(SV(R"("hello )"), SV("{:.7?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello )"), SV("{:.8?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻)"), SV("{:.9?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻\)"), SV("{:.10?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻\u{200d})"), SV("{:.17?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻\u{200d}♂️)"), SV("{:.18?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻\u{200d}♂️")"), SV("{:.19?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hell#########################)"), SV("{:#<30.5?}"), SV("hellö"));
check(SV(R"("hellö########################)"), SV("{:#<30.6?}"), SV("hellö"));
check(SV(R"("hellö"#######################)"), SV("{:#<30.7?}"), SV("hellö"));
check(SV(R"("hello #######################)"), SV("{:#<30.7?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello #######################)"), SV("{:#<30.8?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻#####################)"), SV("{:#<30.9?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻\####################)"), SV("{:#<30.10?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻\u{200d}#############)"), SV("{:#<30.17?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻\u{200d}♂️############)"), SV("{:#<30.18?}"), SV("hello 🤷🏻♂️"));
check(SV(R"("hello 🤷🏻\u{200d}♂️"###########)"), SV("{:#<30.19?}"), 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);
}
static void test_ill_formed_utf8() {
using namespace std::literals;
test_format(R"("\x{df}")"sv, "{:?}", "\xdf");
test_format(R"("\x{ef}")"sv, "{:?}", "\xef");
test_format(R"("\x{ef}\x{bf}")"sv, "{:?}", "\xef\xbf");
test_format(R"("\x{f7}")"sv, "{:?}", "\xf7");
test_format(R"("\x{f7}\x{bf}")"sv, "{:?}", "\xf7\xbf");
test_format(R"("\x{f7}\x{bf}\x{bf}")"sv, "{:?}", "\xf7\xbf\xbf");
test_format(R"("\x{df}a")"sv,
"{:?}",
"\xdf"
"a");
test_format(R"("\x{ef}a")"sv,
"{:?}",
"\xef"
"a");
test_format(R"("\x{ef}\x{bf}a")"sv,
"{:?}",
"\xef\xbf"
"a");
test_format(R"("\x{f7}a")"sv,
"{:?}",
"\xf7"
"a");
test_format(R"("\x{f7}\x{bf}a")"sv,
"{:?}",
"\xf7\xbf"
"a");
test_format(R"("\x{f7}\x{bf}\x{bf}a")"sv,
"{:?}",
"\xf7\xbf\xbf"
"a");
test_format(R"("a\x{f1}\x{80}\x{80}\x{e1}\x{80}\x{c2}b")"sv,
"{:?}",
"a"
"\xf1\x80\x80\xe1\x80\xc2"
"b");
test_format(R"("\u{10ffff}")"sv, "{:?}", "\xf4\x8f\xbf\xbf");
test_format(R"("\x{f4}\x{90}\x{80}\x{80}")"sv, "{:?}", "\xf4\x90\x80\x80");
test_format(R"("\x{f5}\x{b1}\x{b2}\x{b3}")"sv, "{:?}", "\xf5\xb1\xb2\xb3");
test_format(R"("\x{f7}\x{bf}\x{bf}\x{bf}")"sv, "{:?}", "\xf7\xbf\xbf\xbf");
}
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
# ifdef TEST_SHORT_WCHAR
static void test_ill_formed_utf16() {
using namespace std::literals;
test_format(LR"("\x{d800}")"sv, L"{:?}", L"\xd800");
test_format(LR"("\x{dbff}")"sv, L"{:?}", L"\xdbff");
test_format(LR"("\x{dc00}a")"sv,
L"{:?}",
L"\xdc00"
"a");
test_format(LR"("\x{dfff}a")"sv,
L"{:?}",
L"\xdfff"
"a");
test_format(LR"("\x{d800}a")"sv,
L"{:?}",
L"\xd800"
"a");
test_format(LR"("\x{dbff}a")"sv,
L"{:?}",
L"\xdbff"
"a");
}
# else
static void test_ill_formed_utf32() {
using namespace std::literals;
test_format(LR"("\u{10ffff}")"sv, L"{:?}", L"\x10ffff");
test_format(LR"("\x{110000}")"sv, L"{:?}", L"\x110000");
test_format(LR"("\x{ffffffff}")"sv, L"{:?}", L"\xffffffff");
}
# endif
#endif
int main(int, char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif
test_ill_formed_utf8();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
# ifdef TEST_SHORT_WCHAR
test_ill_formed_utf16();
# else
test_ill_formed_utf32();
# endif
#endif
return 0;
}