1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef TEST_STD_UTILITIES_FORMAT_FORMAT_TUPLE_FORMAT_TESTS_H 10 #define TEST_STD_UTILITIES_FORMAT_FORMAT_TUPLE_FORMAT_TESTS_H 11 12 #include <concepts> 13 #include <format> 14 #include <tuple> 15 16 #include "format.functions.common.h" 17 18 enum class color { black, red, gold }; 19 20 template <class CharT> 21 struct 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 // 31 template <class CharT, class TestFunction, class ExceptionTest, class TupleOrPair> 32 void 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 79 template <class CharT, class TestFunction, class ExceptionTest, class TupleOrPair> 80 void 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 127 template <class CharT, class TestFunction, class TupleOrPair> 128 void 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 // Char 135 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 // String 143 std::get<0>(input) = CharT('*'); 144 std::get<1>(input) = SV("hellö"); 145 check(SV("('*', \"hellö\")"), SV("{}"), input); 146 } 147 148 // 149 // pair tests 150 // 151 152 template <class CharT, class TestFunction, class ExceptionTest> 153 void 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 157 template <class CharT, class TestFunction, class ExceptionTest> 158 void 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 tests 166 // 167 168 template <class CharT, class TestFunction, class ExceptionTest> 169 void 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 218 template <class CharT, class TestFunction, class ExceptionTest> 219 void 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 268 template <class CharT, class TestFunction, class ExceptionTest> 269 void 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 273 template <class CharT, class TestFunction, class ExceptionTest> 274 void 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 tests 282 // 283 284 template <class CharT, class TestFunction, class ExceptionTest, class Nested> 285 void test_nested(TestFunction check, ExceptionTest check_exception, Nested&& input) { 286 // [format.formatter.spec]/2 287 // A debug-enabled specialization of formatter additionally provides a 288 // public, constexpr, non-static member function set_debug_format() 289 // which modifies the state of the formatter to be as if the type of the 290 // std-format-spec parsed by the last call to parse were ?. 291 // pair and tuple are not debug-enabled specializations to the 292 // set_debug_format is not propagated. The paper 293 // P2733 Fix handling of empty specifiers in std::format 294 // 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 342 template <class CharT, class TestFunction, class ExceptionTest> 343 void 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 off 362 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 on 372 } 373 374 #endif // TEST_STD_UTILITIES_FORMAT_FORMAT_TUPLE_FORMAT_TESTS_H 375