]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // Formatting library for C++ - the core API |
2 | // | |
3 | // Copyright (c) 2012 - present, Victor Zverovich | |
4 | // All rights reserved. | |
5 | // | |
6 | // For the license information refer to format.h. | |
7 | // | |
8 | // Copyright (c) 2018 - present, Remotion (Igor Schulz) | |
9 | // All Rights Reserved | |
10 | // {fmt} support for ranges, containers and types tuple interface. | |
11 | ||
f67539c2 TL |
12 | #include "fmt/ranges.h" |
13 | ||
20effc67 TL |
14 | #include <map> |
15 | #include <string> | |
16 | #include <vector> | |
f67539c2 | 17 | |
20effc67 | 18 | #include "gtest/gtest.h" |
11fdf7f2 | 19 | |
20effc67 TL |
20 | #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601 |
21 | # define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY | |
22 | #endif | |
11fdf7f2 | 23 | |
1e59de90 | 24 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1910 |
20effc67 TL |
25 | # define FMT_RANGES_TEST_ENABLE_JOIN |
26 | # define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT | |
27 | #endif | |
11fdf7f2 | 28 | |
20effc67 TL |
29 | #ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY |
30 | TEST(ranges_test, format_array) { | |
31 | int arr[] = {1, 2, 3, 5, 7, 11}; | |
32 | EXPECT_EQ(fmt::format("{}", arr), "[1, 2, 3, 5, 7, 11]"); | |
11fdf7f2 TL |
33 | } |
34 | ||
20effc67 TL |
35 | TEST(ranges_test, format_2d_array) { |
36 | int arr[][2] = {{1, 2}, {3, 5}, {7, 11}}; | |
37 | EXPECT_EQ(fmt::format("{}", arr), "[[1, 2], [3, 5], [7, 11]]"); | |
11fdf7f2 TL |
38 | } |
39 | ||
20effc67 TL |
40 | TEST(ranges_test, format_array_of_literals) { |
41 | const char* arr[] = {"1234", "abcd"}; | |
42 | EXPECT_EQ(fmt::format("{}", arr), "[\"1234\", \"abcd\"]"); | |
1e59de90 TL |
43 | EXPECT_EQ(fmt::format("{:n}", arr), "\"1234\", \"abcd\""); |
44 | EXPECT_EQ(fmt::format("{:n:}", arr), "1234, abcd"); | |
11fdf7f2 | 45 | } |
20effc67 | 46 | #endif // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY |
11fdf7f2 | 47 | |
20effc67 TL |
48 | TEST(ranges_test, format_vector) { |
49 | auto v = std::vector<int>{1, 2, 3, 5, 7, 11}; | |
50 | EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]"); | |
1e59de90 TL |
51 | EXPECT_EQ(fmt::format("{::#x}", v), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]"); |
52 | EXPECT_EQ(fmt::format("{:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb"); | |
f67539c2 TL |
53 | } |
54 | ||
20effc67 TL |
55 | TEST(ranges_test, format_vector2) { |
56 | auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}}; | |
57 | EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]"); | |
1e59de90 TL |
58 | EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]"); |
59 | EXPECT_EQ(fmt::format("{:n:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb"); | |
20effc67 | 60 | } |
f67539c2 | 61 | |
20effc67 TL |
62 | TEST(ranges_test, format_map) { |
63 | auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}}; | |
1e59de90 TL |
64 | EXPECT_EQ(fmt::format("{}", m), "{\"one\": 1, \"two\": 2}"); |
65 | EXPECT_EQ(fmt::format("{:n}", m), "\"one\": 1, \"two\": 2"); | |
66 | } | |
67 | ||
68 | TEST(ranges_test, format_set) { | |
69 | EXPECT_EQ(fmt::format("{}", std::set<std::string>{"one", "two"}), | |
70 | "{\"one\", \"two\"}"); | |
71 | } | |
72 | ||
73 | namespace adl { | |
74 | struct box { | |
75 | int value; | |
76 | }; | |
77 | ||
78 | auto begin(const box& b) -> const int* { return &b.value; } | |
79 | ||
80 | auto end(const box& b) -> const int* { return &b.value + 1; } | |
81 | } // namespace adl | |
82 | ||
83 | TEST(ranges_test, format_adl_begin_end) { | |
84 | auto b = adl::box{42}; | |
85 | EXPECT_EQ(fmt::format("{}", b), "[42]"); | |
20effc67 | 86 | } |
f67539c2 | 87 | |
20effc67 TL |
88 | TEST(ranges_test, format_pair) { |
89 | auto p = std::pair<int, float>(42, 1.5f); | |
90 | EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)"); | |
f67539c2 TL |
91 | } |
92 | ||
1e59de90 TL |
93 | struct unformattable {}; |
94 | ||
20effc67 TL |
95 | TEST(ranges_test, format_tuple) { |
96 | auto t = | |
97 | std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i'); | |
98 | EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')"); | |
99 | EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()"); | |
1e59de90 TL |
100 | |
101 | EXPECT_TRUE((fmt::is_formattable<std::tuple<>>::value)); | |
102 | EXPECT_FALSE((fmt::is_formattable<unformattable>::value)); | |
103 | EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable>>::value)); | |
104 | EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable, int>>::value)); | |
105 | EXPECT_FALSE((fmt::is_formattable<std::tuple<int, unformattable>>::value)); | |
106 | EXPECT_FALSE( | |
107 | (fmt::is_formattable<std::tuple<unformattable, unformattable>>::value)); | |
108 | EXPECT_TRUE((fmt::is_formattable<std::tuple<int, float>>::value)); | |
11fdf7f2 TL |
109 | } |
110 | ||
20effc67 TL |
111 | #ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT |
112 | struct tuple_like { | |
113 | int i; | |
114 | std::string str; | |
115 | ||
116 | template <size_t N> fmt::enable_if_t<N == 0, int> get() const noexcept { | |
117 | return i; | |
118 | } | |
119 | template <size_t N> | |
120 | fmt::enable_if_t<N == 1, fmt::string_view> get() const noexcept { | |
121 | return str; | |
11fdf7f2 TL |
122 | } |
123 | }; | |
124 | ||
20effc67 TL |
125 | template <size_t N> |
126 | auto get(const tuple_like& t) noexcept -> decltype(t.get<N>()) { | |
127 | return t.get<N>(); | |
11fdf7f2 TL |
128 | } |
129 | ||
130 | namespace std { | |
20effc67 TL |
131 | template <> |
132 | struct tuple_size<tuple_like> : std::integral_constant<size_t, 2> {}; | |
11fdf7f2 | 133 | |
20effc67 TL |
134 | template <size_t N> struct tuple_element<N, tuple_like> { |
135 | using type = decltype(std::declval<tuple_like>().get<N>()); | |
11fdf7f2 | 136 | }; |
11fdf7f2 TL |
137 | } // namespace std |
138 | ||
20effc67 TL |
139 | TEST(ranges_test, format_struct) { |
140 | auto t = tuple_like{42, "foo"}; | |
141 | EXPECT_EQ(fmt::format("{}", t), "(42, \"foo\")"); | |
11fdf7f2 | 142 | } |
20effc67 | 143 | #endif // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT |
11fdf7f2 | 144 | |
20effc67 | 145 | TEST(ranges_test, format_to) { |
9f95a23c | 146 | char buf[10]; |
20effc67 | 147 | auto end = fmt::format_to(buf, "{}", std::vector<int>{1, 2, 3}); |
9f95a23c | 148 | *end = '\0'; |
20effc67 | 149 | EXPECT_STREQ(buf, "[1, 2, 3]"); |
9f95a23c TL |
150 | } |
151 | ||
f67539c2 TL |
152 | struct path_like { |
153 | const path_like* begin() const; | |
154 | const path_like* end() const; | |
155 | ||
156 | operator std::string() const; | |
157 | }; | |
158 | ||
20effc67 | 159 | TEST(ranges_test, path_like) { |
f67539c2 TL |
160 | EXPECT_FALSE((fmt::is_range<path_like, char>::value)); |
161 | } | |
162 | ||
f67539c2 TL |
163 | #ifdef FMT_USE_STRING_VIEW |
164 | struct string_like { | |
165 | const char* begin(); | |
166 | const char* end(); | |
1e59de90 TL |
167 | operator fmt::string_view() const { return "foo"; } |
168 | operator std::string_view() const { return "foo"; } | |
f67539c2 TL |
169 | }; |
170 | ||
20effc67 TL |
171 | TEST(ranges_test, format_string_like) { |
172 | EXPECT_EQ(fmt::format("{}", string_like()), "foo"); | |
f67539c2 TL |
173 | } |
174 | #endif // FMT_USE_STRING_VIEW | |
175 | ||
20effc67 TL |
176 | // A range that provides non-const only begin()/end() to test fmt::join handles |
177 | // that. | |
178 | // | |
179 | // Some ranges (e.g. those produced by range-v3's views::filter()) can cache | |
180 | // information during iteration so they only provide non-const begin()/end(). | |
181 | template <typename T> class non_const_only_range { | |
182 | private: | |
183 | std::vector<T> vec; | |
184 | ||
185 | public: | |
186 | using const_iterator = typename ::std::vector<T>::const_iterator; | |
187 | ||
188 | template <typename... Args> | |
189 | explicit non_const_only_range(Args&&... args) | |
190 | : vec(std::forward<Args>(args)...) {} | |
191 | ||
192 | const_iterator begin() { return vec.begin(); } | |
193 | const_iterator end() { return vec.end(); } | |
194 | }; | |
195 | ||
196 | template <typename T> class noncopyable_range { | |
197 | private: | |
198 | std::vector<T> vec; | |
199 | ||
200 | public: | |
201 | using const_iterator = typename ::std::vector<T>::const_iterator; | |
202 | ||
203 | template <typename... Args> | |
204 | explicit noncopyable_range(Args&&... args) | |
205 | : vec(std::forward<Args>(args)...) {} | |
206 | ||
207 | noncopyable_range(noncopyable_range const&) = delete; | |
208 | noncopyable_range(noncopyable_range&) = delete; | |
209 | ||
210 | const_iterator begin() const { return vec.begin(); } | |
211 | const_iterator end() const { return vec.end(); } | |
212 | }; | |
213 | ||
214 | TEST(ranges_test, range) { | |
215 | noncopyable_range<int> w(3u, 0); | |
216 | EXPECT_EQ(fmt::format("{}", w), "[0, 0, 0]"); | |
217 | EXPECT_EQ(fmt::format("{}", noncopyable_range<int>(3u, 0)), "[0, 0, 0]"); | |
218 | ||
219 | non_const_only_range<int> x(3u, 0); | |
220 | EXPECT_EQ(fmt::format("{}", x), "[0, 0, 0]"); | |
221 | EXPECT_EQ(fmt::format("{}", non_const_only_range<int>(3u, 0)), "[0, 0, 0]"); | |
222 | ||
223 | auto y = std::vector<int>(3u, 0); | |
224 | EXPECT_EQ(fmt::format("{}", y), "[0, 0, 0]"); | |
225 | EXPECT_EQ(fmt::format("{}", std::vector<int>(3u, 0)), "[0, 0, 0]"); | |
226 | ||
227 | const auto z = std::vector<int>(3u, 0); | |
228 | EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]"); | |
229 | } | |
230 | ||
1e59de90 TL |
231 | enum test_enum { foo }; |
232 | auto format_as(test_enum e) -> int { return e; } | |
233 | ||
234 | TEST(ranges_test, enum_range) { | |
235 | auto v = std::vector<test_enum>{test_enum::foo}; | |
236 | EXPECT_EQ(fmt::format("{}", v), "[0]"); | |
237 | } | |
238 | ||
239 | #if !FMT_MSC_VERSION | |
20effc67 TL |
240 | |
241 | TEST(ranges_test, unformattable_range) { | |
242 | EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>, | |
243 | fmt::format_context>::value)); | |
244 | } | |
245 | #endif | |
246 | ||
247 | #ifdef FMT_RANGES_TEST_ENABLE_JOIN | |
248 | TEST(ranges_test, join_tuple) { | |
249 | // Value tuple args. | |
250 | auto t1 = std::tuple<char, int, float>('a', 1, 2.0f); | |
251 | EXPECT_EQ(fmt::format("({})", fmt::join(t1, ", ")), "(a, 1, 2)"); | |
252 | ||
253 | // Testing lvalue tuple args. | |
254 | int x = 4; | |
255 | auto t2 = std::tuple<char, int&>('b', x); | |
256 | EXPECT_EQ(fmt::format("{}", fmt::join(t2, " + ")), "b + 4"); | |
257 | ||
258 | // Empty tuple. | |
259 | auto t3 = std::tuple<>(); | |
260 | EXPECT_EQ(fmt::format("{}", fmt::join(t3, "|")), ""); | |
261 | ||
262 | // Single element tuple. | |
263 | auto t4 = std::tuple<float>(4.0f); | |
264 | EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4"); | |
1e59de90 TL |
265 | |
266 | # if FMT_TUPLE_JOIN_SPECIFIERS | |
267 | // Specs applied to each element. | |
268 | auto t5 = std::tuple<int, int, long>(-3, 100, 1); | |
269 | EXPECT_EQ(fmt::format("{:+03}", fmt::join(t5, ", ")), "-03, +100, +01"); | |
270 | ||
271 | auto t6 = std::tuple<float, double, long double>(3, 3.14, 3.1415); | |
272 | EXPECT_EQ(fmt::format("{:5.5f}", fmt::join(t6, ", ")), | |
273 | "3.00000, 3.14000, 3.14150"); | |
274 | ||
275 | // Testing lvalue tuple args. | |
276 | int y = -1; | |
277 | auto t7 = std::tuple<int, int&, const int&>(3, y, y); | |
278 | EXPECT_EQ(fmt::format("{:03}", fmt::join(t7, ", ")), "003, -01, -01"); | |
279 | # endif | |
20effc67 TL |
280 | } |
281 | ||
282 | TEST(ranges_test, join_initializer_list) { | |
283 | EXPECT_EQ(fmt::format("{}", fmt::join({1, 2, 3}, ", ")), "1, 2, 3"); | |
284 | EXPECT_EQ(fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")), | |
285 | "fmt rocks !"); | |
286 | } | |
287 | ||
f67539c2 | 288 | struct zstring_sentinel {}; |
20effc67 | 289 | |
f67539c2 TL |
290 | bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } |
291 | bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } | |
20effc67 | 292 | |
f67539c2 TL |
293 | struct zstring { |
294 | const char* p; | |
295 | const char* begin() const { return p; } | |
296 | zstring_sentinel end() const { return {}; } | |
297 | }; | |
20effc67 | 298 | |
1e59de90 TL |
299 | # ifdef __cpp_lib_ranges |
300 | struct cpp20_only_range { | |
301 | struct iterator { | |
302 | int val = 0; | |
303 | ||
304 | using value_type = int; | |
305 | using difference_type = std::ptrdiff_t; | |
306 | using iterator_concept = std::input_iterator_tag; | |
307 | ||
308 | iterator() = default; | |
309 | iterator(int i) : val(i) {} | |
310 | int operator*() const { return val; } | |
311 | iterator& operator++() { | |
312 | ++val; | |
313 | return *this; | |
314 | } | |
315 | void operator++(int) { ++*this; } | |
316 | bool operator==(const iterator& rhs) const { return val == rhs.val; } | |
317 | }; | |
318 | ||
319 | int lo; | |
320 | int hi; | |
321 | ||
322 | iterator begin() const { return iterator(lo); } | |
323 | iterator end() const { return iterator(hi); } | |
324 | }; | |
325 | ||
326 | static_assert(std::input_iterator<cpp20_only_range::iterator>); | |
327 | # endif | |
328 | ||
20effc67 TL |
329 | TEST(ranges_test, join_sentinel) { |
330 | auto hello = zstring{"hello"}; | |
331 | EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']"); | |
1e59de90 | 332 | EXPECT_EQ(fmt::format("{::}", hello), "[h, e, l, l, o]"); |
20effc67 TL |
333 | EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o"); |
334 | } | |
335 | ||
336 | TEST(ranges_test, join_range) { | |
337 | noncopyable_range<int> w(3u, 0); | |
338 | EXPECT_EQ(fmt::format("{}", fmt::join(w, ",")), "0,0,0"); | |
339 | EXPECT_EQ(fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")), | |
340 | "0,0,0"); | |
341 | ||
342 | non_const_only_range<int> x(3u, 0); | |
343 | EXPECT_EQ(fmt::format("{}", fmt::join(x, ",")), "0,0,0"); | |
344 | EXPECT_EQ(fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")), | |
345 | "0,0,0"); | |
346 | ||
347 | auto y = std::vector<int>(3u, 0); | |
348 | EXPECT_EQ(fmt::format("{}", fmt::join(y, ",")), "0,0,0"); | |
349 | EXPECT_EQ(fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")), | |
350 | "0,0,0"); | |
351 | ||
352 | const auto z = std::vector<int>(3u, 0); | |
353 | EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0"); | |
1e59de90 TL |
354 | |
355 | # ifdef __cpp_lib_ranges | |
356 | EXPECT_EQ(fmt::format("{}", cpp20_only_range{.lo = 0, .hi = 5}), | |
357 | "[0, 1, 2, 3, 4]"); | |
358 | EXPECT_EQ( | |
359 | fmt::format("{}", fmt::join(cpp20_only_range{.lo = 0, .hi = 5}, ",")), | |
360 | "0,1,2,3,4"); | |
361 | # endif | |
f67539c2 | 362 | } |
20effc67 | 363 | #endif // FMT_RANGES_TEST_ENABLE_JOIN |
1e59de90 TL |
364 | |
365 | TEST(ranges_test, is_printable) { | |
366 | using fmt::detail::is_printable; | |
367 | EXPECT_TRUE(is_printable(0x0323)); | |
368 | EXPECT_FALSE(is_printable(0x0378)); | |
369 | EXPECT_FALSE(is_printable(0x110000)); | |
370 | } | |
371 | ||
372 | TEST(ranges_test, escape_string) { | |
373 | using vec = std::vector<std::string>; | |
374 | EXPECT_EQ(fmt::format("{}", vec{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]"); | |
375 | EXPECT_EQ(fmt::format("{}", vec{"\x07"}), "[\"\\x07\"]"); | |
376 | EXPECT_EQ(fmt::format("{}", vec{"\x7f"}), "[\"\\x7f\"]"); | |
377 | EXPECT_EQ(fmt::format("{}", vec{"n\xcc\x83"}), "[\"n\xcc\x83\"]"); | |
378 | ||
379 | if (fmt::detail::is_utf8()) { | |
380 | EXPECT_EQ(fmt::format("{}", vec{"\xcd\xb8"}), "[\"\\u0378\"]"); | |
381 | // Unassigned Unicode code points. | |
382 | EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]"); | |
383 | // Broken utf-8. | |
384 | EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}), | |
385 | "[\"\\xf4\\x8f\\xbf\\xc0\"]"); | |
386 | EXPECT_EQ(fmt::format("{}", vec{"\xf0\x28"}), "[\"\\xf0(\"]"); | |
387 | EXPECT_EQ(fmt::format("{}", vec{"\xe1\x28"}), "[\"\\xe1(\"]"); | |
388 | EXPECT_EQ(fmt::format("{}", vec{std::string("\xf0\x28\0\0anything", 12)}), | |
389 | "[\"\\xf0(\\x00\\x00anything\"]"); | |
390 | ||
391 | // Correct utf-8. | |
392 | EXPECT_EQ(fmt::format("{}", vec{"понедельник"}), "[\"понедельник\"]"); | |
393 | } | |
394 | } | |
395 | ||
396 | #ifdef FMT_USE_STRING_VIEW | |
397 | struct convertible_to_string_view { | |
398 | operator std::string_view() const { return "foo"; } | |
399 | }; | |
400 | ||
401 | TEST(ranges_test, escape_convertible_to_string_view) { | |
402 | EXPECT_EQ(fmt::format("{}", std::vector<convertible_to_string_view>(1)), | |
403 | "[\"foo\"]"); | |
404 | } | |
405 | #endif // FMT_USE_STRING_VIEW | |
406 | ||
407 | template <typename R> struct fmt_ref_view { | |
408 | R* r; | |
409 | ||
410 | auto begin() const -> decltype(r->begin()) { return r->begin(); } | |
411 | auto end() const -> decltype(r->end()) { return r->end(); } | |
412 | }; | |
413 | ||
414 | TEST(ranges_test, range_of_range_of_mixed_const) { | |
415 | std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5}}; | |
416 | EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]"); | |
417 | ||
418 | fmt_ref_view<decltype(v)> r{&v}; | |
419 | EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]"); | |
420 | } | |
421 | ||
422 | TEST(ranges_test, vector_char) { | |
423 | EXPECT_EQ(fmt::format("{}", std::vector<char>{'a', 'b'}), "['a', 'b']"); | |
424 | } |