diff --git a/libcxx/docs/Status/FormatPaper.csv b/libcxx/docs/Status/FormatPaper.csv --- a/libcxx/docs/Status/FormatPaper.csv +++ b/libcxx/docs/Status/FormatPaper.csv @@ -32,7 +32,7 @@ `[format.string.std] `_,"std-format-spec ``type`` debug",,Mark de Wever,|Complete|,Clang 16 `[format.range] `_,"Formatting for ranges: sequences",,Mark de Wever,|Complete|,Clang 16 `[format.range.fmtmap] `_,"Formatting for ranges: map",,Mark de Wever,|Complete|,Clang 16 -`[format.range.fmtset] `_,"Formatting for ranges: set",,Mark de Wever,|In Progress|, +`[format.range.fmtset] `_,"Formatting for ranges: set",,Mark de Wever,|Complete|,Clang 16 `[format.range.fmtstr] `_,"Formatting for ranges: strings",,Mark de Wever,|In Progress|, `[format.range] `_,"Formatting for ranges: container adaptors",,Mark de Wever,, `[format.range] `_,"Formatting for ranges: ``pair`` and ``tuple``",,Mark de Wever,|Complete|,Clang 16 diff --git a/libcxx/include/__format/range_default_formatter.h b/libcxx/include/__format/range_default_formatter.h --- a/libcxx/include/__format/range_default_formatter.h +++ b/libcxx/include/__format/range_default_formatter.h @@ -161,7 +161,26 @@ template struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter { - __range_default_formatter() = delete; // TODO FMT Implement +private: + using __maybe_const_set = __fmt_maybe_const<_Rp, _CharT>; + using __element_type = remove_cvref_t>; + range_formatter<__element_type, _CharT> __underlying_; + +public: + _LIBCPP_HIDE_FROM_ABI constexpr __range_default_formatter() { + __underlying_.set_brackets(_LIBCPP_STATICALLY_WIDEN(_CharT, "{"), _LIBCPP_STATICALLY_WIDEN(_CharT, "}")); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) { + return __underlying_.parse(__ctx); + } + + template + _LIBCPP_HIDE_FROM_ABI typename _FormatContext::iterator + format(__maybe_const_set& __range, _FormatContext& __ctx) const { + return __underlying_.format(__range, __ctx); + } }; template diff --git a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp --- a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp @@ -206,14 +206,14 @@ assert_is_formattable, CharT>(); assert_is_formattable, CharT>(); - assert_is_not_formattable, CharT>(); + assert_is_formattable, CharT>(); assert_is_formattable, CharT>(); - assert_is_not_formattable, CharT>(); + assert_is_formattable, CharT>(); assert_is_formattable, CharT>(); - assert_is_not_formattable, CharT>(); + assert_is_formattable, CharT>(); assert_is_formattable, CharT>(); - assert_is_not_formattable, CharT>(); + assert_is_formattable, CharT>(); assert_is_formattable, CharT>(); assert_is_not_formattable, CharT>(); diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.format.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.format.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.format.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// 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 Fix this test using GCC, it currently times out. +// UNSUPPORTED: gcc-12 + +// This test requires the dylib support introduced in D92214. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}} +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}} + +// + +// template +// struct range-default-formatter +// +// template +// string format(format_string fmt, Args&&... args); +// template +// wstring format(wformat_string fmt, Args&&... args); + +#include +#include + +#include "format.functions.tests.h" +#include "test_format_string.h" +#include "test_macros.h" +#include "test_validation.h" + +auto test = []( + std::basic_string_view expected, test_format_string fmt, Args&&... args) { + std::basic_string out = std::format(fmt, std::forward(args)...); + REQUIRE(out == expected, "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'); +}; + +auto test_exception = [](std::string_view, std::basic_string_view, Args&&...) { + // After P2216 most exceptions thrown by std::format become ill-formed. + // Therefore this tests does nothing. +}; + +int main(int, char**) { + format_tests(test, test_exception); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + format_tests(test, test_exception); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.tests.h @@ -0,0 +1,1380 @@ +//===----------------------------------------------------------------------===// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_UTILITIES_FORMAT_FORMAT_RANGE_FORMAT_RANGE_FMTSET_FORMAT_FUNCTIONS_TESTS_H +#define TEST_STD_UTILITIES_FORMAT_FORMAT_RANGE_FORMAT_RANGE_FMTSET_FORMAT_FUNCTIONS_TESTS_H + +#include +#include +#include +#include +#include +#include +#include + +#include "format.functions.common.h" +#include "make_string.h" +#include "platform_support.h" // locale name macros +#include "test_macros.h" + +// +// Char +// + +template +void test_char_default(TestFunction check, ExceptionTest check_exception) { + std::set input{CharT('a'), CharT('c'), CharT('b')}; // input not sorted. + + // Note when no range-underlying-spec is present the char is escaped, + check(SV("{'a', 'b', 'c'}"), SV("{}"), input); + // when one is present there is no escaping, + check(SV("{a, b, c}"), SV("{::}"), input); + // unless forced by the type specifier. + check(SV("{'a', 'b', 'c'}"), SV("{::?}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{'a', 'b', 'c'} "), SV("{:20}"), input); + check(SV("{'a', 'b', 'c'}*****"), SV("{:*<20}"), input); + check(SV("__{'a', 'b', 'c'}___"), SV("{:_^20}"), input); + check(SV("#####{'a', 'b', 'c'}"), SV("{:#>20}"), input); + + check(SV("{'a', 'b', 'c'} "), SV("{:{}}"), input, 20); + check(SV("{'a', 'b', 'c'}*****"), SV("{:*<{}}"), input, 20); + check(SV("__{'a', 'b', 'c'}___"), SV("{:_^{}}"), input, 20); + check(SV("#####{'a', 'b', 'c'}"), SV("{:#>{}}"), input, 20); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__'a', 'b', 'c'___"), SV("{:_^18n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + + // ***** Only underlying has a format-spec + check(SV("{a , b , c }"), SV("{::4}"), input); + check(SV("{a***, b***, c***}"), SV("{::*<4}"), input); + check(SV("{_a__, _b__, _c__}"), SV("{::_^4}"), input); + check(SV("{:::a, :::b, :::c}"), SV("{:::>4}"), input); + + check(SV("{a , b , c }"), SV("{::{}}"), input, 4); + check(SV("{a***, b***, c***}"), SV("{::*<{}}"), input, 4); + check(SV("{_a__, _b__, _c__}"), SV("{::_^{}}"), input, 4); + check(SV("{:::a, :::b, :::c}"), SV("{:::>{}}"), input, 4); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("A sign field isn't allowed in this format-spec", SV("{::-}"), input); + check_exception("A sign field isn't allowed in this format-spec", SV("{::+}"), input); + check_exception("A sign field isn't allowed in this format-spec", SV("{:: }"), input); + + check(SV("{97, 98, 99}"), SV("{::-d}"), input); + check(SV("{+97, +98, +99}"), SV("{::+d}"), input); + check(SV("{ 97, 98, 99}"), SV("{:: d}"), input); + + // *** alternate form *** + check_exception("An alternate form field isn't allowed in this format-spec", SV("{::#}"), input); + + check(SV("{0x61, 0x62, 0x63}"), SV("{::#x}"), input); + + // *** zero-padding *** + check_exception("A zero-padding field isn't allowed in this format-spec", SV("{::05}"), input); + + check(SV("{00141, 00142, 00143}"), SV("{::05o}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); + + // *** locale-specific form *** + + check(SV("{a, b, c}"), SV("{::L}"), input); + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("bBcdoxX?")) + check_exception("The format-spec type has a type not supported for a char argument", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{:a, :b, :c}^^^"), SV("{:^^17::>2}"), input); + check(SV("^^{:a, :b, :c}^^^"), SV("{:^^{}::>2}"), input, 17); + check(SV("^^{:a, :b, :c}^^^"), SV("{:^^{}::>{}}"), input, 17, 2); + + check_exception("Argument index out of bounds", SV("{:^^{}::>2}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 17); +} + +// TODO FMT Add to paper +// [tab:formatter.range.type] +// s T shall be charT. +// This means a set is printed as a sorted string, this seems odd +// Should this be prohibited? How does it behave for map, string, and debug_string? +template +void test_char_string(TestFunction check, [[maybe_unused]] ExceptionTest check_exception) { + std::set input{CharT('a'), CharT('c'), CharT('b')}; // input not sorted. + + check(SV("abc"), SV("{:s}"), input); +} + +// TODO FMT see test_char_string +template +void test_char_escaped_string(TestFunction check, [[maybe_unused]] ExceptionTest check_exception) { + std::set input{CharT('a'), CharT('c'), CharT('b')}; // input not sorted. + + check(SV("\"abc\""), SV("{:?s}"), input); +} + +template +void test_char(TestFunction check, ExceptionTest check_exception) { + test_char_default(check, check_exception); + test_char_string(check, check_exception); + test_char_escaped_string(check, check_exception); +} +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS +// +// char -> wchar_t +// + +template +void test_char_to_wchar(TestFunction check, ExceptionTest check_exception) { + std::set input{'a', 'c', 'b'}; // input not sorted. + + using CharT = wchar_t; + + // Note when no range-underlying-spec is present the char is escaped, + check(SV("{'a', 'b', 'c'}"), SV("{}"), input); + // when one is present there is no escaping, + check(SV("{a, b, c}"), SV("{::}"), input); + // unless forced by the type specifier. + check(SV("{'a', 'b', 'c'}"), SV("{::?}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{'a', 'b', 'c'} "), SV("{:20}"), input); + check(SV("{'a', 'b', 'c'}*****"), SV("{:*<20}"), input); + check(SV("__{'a', 'b', 'c'}___"), SV("{:_^20}"), input); + check(SV("#####{'a', 'b', 'c'}"), SV("{:#>20}"), input); + + check(SV("{'a', 'b', 'c'} "), SV("{:{}}"), input, 20); + check(SV("{'a', 'b', 'c'}*****"), SV("{:*<{}}"), input, 20); + check(SV("__{'a', 'b', 'c'}___"), SV("{:_^{}}"), input, 20); + check(SV("#####{'a', 'b', 'c'}"), SV("{:#>{}}"), input, 20); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__'a', 'b', 'c'___"), SV("{:_^18n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + + // ***** Only underlying has a format-spec + check(SV("{a , b , c }"), SV("{::4}"), input); + check(SV("{a***, b***, c***}"), SV("{::*<4}"), input); + check(SV("{_a__, _b__, _c__}"), SV("{::_^4}"), input); + check(SV("{:::a, :::b, :::c}"), SV("{:::>4}"), input); + + check(SV("{a , b , c }"), SV("{::{}}"), input, 4); + check(SV("{a***, b***, c***}"), SV("{::*<{}}"), input, 4); + check(SV("{_a__, _b__, _c__}"), SV("{::_^{}}"), input, 4); + check(SV("{:::a, :::b, :::c}"), SV("{:::>{}}"), input, 4); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("A sign field isn't allowed in this format-spec", SV("{::-}"), input); + check_exception("A sign field isn't allowed in this format-spec", SV("{::+}"), input); + check_exception("A sign field isn't allowed in this format-spec", SV("{:: }"), input); + + check(SV("{97, 98, 99}"), SV("{::-d}"), input); + check(SV("{+97, +98, +99}"), SV("{::+d}"), input); + check(SV("{ 97, 98, 99}"), SV("{:: d}"), input); + + // *** alternate form *** + check_exception("An alternate form field isn't allowed in this format-spec", SV("{::#}"), input); + + check(SV("{0x61, 0x62, 0x63}"), SV("{::#x}"), input); + + // *** zero-padding *** + check_exception("A zero-padding field isn't allowed in this format-spec", SV("{::05}"), input); + + check(SV("{00141, 00142, 00143}"), SV("{::05o}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); + + // *** locale-specific form *** + + check(SV("{a, b, c}"), SV("{::L}"), input); + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("bBcdoxX?")) + check_exception("The format-spec type has a type not supported for a char argument", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{:a, :b, :c}^^^"), SV("{:^^17::>2}"), input); + check(SV("^^{:a, :b, :c}^^^"), SV("{:^^{}::>2}"), input, 17); + check(SV("^^{:a, :b, :c}^^^"), SV("{:^^{}::>{}}"), input, 17, 2); + + check_exception("Argument index out of bounds", SV("{:^^{}::>2}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 17); +} +#endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS + +// +// Bool +// + +template +void test_bool(TestFunction check, ExceptionTest check_exception) { + std::set input{true, false}; + + check(SV("{false, true}"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{false, true} "), SV("{:18}"), input); + check(SV("{false, true}*****"), SV("{:*<18}"), input); + check(SV("__{false, true}___"), SV("{:_^18}"), input); + check(SV("#####{false, true}"), SV("{:#>18}"), input); + + check(SV("{false, true} "), SV("{:{}}"), input, 18); + check(SV("{false, true}*****"), SV("{:*<{}}"), input, 18); + check(SV("__{false, true}___"), SV("{:_^{}}"), input, 18); + check(SV("#####{false, true}"), SV("{:#>{}}"), input, 18); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__false, true___"), SV("{:_^16n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV("{false , true }"), SV("{::7}"), input); + check(SV("{false**, true***}"), SV("{::*<7}"), input); + check(SV("{_false_, _true__}"), SV("{::_^7}"), input); + check(SV("{::false, :::true}"), SV("{:::>7}"), input); + + check(SV("{false , true }"), SV("{::{}}"), input, 7); + check(SV("{false**, true***}"), SV("{::*<{}}"), input, 7); + check(SV("{_false_, _true__}"), SV("{::_^{}}"), input, 7); + check(SV("{::false, :::true}"), SV("{:::>{}}"), input, 7); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("A sign field isn't allowed in this format-spec", SV("{::-}"), input); + check_exception("A sign field isn't allowed in this format-spec", SV("{::+}"), input); + check_exception("A sign field isn't allowed in this format-spec", SV("{:: }"), input); + + check(SV("{0, 1}"), SV("{::-d}"), input); + check(SV("{+0, +1}"), SV("{::+d}"), input); + check(SV("{ 0, 1}"), SV("{:: d}"), input); + + // *** alternate form *** + check_exception("An alternate form field isn't allowed in this format-spec", SV("{::#}"), input); + + check(SV("{0x0, 0x1}"), SV("{::#x}"), input); + + // *** zero-padding *** + check_exception("A zero-padding field isn't allowed in this format-spec", SV("{::05}"), input); + + check(SV("{00000, 00001}"), SV("{::05o}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); + + // *** locale-specific form *** + + check(SV("{false, true}"), SV("{::L}"), input); + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("bBdosxX")) + check_exception("The format-spec type has a type not supported for a bool argument", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{::false, :::true}^^^"), SV("{:^^23::>7}"), input); + check(SV("^^{::false, :::true}^^^"), SV("{:^^{}::>7}"), input, 23); + check(SV("^^{::false, :::true}^^^"), SV("{:^^{}::>{}}"), input, 23, 7); + + check_exception("Argument index out of bounds", SV("{:^^{}::>5}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 23); +} + +template +void test_bool_multiset(TestFunction check, ExceptionTest check_exception) { + std::multiset> input{true, false, true}; // unordered + + check(SV("{true, true, false}"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{true, true, false} "), SV("{:24}"), input); + check(SV("{true, true, false}*****"), SV("{:*<24}"), input); + check(SV("__{true, true, false}___"), SV("{:_^24}"), input); + check(SV("#####{true, true, false}"), SV("{:#>24}"), input); + + check(SV("{true, true, false} "), SV("{:{}}"), input, 24); + check(SV("{true, true, false}*****"), SV("{:*<{}}"), input, 24); + check(SV("__{true, true, false}___"), SV("{:_^{}}"), input, 24); + check(SV("#####{true, true, false}"), SV("{:#>{}}"), input, 24); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__true, true, false___"), SV("{:_^22n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV("{true , true , false }"), SV("{::7}"), input); + check(SV("{true***, true***, false**}"), SV("{::*<7}"), input); + check(SV("{_true__, _true__, _false_}"), SV("{::_^7}"), input); + check(SV("{:::true, :::true, ::false}"), SV("{:::>7}"), input); + + check(SV("{true , true , false }"), SV("{::{}}"), input, 7); + check(SV("{true***, true***, false**}"), SV("{::*<{}}"), input, 7); + check(SV("{_true__, _true__, _false_}"), SV("{::_^{}}"), input, 7); + check(SV("{:::true, :::true, ::false}"), SV("{:::>{}}"), input, 7); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("A sign field isn't allowed in this format-spec", SV("{::-}"), input); + check_exception("A sign field isn't allowed in this format-spec", SV("{::+}"), input); + check_exception("A sign field isn't allowed in this format-spec", SV("{:: }"), input); + + check(SV("{1, 1, 0}"), SV("{::-d}"), input); + check(SV("{+1, +1, +0}"), SV("{::+d}"), input); + check(SV("{ 1, 1, 0}"), SV("{:: d}"), input); + + // *** alternate form *** + check_exception("An alternate form field isn't allowed in this format-spec", SV("{::#}"), input); + + check(SV("{0x1, 0x1, 0x0}"), SV("{::#x}"), input); + + // *** zero-padding *** + check_exception("A zero-padding field isn't allowed in this format-spec", SV("{::05}"), input); + + check(SV("{00001, 00001, 00000}"), SV("{::05o}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); + + // *** locale-specific form *** + + check(SV("{true, true, false}"), SV("{::L}"), input); + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("bBdosxX")) + check_exception("The format-spec type has a type not supported for a bool argument", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{:::true, :::true, ::false}^^^"), SV("{:^^32::>7}"), input); + check(SV("^^{:::true, :::true, ::false}^^^"), SV("{:^^{}::>7}"), input, 32); + check(SV("^^{:::true, :::true, ::false}^^^"), SV("{:^^{}::>{}}"), input, 32, 7); + + check_exception("Argument index out of bounds", SV("{:^^{}::>5}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 32); +} + +// +// Integral +// + +template +void test_int(TestFunction check, ExceptionTest check_exception, auto&& input) { + check(SV("{-42, 1, 2, 42}"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{-42, 1, 2, 42} "), SV("{:20}"), input); + check(SV("{-42, 1, 2, 42}*****"), SV("{:*<20}"), input); + check(SV("__{-42, 1, 2, 42}___"), SV("{:_^20}"), input); + check(SV("#####{-42, 1, 2, 42}"), SV("{:#>20}"), input); + + check(SV("{-42, 1, 2, 42} "), SV("{:{}}"), input, 20); + check(SV("{-42, 1, 2, 42}*****"), SV("{:*<{}}"), input, 20); + check(SV("__{-42, 1, 2, 42}___"), SV("{:_^{}}"), input, 20); + check(SV("#####{-42, 1, 2, 42}"), SV("{:#>{}}"), input, 20); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__-42, 1, 2, 42___"), SV("{:_^18n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV("{ -42, 1, 2, 42}"), SV("{::5}"), input); + check(SV("{-42**, 1****, 2****, 42***}"), SV("{::*<5}"), input); + check(SV("{_-42_, __1__, __2__, _42__}"), SV("{::_^5}"), input); + check(SV("{::-42, ::::1, ::::2, :::42}"), SV("{:::>5}"), input); + + check(SV("{ -42, 1, 2, 42}"), SV("{::{}}"), input, 5); + check(SV("{-42**, 1****, 2****, 42***}"), SV("{::*<{}}"), input, 5); + check(SV("{_-42_, __1__, __2__, _42__}"), SV("{::_^{}}"), input, 5); + check(SV("{::-42, ::::1, ::::2, :::42}"), SV("{:::>{}}"), input, 5); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check(SV("{-42, 1, 2, 42}"), SV("{::-}"), input); + check(SV("{-42, +1, +2, +42}"), SV("{::+}"), input); + check(SV("{-42, 1, 2, 42}"), SV("{:: }"), input); + + // *** alternate form *** + check(SV("{-0x2a, 0x1, 0x2, 0x2a}"), SV("{::#x}"), input); + + // *** zero-padding *** + check(SV("{-0042, 00001, 00002, 00042}"), SV("{::05}"), input); + check(SV("{-002a, 00001, 00002, 0002a}"), SV("{::05x}"), input); + check(SV("{-0x2a, 0x001, 0x002, 0x02a}"), SV("{::#05x}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); + + // *** locale-specific form *** + + // TODO FMT Need to add vformat.locale.pass.cpp and format.locale.pass.cpp + + check(SV("{-42, 1, 2, 42}"), SV("{::L}"), input); // does nothing in this test, but is accepted. + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("bBcdoxX")) + check_exception("The format-spec type has a type not supported for an integer argument", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{::-42, ::::1, ::::2, :::42}^^^"), SV("{:^^33::>5}"), input); + check(SV("^^{::-42, ::::1, ::::2, :::42}^^^"), SV("{:^^{}::>5}"), input, 33); + check(SV("^^{::-42, ::::1, ::::2, :::42}^^^"), SV("{:^^{}::>{}}"), input, 33, 5); + + check_exception("Argument index out of bounds", SV("{:^^{}::>5}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 33); +} + +template +void test_int(TestFunction check, ExceptionTest check_exception) { + test_int(check, check_exception, std::set{1, 42, 2, -42}); // unsorted + test_int(check, check_exception, std::multiset{1, 42, 2, -42}); // unsorted + +#if 0 + // Test with 1 element, so the order is the same regardless of the implementation + test_int(check, check_exception, std::unordered_set{1, 42, 2, -42}); + test_int(check, check_exception, std::unordered_multiset{1, 2, 3});*/ +#endif +} + +// +// Floating point +// + +template +void test_floating_point(TestFunction check, ExceptionTest check_exception, auto&& input) { + // nan and inf in the set too? + check(SV("{-42.5, 0, 1.25, 42.5}"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{-42.5, 0, 1.25, 42.5} "), SV("{:27}"), input); + check(SV("{-42.5, 0, 1.25, 42.5}*****"), SV("{:*<27}"), input); + check(SV("__{-42.5, 0, 1.25, 42.5}___"), SV("{:_^27}"), input); + check(SV("#####{-42.5, 0, 1.25, 42.5}"), SV("{:#>27}"), input); + + check(SV("{-42.5, 0, 1.25, 42.5} "), SV("{:{}}"), input, 27); + check(SV("{-42.5, 0, 1.25, 42.5}*****"), SV("{:*<{}}"), input, 27); + check(SV("__{-42.5, 0, 1.25, 42.5}___"), SV("{:_^{}}"), input, 27); + check(SV("#####{-42.5, 0, 1.25, 42.5}"), SV("{:#>{}}"), input, 27); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__-42.5, 0, 1.25, 42.5___"), SV("{:_^25n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV("{-42.5, 0, 1.25, 42.5}"), SV("{::5}"), input); + check(SV("{-42.5, 0****, 1.25*, 42.5*}"), SV("{::*<5}"), input); + check(SV("{-42.5, __0__, 1.25_, 42.5_}"), SV("{::_^5}"), input); + check(SV("{-42.5, ::::0, :1.25, :42.5}"), SV("{:::>5}"), input); + + check(SV("{-42.5, 0, 1.25, 42.5}"), SV("{::{}}"), input, 5); + check(SV("{-42.5, 0****, 1.25*, 42.5*}"), SV("{::*<{}}"), input, 5); + check(SV("{-42.5, __0__, 1.25_, 42.5_}"), SV("{::_^{}}"), input, 5); + check(SV("{-42.5, ::::0, :1.25, :42.5}"), SV("{:::>{}}"), input, 5); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check(SV("{-42.5, 0, 1.25, 42.5}"), SV("{::-}"), input); + check(SV("{-42.5, +0, +1.25, +42.5}"), SV("{::+}"), input); + check(SV("{-42.5, 0, 1.25, 42.5}"), SV("{:: }"), input); + + // *** alternate form *** + check(SV("{-42.5, 0., 1.25, 42.5}"), SV("{::#}"), input); + + // *** zero-padding *** + check(SV("{-42.5, 00000, 01.25, 042.5}"), SV("{::05}"), input); + check(SV("{-42.5, 0000., 01.25, 042.5}"), SV("{::#05}"), input); + + // *** precision *** + check(SV("{-42, 0, 1.2, 42}"), SV("{::.2}"), input); + check(SV("{-42.500, 0.000, 1.250, 42.500}"), SV("{::.3f}"), input); + + check(SV("{-42, 0, 1.2, 42}"), SV("{::.{}}"), input, 2); + check(SV("{-42.500, 0.000, 1.250, 42.500}"), SV("{::.{}f}"), input, 3); + + check_exception("The format-spec precision field doesn't contain a value or arg-id", SV("{::.}"), input); + + // *** locale-specific form *** + check(SV("{-42.5, 0, 1.25, 42.5}"), SV("{::L}"), input); // does not require locales present +#ifdef TEST_HAS_NO_LOCALIZATION + std::locale::global(std::locale(LOCALE_fr_FR_UTF_8)); + check(SV("{-42,5, 0, 1,25, 42,5}"), SV("{::L}"), input); + + std::locale::global(std::locale(LOCALE_en_US_UTF_8)); + check(SV("{-42.5, 0, 1.25, 42.5}"), SV("{::L}"), input); + + std::locale::global(std::locale::classic()); +#endif // TEST_HAS_NO_LOCALIZATION + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("aAeEfFgG")) + check_exception("The format-spec type has a type not supported for a floating-point argument", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{-42.5, ::::0, :1.25, :42.5}^^^"), SV("{:^^33::>5}"), input); + check(SV("^^{-42.5, ::::0, :1.25, :42.5}^^^"), SV("{:^^{}::>5}"), input, 33); + check(SV("^^{-42.5, ::::0, :1.25, :42.5}^^^"), SV("{:^^{}::>{}}"), input, 33, 5); + + check(SV("^^{::-42, ::::0, ::1.2, :::42}^^^"), SV("{:^^33::>5.2}"), input); + check(SV("^^{::-42, ::::0, ::1.2, :::42}^^^"), SV("{:^^{}::>5.2}"), input, 33); + check(SV("^^{::-42, ::::0, ::1.2, :::42}^^^"), SV("{:^^{}::>{}.2}"), input, 33, 5); + check(SV("^^{::-42, ::::0, ::1.2, :::42}^^^"), SV("{:^^{}::>{}.{}}"), input, 33, 5, 2); + + check_exception("Argument index out of bounds", SV("{:^^{}::>5.2}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}.2}"), input, 33); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}.{}}"), input, 33, 5); +} + +template +void test_floating_point(TestFunction check, ExceptionTest check_exception) { + test_floating_point(check, check_exception, std::set{-42.5f, 0.0f, 1.25f, 42.5f}); + test_floating_point(check, check_exception, std::multiset{-42.5, 0.0, 1.25, 42.5}); + test_floating_point(check, check_exception, std::set{-42.5l, 0.0l, 1.25l, 42.5l}); +} + +// +// Pointer +// + +template +void test_pointer(TestFunction check, ExceptionTest check_exception, auto&& input) { + check(SV("{0x0}"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{0x0} "), SV("{:10}"), input); + check(SV("{0x0}*****"), SV("{:*<10}"), input); + check(SV("__{0x0}___"), SV("{:_^10}"), input); + check(SV("#####{0x0}"), SV("{:#>10}"), input); + + check(SV("{0x0} "), SV("{:{}}"), input, 10); + check(SV("{0x0}*****"), SV("{:*<{}}"), input, 10); + check(SV("__{0x0}___"), SV("{:_^{}}"), input, 10); + check(SV("#####{0x0}"), SV("{:#>{}}"), input, 10); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("_0x0_"), SV("{:_^5n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV("{ 0x0}"), SV("{::5}"), input); + check(SV("{0x0**}"), SV("{::*<5}"), input); + check(SV("{_0x0_}"), SV("{::_^5}"), input); + check(SV("{::0x0}"), SV("{:::>5}"), input); + + check(SV("{ 0x0}"), SV("{::{}}"), input, 5); + check(SV("{0x0**}"), SV("{::*<{}}"), input, 5); + check(SV("{_0x0_}"), SV("{::_^{}}"), input, 5); + check(SV("{::0x0}"), SV("{:::>{}}"), input, 5); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::-}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("p")) + check_exception("The format-spec type has a type not supported for a pointer argument", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{::0x0}^^^"), SV("{:^^12::>5}"), input); + check(SV("^^{::0x0}^^^"), SV("{:^^{}::>5}"), input, 12); + check(SV("^^{::0x0}^^^"), SV("{:^^{}::>{}}"), input, 12, 5); + + check(SV("^^{::0x0}^^^"), SV("{:^^12::>5}"), input); + check(SV("^^{::0x0}^^^"), SV("{:^^{}::>5}"), input, 12); + check(SV("^^{::0x0}^^^"), SV("{:^^{}::>{}}"), input, 12, 5); + + check_exception("Argument index out of bounds", SV("{:^^{}::>5}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 12); +} + +template +void test_pointer(TestFunction check, ExceptionTest check_exception) { + // Note nullptr_t can only be equality compared so not used in a set. + test_pointer(check, check_exception, std::unordered_set{static_cast(0)}); + test_pointer(check, check_exception, std::unordered_multiset{static_cast(0)}); +} + +// +// String +// + +template +void test_string(TestFunction check, ExceptionTest check_exception, auto&& input) { + check(SV(R"({"Hello", "world"})"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV(R"({"Hello", "world"} )"), SV("{:23}"), input); + check(SV(R"({"Hello", "world"}*****)"), SV("{:*<23}"), input); + check(SV(R"(__{"Hello", "world"}___)"), SV("{:_^23}"), input); + check(SV(R"(#####{"Hello", "world"})"), SV("{:#>23}"), input); + + check(SV(R"({"Hello", "world"} )"), SV("{:{}}"), input, 23); + check(SV(R"({"Hello", "world"}*****)"), SV("{:*<{}}"), input, 23); + check(SV(R"(__{"Hello", "world"}___)"), SV("{:_^{}}"), input, 23); + check(SV(R"(#####{"Hello", "world"})"), SV("{:#>{}}"), input, 23); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV(R"(_"Hello", "world"_)"), SV("{:_^18n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV(R"({Hello , world })"), SV("{::8}"), input); + check(SV(R"({Hello***, world***})"), SV("{::*<8}"), input); + check(SV(R"({_Hello__, _world__})"), SV("{::_^8}"), input); + check(SV(R"({:::Hello, :::world})"), SV("{:::>8}"), input); + + check(SV(R"({Hello , world })"), SV("{::{}}"), input, 8); + check(SV(R"({Hello***, world***})"), SV("{::*<{}}"), input, 8); + check(SV(R"({_Hello__, _world__})"), SV("{::_^{}}"), input, 8); + check(SV(R"({:::Hello, :::world})"), SV("{:::>{}}"), input, 8); + + check_exception("The format-spec fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::-}"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + + // *** precision *** + check(SV(R"({Hel, wor})"), SV("{::.3}"), input); + + check(SV(R"({Hel, wor})"), SV("{::.{}}"), input, 3); + + check_exception("The format-spec precision field doesn't contain a value or arg-id", SV("{::.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); + + // *** type *** + for (std::basic_string_view fmt : fmt_invalid_nested_types("s?")) + check_exception("The format-spec type has a type not supported for a string argument", fmt, input); + + // ***** Both have a format-spec + check(SV(R"(^^{:::Hello, :::world}^^^)"), SV("{:^^25::>8}"), input); + check(SV(R"(^^{:::Hello, :::world}^^^)"), SV("{:^^{}::>8}"), input, 25); + check(SV(R"(^^{:::Hello, :::world}^^^)"), SV("{:^^{}::>{}}"), input, 25, 8); + + check(SV(R"(^^{:::Hello, :::world}^^^)"), SV("{:^^25::>8}"), input); + check(SV(R"(^^{:::Hello, :::world}^^^)"), SV("{:^^{}::>8}"), input, 25); + check(SV(R"(^^{:::Hello, :::world}^^^)"), SV("{:^^{}::>{}}"), input, 25, 8); + + check_exception("Argument index out of bounds", SV("{:^^{}::>8}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}::>{}}"), input, 25); +} + +template +void test_string(TestFunction check, ExceptionTest check_exception) { + test_string(check, check_exception, std::set{STR("Hello"), STR("world")}); + test_string(check, check_exception, std::set{SV("Hello"), SV("world")}); +} + +// +// Handle +// + +template +void test_status(TestFunction check, ExceptionTest check_exception) { + std::set input{status::foo, status::bar, status::foobar}; // unordered input + + check(SV("{0x5555, 0xaa55, 0xaaaa}"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{0x5555, 0xaa55, 0xaaaa} "), SV("{:29}"), input); + check(SV("{0x5555, 0xaa55, 0xaaaa}*****"), SV("{:*<29}"), input); + check(SV("__{0x5555, 0xaa55, 0xaaaa}___"), SV("{:_^29}"), input); + check(SV("#####{0x5555, 0xaa55, 0xaaaa}"), SV("{:#>29}"), input); + + check(SV("{0x5555, 0xaa55, 0xaaaa} "), SV("{:{}}"), input, 29); + check(SV("{0x5555, 0xaa55, 0xaaaa}*****"), SV("{:*<{}}"), input, 29); + check(SV("__{0x5555, 0xaa55, 0xaaaa}___"), SV("{:_^{}}"), input, 29); + check(SV("#####{0x5555, 0xaa55, 0xaaaa}"), SV("{:#>{}}"), input, 29); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__0x5555, 0xaa55, 0xaaaa___"), SV("{:_^27n}"), input); + + // *** type *** + check_exception("The range-format-spec type m requires two elements for a pair or tuple", SV("{:m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check_exception("The format-spec type has a type not supported for a status argument", SV("{::*<7}"), input); + + check(SV("{0x5555, 0xaa55, 0xaaaa}"), SV("{::x}"), input); + check(SV("{0X5555, 0XAA55, 0XAAAA}"), SV("{::X}"), input); + check(SV("{bar, foobar, foo}"), SV("{::s}"), input); + + // ***** Both have a format-spec + check(SV("^^{0X5555, 0XAA55, 0XAAAA}^^^"), SV("{:^^29:X}"), input); + check(SV("^^{0X5555, 0XAA55, 0XAAAA}^^^"), SV("{:^^{}:X}"), input, 29); + + check_exception("Argument index out of bounds", SV("{:^^{}:X}"), input); +} + +// +// Pair +// + +template +void test_pair_tuple(TestFunction check, ExceptionTest check_exception, auto&& input) { + // [format.range.formatter]/3 + // For range_formatter, the format-spec in a + // range-underlying-spec, if any, is interpreted by formatter. + // + // template + // constexpr typename ParseContext::iterator + // parse(ParseContext& ctx); + // [format.tuple]/7 + // ... if e.set_debug_format() is a valid expression, calls + // e.set_debug_format(). + // So when there is no range-underlying-spec, there is no need to call parse + // thus the char element is not escaped. + // TODO FMT document this + check(SV("{(1, a), (42, *)}"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{(1, a), (42, *)} "), SV("{:22}"), input); + check(SV("{(1, a), (42, *)}*****"), SV("{:*<22}"), input); + check(SV("__{(1, a), (42, *)}___"), SV("{:_^22}"), input); + check(SV("#####{(1, a), (42, *)}"), SV("{:#>22}"), input); + + check(SV("{(1, a), (42, *)} "), SV("{:{}}"), input, 22); + check(SV("{(1, a), (42, *)}*****"), SV("{:*<{}}"), input, 22); + check(SV("__{(1, a), (42, *)}___"), SV("{:_^{}}"), input, 22); + check(SV("#####{(1, a), (42, *)}"), SV("{:#>{}}"), input, 22); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__(1, a), (42, *)___"), SV("{:_^20n}"), input); + check(SV("__(1, a), (42, *)___"), SV("{:_^20nm}"), input); // m should have no effect + + // *** type *** + check(SV("__{(1, a), (42, *)}___"), SV("{:_^22m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV("{(1, 'a') , (42, '*') }"), SV("{::11}"), input); + check(SV("{(1, 'a')***, (42, '*')**}"), SV("{::*<11}"), input); + check(SV("{_(1, 'a')__, _(42, '*')_}"), SV("{::_^11}"), input); + check(SV("{###(1, 'a'), ##(42, '*')}"), SV("{::#>11}"), input); + + check(SV("{(1, 'a') , (42, '*') }"), SV("{::{}}"), input, 11); + check(SV("{(1, 'a')***, (42, '*')**}"), SV("{::*<{}}"), input, 11); + check(SV("{_(1, 'a')__, _(42, '*')_}"), SV("{::_^{}}"), input, 11); + check(SV("{###(1, 'a'), ##(42, '*')}"), SV("{::#>{}}"), input, 11); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:::<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{::+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); + + // *** locale-specific form *** + + check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); + + // *** type *** + check(SV("{1: 'a', 42: '*'}"), SV("{::m}"), input); + check(SV("{1, 'a', 42, '*'}"), SV("{::n}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{::s}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{::?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_nested_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{###(1, 'a'), ##(42, '*')}^^^"), SV("{:^^31:#>11}"), input); + check(SV("^^{###(1, 'a'), ##(42, '*')}^^^"), SV("{:^^31:#>11}"), input); + check(SV("^^{###(1, 'a'), ##(42, '*')}^^^"), SV("{:^^{}:#>11}"), input, 31); + check(SV("^^{###(1, 'a'), ##(42, '*')}^^^"), SV("{:^^{}:#>{}}"), input, 31, 11); + + check_exception("Argument index out of bounds", SV("{:^^{}:#>5}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}:#>{}}"), input, 31); + + check(SV("1: 'a', 42: '*'"), SV("{:n:m}"), input); + check(SV("1, 'a', 42, '*'"), SV("{:n:n}"), input); + check(SV("{1: 'a', 42: '*'}"), SV("{:m:m}"), input); + check(SV("{1, 'a', 42, '*'}"), SV("{:m:n}"), input); +} + +template +void test_pair_tuple(TestFunction check, ExceptionTest check_exception) { + test_pair_tuple( + check, check_exception, std::set{std::make_pair(1, CharT('a')), std::make_pair(42, CharT('*'))}); + test_pair_tuple( + check, check_exception, std::set{std::make_tuple(1, CharT('a')), std::make_tuple(42, CharT('*'))}); +} + +// +// Tuple 1 +// + +template +void test_tuple_int(TestFunction check, ExceptionTest check_exception) { + std::set input{std::make_tuple(42), std::make_tuple(99)}; + + check(SV("{(42), (99)}"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{(42), (99)} "), SV("{:17}"), input); + check(SV("{(42), (99)}*****"), SV("{:*<17}"), input); + check(SV("__{(42), (99)}___"), SV("{:_^17}"), input); + check(SV("#####{(42), (99)}"), SV("{:#>17}"), input); + + check(SV("{(42), (99)} "), SV("{:{}}"), input, 17); + check(SV("{(42), (99)}*****"), SV("{:*<{}}"), input, 17); + check(SV("__{(42), (99)}___"), SV("{:_^{}}"), input, 17); + check(SV("#####{(42), (99)}"), SV("{:#>{}}"), input, 17); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__(42), (99)___"), SV("{:_^15n}"), input); + + // *** type *** + check(SV("__{(42), (99)}___"), SV("{:_^17m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV("{(42) , (99) }"), SV("{::7}"), input); + check(SV("{(42)***, (99)***}"), SV("{::*<7}"), input); + check(SV("{_(42)__, _(99)__}"), SV("{::_^7}"), input); + check(SV("{###(42), ###(99)}"), SV("{::#>7}"), input); + + check(SV("{(42) , (99) }"), SV("{::{}}"), input, 7); + check(SV("{(42)***, (99)***}"), SV("{::*<{}}"), input, 7); + check(SV("{_(42)__, _(99)__}"), SV("{::_^{}}"), input, 7); + check(SV("{###(42), ###(99)}"), SV("{::#>{}}"), input, 7); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:::<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{::+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); + + // *** locale-specific form *** + + check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); + + // *** type *** + check(SV("{42, 99}"), SV("{::n}"), input); + check_exception("The format specifier m requires a pair or a two-element tuple", SV("{::m}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{::s}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{::?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_nested_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{###(42), ###(99)}^^^"), SV("{:^^23:#>7}"), input); + check(SV("^^{###(42), ###(99)}^^^"), SV("{:^^23:#>7}"), input); + check(SV("^^{###(42), ###(99)}^^^"), SV("{:^^{}:#>7}"), input, 23); + check(SV("^^{###(42), ###(99)}^^^"), SV("{:^^{}:#>{}}"), input, 23, 7); + + check_exception("Argument index out of bounds", SV("{:^^{}:#>5}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}:#>{}}"), input, 23); +} + +// +// Tuple 3 +// + +template +void test_tuple_int_int_int(TestFunction check, ExceptionTest check_exception) { + std::set input{std::make_tuple(42, 99, 0), std::make_tuple(1, 10, 100)}; // unordered + + check(SV("{(1, 10, 100), (42, 99, 0)}"), SV("{}"), input); + + // ***** underlying has no format-spec + + // *** align-fill & width *** + check(SV("{(1, 10, 100), (42, 99, 0)} "), SV("{:32}"), input); + check(SV("{(1, 10, 100), (42, 99, 0)}*****"), SV("{:*<32}"), input); + check(SV("__{(1, 10, 100), (42, 99, 0)}___"), SV("{:_^32}"), input); + check(SV("#####{(1, 10, 100), (42, 99, 0)}"), SV("{:#>32}"), input); + + check(SV("{(1, 10, 100), (42, 99, 0)} "), SV("{:{}}"), input, 32); + check(SV("{(1, 10, 100), (42, 99, 0)}*****"), SV("{:*<{}}"), input, 32); + check(SV("__{(1, 10, 100), (42, 99, 0)}___"), SV("{:_^{}}"), input, 32); + check(SV("#####{(1, 10, 100), (42, 99, 0)}"), SV("{:#>{}}"), input, 32); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{:{<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{:0}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:.}"), input); + + // *** locale-specific form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{:L}"), input); + + // *** n + check(SV("__(1, 10, 100), (42, 99, 0)___"), SV("{:_^30n}"), input); + + // *** type *** + check(SV("__{(1, 10, 100), (42, 99, 0)}___"), SV("{:_^32m}"), input); + check_exception("The range-format-spec type s requires formatting a character type", SV("{:s}"), input); + check_exception("The range-format-spec type ?s requires formatting a character type", SV("{:?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Only underlying has a format-spec + check(SV("{(1, 10, 100) , (42, 99, 0) }"), SV("{::14}"), input); + check(SV("{(1, 10, 100)**, (42, 99, 0)***}"), SV("{::*<14}"), input); + check(SV("{_(1, 10, 100)_, _(42, 99, 0)__}"), SV("{::_^14}"), input); + check(SV("{##(1, 10, 100), ###(42, 99, 0)}"), SV("{::#>14}"), input); + + check(SV("{(1, 10, 100) , (42, 99, 0) }"), SV("{::{}}"), input, 14); + check(SV("{(1, 10, 100)**, (42, 99, 0)***}"), SV("{::*<{}}"), input, 14); + check(SV("{_(1, 10, 100)_, _(42, 99, 0)__}"), SV("{::_^{}}"), input, 14); + check(SV("{##(1, 10, 100), ###(42, 99, 0)}"), SV("{::#>{}}"), input, 14); + + check_exception("The format-spec range-fill field contains an invalid character", SV("{:::<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::}<}"), input); + check_exception("The format-spec range-fill field contains an invalid character", SV("{::{<}"), input); + + // *** sign *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::-}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{::+}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{:: }"), input); + + // *** alternate form *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::#}"), input); + + // *** zero-padding *** + check_exception("A format-spec width field shouldn't have a leading zero", SV("{::05}"), input); + + // *** precision *** + check_exception("The format-spec should consume the input or end with a '}'", SV("{::.}"), input); + + // *** locale-specific form *** + + check_exception("The format-spec should consume the input or end with a '}'", SV("{::L}"), input); + + // *** type *** + check(SV("{1, 10, 100, 42, 99, 0}"), SV("{::n}"), input); + check_exception("The format specifier m requires a pair or a two-element tuple", SV("{::m}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{::s}"), input); + check_exception("The format-spec should consume the input or end with a '}'", SV("{::?s}"), input); + + for (std::basic_string_view fmt : fmt_invalid_nested_types("s")) + check_exception("The format-spec should consume the input or end with a '}'", fmt, input); + + // ***** Both have a format-spec + check(SV("^^{##(1, 10, 100), ###(42, 99, 0)}^^^"), SV("{:^^37:#>14}"), input); + check(SV("^^{##(1, 10, 100), ###(42, 99, 0)}^^^"), SV("{:^^37:#>14}"), input); + check(SV("^^{##(1, 10, 100), ###(42, 99, 0)}^^^"), SV("{:^^{}:#>14}"), input, 37); + check(SV("^^{##(1, 10, 100), ###(42, 99, 0)}^^^"), SV("{:^^{}:#>{}}"), input, 37, 14); + + check_exception("Argument index out of bounds", SV("{:^^{}:#>5}"), input); + check_exception("Argument index out of bounds", SV("{:^^{}:#>{}}"), input, 37); +} + +// +// Driver +// + +template +void format_tests(TestFunction check, ExceptionTest check_exception) { + // A generator produes a real input range. + //test_generator(check, check_exception); + + // XXX test empty set + + test_char(check, check_exception); + +#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS + if (std::same_as) // avoid testing twice + test_char_to_wchar(check, check_exception); +#endif + test_bool(check, check_exception); + test_bool_multiset(check, check_exception); + test_int(check, check_exception); + test_floating_point(check, check_exception); + test_pointer(check, check_exception); + test_string(check, check_exception); + + test_status(check, check_exception); // Has its own handler with its own parser + + test_pair_tuple(check, check_exception); + test_tuple_int(check, check_exception); + test_tuple_int_int_int(check, check_exception); +} + +#endif // TEST_STD_UTILITIES_FORMAT_FORMAT_RANGE_FORMAT_RANGE_FMTSET_FORMAT_FUNCTIONS_TESTS_H diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.vformat.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.vformat.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.functions.vformat.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// 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 Fix this test using GCC, it currently times out. +// UNSUPPORTED: gcc-12 + +// This test requires the dylib support introduced in D92214. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}} +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}} + +// + +// template +// struct range-default-formatter +// +// string vformat(string_view fmt, format_args args); +// wstring vformat(wstring_view fmt, wformat_args args); + +#include +#include + +#include "format.functions.tests.h" +#include "test_macros.h" +#include "test_validation.h" + +auto test = []( + std::basic_string_view expected, std::basic_string_view fmt, Args&&... args) { + std::basic_string out = std::vformat(fmt, std::make_format_args>(args...)); + REQUIRE(out == expected, "\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'); +}; + +auto test_exception = + []( + [[maybe_unused]] std::string_view what, + [[maybe_unused]] std::basic_string_view fmt, + [[maybe_unused]] Args&&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + TEST_IGNORE_NODISCARD std::vformat(fmt, std::make_format_args>(args...)); + FAIL("\nFormat string ", fmt, "\nDidn't throw an exception.\n"); + } catch (const std::format_error& e) { + LIBCPP_REQUIRE( + e.what() == what, + "\nFormat string ", + fmt, + "\nExpected exception ", + what, + "\nActual exception ", + e.what(), + '\n'); + + return; + } + assert(false); +#endif + }; + +int main(int, char**) { + format_tests(test, test_exception); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + format_tests(test, test_exception); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/format.pass.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// 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 + +// This test requires the dylib support introduced in D92214. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}} +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}} + +// + +// template +// struct range-default-formatter + +// template +// typename FormatContext::iterator +// format(const T& ref, FormatContext& ctx) const; + +// Note this tests the basics of this function. It's tested in more detail in +// the format.functions test. + +#include +#include +#include +#include + +#include "test_format_context.h" +#include "test_macros.h" +#include "make_string.h" + +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +template +void test_format(StringViewT expected, std::set arg) { + using CharT = typename StringViewT::value_type; + using String = std::basic_string; + using OutIt = std::back_insert_iterator; + using FormatCtxT = std::basic_format_context; + + std::formatter, CharT> formatter; + + String result; + OutIt out = std::back_inserter(result); + FormatCtxT format_ctx = test_format_context_create(out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + assert(result == expected); +} + +template +void test_fmt() { + test_format(SV("{42}"), std::set{42}); +} + +void test() { + test_fmt(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_fmt(); +#endif +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtset/parse.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/parse.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtset/parse.pass.cpp @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// 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 + +// This test requires the dylib support introduced in D92214. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{.+}} +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx11.{{.+}} + +// + +// template +// struct range-default-formatter + +// template +// constexpr typename ParseContext::iterator +// parse(ParseContext& ctx); + +// Note this tests the basics of this function. It's tested in more detail in +// the format.functions test. + +#include +#include +#include +#include + +#include "test_format_context.h" +#include "test_macros.h" +#include "make_string.h" + +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +template +constexpr void test_parse(StringViewT fmt) { + using CharT = typename StringViewT::value_type; + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter, CharT> formatter; + static_assert(std::semiregular); + + std::same_as auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); +} + +template +constexpr void test_fmt() { + test_parse(SV("")); + test_parse(SV(":5")); + + test_parse(SV("}")); + test_parse(SV(":5}")); +} + +constexpr bool test() { + test_fmt(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_fmt(); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +}