375 lines · c
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef TEST_STD_UTILITIES_FORMAT_FORMAT_TUPLE_FORMAT_TESTS_H10#define TEST_STD_UTILITIES_FORMAT_FORMAT_TUPLE_FORMAT_TESTS_H11 12#include <concepts>13#include <format>14#include <tuple>15 16#include "format.functions.common.h"17 18enum class color { black, red, gold };19 20template <class CharT>21struct std::formatter<color, CharT> : std::formatter<basic_string_view<CharT>, CharT> {22 static constexpr basic_string_view<CharT> color_names[] = {SV("black"), SV("red"), SV("gold")};23 auto format(color c, auto& ctx) const {24 return formatter<basic_string_view<CharT>, CharT>::format(color_names[static_cast<int>(c)], ctx);25 }26};27 28//29// Generic tests for a tuple and pair with two elements.30//31template <class CharT, class TestFunction, class ExceptionTest, class TupleOrPair>32void test_tuple_or_pair_int_int(TestFunction check, ExceptionTest check_exception, TupleOrPair&& input) {33 check(SV("(42, 99)"), SV("{}"), input);34 check(SV("(42, 99)^42"), SV("{}^42"), input);35 check(SV("(42, 99)^42"), SV("{:}^42"), input);36 37 // *** align-fill & width ***38 check(SV("(42, 99) "), SV("{:13}"), input);39 check(SV("(42, 99)*****"), SV("{:*<13}"), input);40 check(SV("__(42, 99)___"), SV("{:_^13}"), input);41 check(SV("#####(42, 99)"), SV("{:#>13}"), input);42 43 check(SV("(42, 99) "), SV("{:{}}"), input, 13);44 check(SV("(42, 99)*****"), SV("{:*<{}}"), input, 13);45 check(SV("__(42, 99)___"), SV("{:_^{}}"), input, 13);46 check(SV("#####(42, 99)"), SV("{:#>{}}"), input, 13);47 48 check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);49 check_exception("The fill option contains an invalid value", SV("{:{<}"), input);50 51 // *** sign ***52 check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);53 check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);54 check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);55 56 // *** alternate form ***57 check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);58 59 // *** zero-padding ***60 check_exception("The width option should not have a leading zero", SV("{:0}"), input);61 62 // *** precision ***63 check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);64 65 // *** locale-specific form ***66 check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);67 68 // *** type ***69 check(SV("__42: 99___"), SV("{:_^11m}"), input);70 check(SV("__42, 99___"), SV("{:_^11n}"), input);71 72 for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {73 check_exception("The format specifier should consume the input or end with a '}'",74 std::basic_string_view{STR("{:") + c + STR("}")},75 input);76 }77}78 79template <class CharT, class TestFunction, class ExceptionTest, class TupleOrPair>80void test_tuple_or_pair_int_string(TestFunction check, ExceptionTest check_exception, TupleOrPair&& input) {81 check(SV("(42, \"hello\")"), SV("{}"), input);82 check(SV("(42, \"hello\")^42"), SV("{}^42"), input);83 check(SV("(42, \"hello\")^42"), SV("{:}^42"), input);84 85 // *** align-fill & width ***86 check(SV("(42, \"hello\") "), SV("{:18}"), input);87 check(SV("(42, \"hello\")*****"), SV("{:*<18}"), input);88 check(SV("__(42, \"hello\")___"), SV("{:_^18}"), input);89 check(SV("#####(42, \"hello\")"), SV("{:#>18}"), input);90 91 check(SV("(42, \"hello\") "), SV("{:{}}"), input, 18);92 check(SV("(42, \"hello\")*****"), SV("{:*<{}}"), input, 18);93 check(SV("__(42, \"hello\")___"), SV("{:_^{}}"), input, 18);94 check(SV("#####(42, \"hello\")"), SV("{:#>{}}"), input, 18);95 96 check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);97 check_exception("The fill option contains an invalid value", SV("{:{<}"), input);98 99 // *** sign ***100 check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);101 check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);102 check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);103 104 // *** alternate form ***105 check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);106 107 // *** zero-padding ***108 check_exception("The width option should not have a leading zero", SV("{:0}"), input);109 110 // *** precision ***111 check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);112 113 // *** locale-specific form ***114 check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);115 116 // *** type ***117 check(SV("__42: \"hello\"___"), SV("{:_^16m}"), input);118 check(SV("__42, \"hello\"___"), SV("{:_^16n}"), input);119 120 for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {121 check_exception("The format specifier should consume the input or end with a '}'",122 std::basic_string_view{STR("{:") + c + STR("}")},123 input);124 }125}126 127template <class CharT, class TestFunction, class TupleOrPair>128void test_escaping(TestFunction check, TupleOrPair&& input) {129 static_assert(std::same_as<std::remove_cvref_t<decltype(std::get<0>(input))>, CharT>);130 static_assert(std::same_as<std::remove_cvref_t<decltype(std::get<1>(input))>, std::basic_string<CharT>>);131 132 check(SV(R"(('*', ""))"), SV("{}"), input);133 134 // Char135 std::get<0>(input) = CharT('\t');136 check(SV(R"(('\t', ""))"), SV("{}"), input);137 std::get<0>(input) = CharT('\n');138 check(SV(R"(('\n', ""))"), SV("{}"), input);139 std::get<0>(input) = CharT('\0');140 check(SV(R"(('\u{0}', ""))"), SV("{}"), input);141 142 // String143 std::get<0>(input) = CharT('*');144 std::get<1>(input) = SV("hellö");145 check(SV("('*', \"hellö\")"), SV("{}"), input);146}147 148//149// pair tests150//151 152template <class CharT, class TestFunction, class ExceptionTest>153void test_pair_int_int(TestFunction check, ExceptionTest check_exception) {154 test_tuple_or_pair_int_int<CharT>(check, check_exception, std::make_pair(42, 99));155}156 157template <class CharT, class TestFunction, class ExceptionTest>158void test_pair_int_string(TestFunction check, ExceptionTest check_exception) {159 test_tuple_or_pair_int_string<CharT>(check, check_exception, std::make_pair(42, SV("hello")));160 test_tuple_or_pair_int_string<CharT>(check, check_exception, std::make_pair(42, STR("hello")));161 test_tuple_or_pair_int_string<CharT>(check, check_exception, std::make_pair(42, CSTR("hello")));162}163 164//165// tuple tests166//167 168template <class CharT, class TestFunction, class ExceptionTest>169void test_tuple_int(TestFunction check, ExceptionTest check_exception) {170 auto input = std::make_tuple(42);171 172 check(SV("(42)"), SV("{}"), input);173 check(SV("(42)^42"), SV("{}^42"), input);174 check(SV("(42)^42"), SV("{:}^42"), input);175 176 // *** align-fill & width ***177 check(SV("(42) "), SV("{:9}"), input);178 check(SV("(42)*****"), SV("{:*<9}"), input);179 check(SV("__(42)___"), SV("{:_^9}"), input);180 check(SV("#####(42)"), SV("{:#>9}"), input);181 182 check(SV("(42) "), SV("{:{}}"), input, 9);183 check(SV("(42)*****"), SV("{:*<{}}"), input, 9);184 check(SV("__(42)___"), SV("{:_^{}}"), input, 9);185 check(SV("#####(42)"), SV("{:#>{}}"), input, 9);186 187 check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);188 check_exception("The fill option contains an invalid value", SV("{:{<}"), input);189 190 // *** sign ***191 check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);192 check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);193 check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);194 195 // *** alternate form ***196 check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);197 198 // *** zero-padding ***199 check_exception("The width option should not have a leading zero", SV("{:0}"), input);200 201 // *** precision ***202 check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);203 204 // *** locale-specific form ***205 check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);206 207 // *** type ***208 check_exception("Type m requires a pair or a tuple with two elements", SV("{:m}"), input);209 check(SV("__42___"), SV("{:_^7n}"), input);210 211 for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {212 check_exception("The format specifier should consume the input or end with a '}'",213 std::basic_string_view{STR("{:") + c + STR("}")},214 input);215 }216}217 218template <class CharT, class TestFunction, class ExceptionTest>219void test_tuple_int_string_color(TestFunction check, ExceptionTest check_exception) {220 const auto input = std::make_tuple(42, SV("hello"), color::red);221 222 check(SV("(42, \"hello\", \"red\")"), SV("{}"), input);223 check(SV("(42, \"hello\", \"red\")^42"), SV("{}^42"), input);224 check(SV("(42, \"hello\", \"red\")^42"), SV("{:}^42"), input);225 226 // *** align-fill & width ***227 check(SV("(42, \"hello\", \"red\") "), SV("{:25}"), input);228 check(SV("(42, \"hello\", \"red\")*****"), SV("{:*<25}"), input);229 check(SV("__(42, \"hello\", \"red\")___"), SV("{:_^25}"), input);230 check(SV("#####(42, \"hello\", \"red\")"), SV("{:#>25}"), input);231 232 check(SV("(42, \"hello\", \"red\") "), SV("{:{}}"), input, 25);233 check(SV("(42, \"hello\", \"red\")*****"), SV("{:*<{}}"), input, 25);234 check(SV("__(42, \"hello\", \"red\")___"), SV("{:_^{}}"), input, 25);235 check(SV("#####(42, \"hello\", \"red\")"), SV("{:#>{}}"), input, 25);236 237 check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);238 check_exception("The fill option contains an invalid value", SV("{:{<}"), input);239 240 // *** sign ***241 check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);242 check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);243 check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);244 245 // *** alternate form ***246 check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);247 248 // *** zero-padding ***249 check_exception("The width option should not have a leading zero", SV("{:0}"), input);250 251 // *** precision ***252 check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);253 254 // *** locale-specific form ***255 check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);256 257 // *** type ***258 check_exception("Type m requires a pair or a tuple with two elements", SV("{:m}"), input);259 check(SV("__42, \"hello\", \"red\"___"), SV("{:_^23n}"), input);260 261 for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {262 check_exception("The format specifier should consume the input or end with a '}'",263 std::basic_string_view{STR("{:") + c + STR("}")},264 input);265 }266}267 268template <class CharT, class TestFunction, class ExceptionTest>269void test_tuple_int_int(TestFunction check, ExceptionTest check_exception) {270 test_tuple_or_pair_int_int<CharT>(check, check_exception, std::make_tuple(42, 99));271}272 273template <class CharT, class TestFunction, class ExceptionTest>274void test_tuple_int_string(TestFunction check, ExceptionTest check_exception) {275 test_tuple_or_pair_int_string<CharT>(check, check_exception, std::make_tuple(42, SV("hello")));276 test_tuple_or_pair_int_string<CharT>(check, check_exception, std::make_tuple(42, STR("hello")));277 test_tuple_or_pair_int_string<CharT>(check, check_exception, std::make_tuple(42, CSTR("hello")));278}279 280//281// nested tests282//283 284template <class CharT, class TestFunction, class ExceptionTest, class Nested>285void test_nested(TestFunction check, ExceptionTest check_exception, Nested&& input) {286 // [format.formatter.spec]/2287 // A debug-enabled specialization of formatter additionally provides a288 // public, constexpr, non-static member function set_debug_format()289 // which modifies the state of the formatter to be as if the type of the290 // std-format-spec parsed by the last call to parse were ?.291 // pair and tuple are not debug-enabled specializations to the292 // set_debug_format is not propagated. The paper293 // P2733 Fix handling of empty specifiers in std::format294 // addressed this.295 296 check(SV("(42, (\"hello\", \"red\"))"), SV("{}"), input);297 check(SV("(42, (\"hello\", \"red\"))^42"), SV("{}^42"), input);298 check(SV("(42, (\"hello\", \"red\"))^42"), SV("{:}^42"), input);299 300 // *** align-fill & width ***301 check(SV("(42, (\"hello\", \"red\")) "), SV("{:27}"), input);302 check(SV("(42, (\"hello\", \"red\"))*****"), SV("{:*<27}"), input);303 check(SV("__(42, (\"hello\", \"red\"))___"), SV("{:_^27}"), input);304 check(SV("#####(42, (\"hello\", \"red\"))"), SV("{:#>27}"), input);305 306 check(SV("(42, (\"hello\", \"red\")) "), SV("{:{}}"), input, 27);307 check(SV("(42, (\"hello\", \"red\"))*****"), SV("{:*<{}}"), input, 27);308 check(SV("__(42, (\"hello\", \"red\"))___"), SV("{:_^{}}"), input, 27);309 check(SV("#####(42, (\"hello\", \"red\"))"), SV("{:#>{}}"), input, 27);310 311 check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);312 check_exception("The fill option contains an invalid value", SV("{:{<}"), input);313 314 // *** sign ***315 check_exception("The format specifier should consume the input or end with a '}'", SV("{:-}"), input);316 check_exception("The format specifier should consume the input or end with a '}'", SV("{:+}"), input);317 check_exception("The format specifier should consume the input or end with a '}'", SV("{: }"), input);318 319 // *** alternate form ***320 check_exception("The format specifier should consume the input or end with a '}'", SV("{:#}"), input);321 322 // *** zero-padding ***323 check_exception("The width option should not have a leading zero", SV("{:0}"), input);324 325 // *** precision ***326 check_exception("The format specifier should consume the input or end with a '}'", SV("{:.}"), input);327 328 // *** locale-specific form ***329 check_exception("The format specifier should consume the input or end with a '}'", SV("{:L}"), input);330 331 // *** type ***332 check(SV("__42: (\"hello\", \"red\")___"), SV("{:_^25m}"), input);333 check(SV("__42, (\"hello\", \"red\")___"), SV("{:_^25n}"), input);334 335 for (CharT c : SV("aAbBcdeEfFgGopPsxX?")) {336 check_exception("The format specifier should consume the input or end with a '}'",337 std::basic_string_view{STR("{:") + c + STR("}")},338 input);339 }340}341 342template <class CharT, class TestFunction, class ExceptionTest>343void run_tests(TestFunction check, ExceptionTest check_exception) {344 test_pair_int_int<CharT>(check, check_exception);345 test_pair_int_string<CharT>(check, check_exception);346 347 test_tuple_int<CharT>(check, check_exception);348 test_tuple_int_int<CharT>(check, check_exception);349 test_tuple_int_string<CharT>(check, check_exception);350 test_tuple_int_string_color<CharT>(check, check_exception);351 352 test_nested<CharT>(check, check_exception, std::make_pair(42, std::make_pair(SV("hello"), color::red)));353 test_nested<CharT>(check, check_exception, std::make_pair(42, std::make_tuple(SV("hello"), color::red)));354 test_nested<CharT>(check, check_exception, std::make_tuple(42, std::make_pair(SV("hello"), color::red)));355 test_nested<CharT>(check, check_exception, std::make_tuple(42, std::make_tuple(SV("hello"), color::red)));356 357 test_escaping<CharT>(check, std::make_pair(CharT('*'), STR("")));358 test_escaping<CharT>(check, std::make_tuple(CharT('*'), STR("")));359 360 // Test const ref-qualified types.361 // clang-format off362 check(SV("(42)"), SV("{}"), std::tuple< int >{42});363 check(SV("(42)"), SV("{}"), std::tuple<const int >{42});364 365 int answer = 42;366 check(SV("(42)"), SV("{}"), std::tuple< int& >{answer});367 check(SV("(42)"), SV("{}"), std::tuple<const int& >{answer});368 369 check(SV("(42)"), SV("{}"), std::tuple< int&&>{42});370 check(SV("(42)"), SV("{}"), std::tuple<const int&&>{42});371 // clang-format on372}373 374#endif // TEST_STD_UTILITIES_FORMAT_FORMAT_TUPLE_FORMAT_TESTS_H375