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