xref: /llvm-project/libcxx/test/std/utilities/format/format.tuple/format.functions.tests.h (revision 6a54dfbfe534276d644d7f9c027f0deeb748dd53)
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