1 | /* |
2 | * Copyright 2012-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/Format.h> |
18 | #include <folly/Utility.h> |
19 | #include <folly/portability/GTest.h> |
20 | |
21 | #include <string> |
22 | |
23 | using namespace folly; |
24 | |
25 | template <class Uint> |
26 | void compareOctal(Uint u) { |
27 | char buf1[detail::kMaxOctalLength + 1]; |
28 | buf1[detail::kMaxOctalLength] = '\0'; |
29 | char* p = buf1 + detail::uintToOctal(buf1, detail::kMaxOctalLength, u); |
30 | |
31 | char buf2[detail::kMaxOctalLength + 1]; |
32 | EXPECT_LT( |
33 | snprintf(buf2, sizeof(buf2), "%jo" , static_cast<uintmax_t>(u)), |
34 | sizeof(buf2)); |
35 | |
36 | EXPECT_EQ(std::string(buf2), std::string(p)); |
37 | } |
38 | |
39 | template <class Uint> |
40 | void compareHex(Uint u) { |
41 | char buf1[detail::kMaxHexLength + 1]; |
42 | buf1[detail::kMaxHexLength] = '\0'; |
43 | char* p = buf1 + detail::uintToHexLower(buf1, detail::kMaxHexLength, u); |
44 | |
45 | char buf2[detail::kMaxHexLength + 1]; |
46 | EXPECT_LT( |
47 | snprintf(buf2, sizeof(buf2), "%jx" , static_cast<uintmax_t>(u)), |
48 | sizeof(buf2)); |
49 | |
50 | EXPECT_EQ(std::string(buf2), std::string(p)); |
51 | } |
52 | |
53 | template <class Uint> |
54 | void compareBinary(Uint u) { |
55 | char buf[detail::kMaxBinaryLength + 1]; |
56 | buf[detail::kMaxBinaryLength] = '\0'; |
57 | char* p = buf + detail::uintToBinary(buf, detail::kMaxBinaryLength, u); |
58 | |
59 | std::string repr; |
60 | if (u == 0) { |
61 | repr = '0'; |
62 | } else { |
63 | std::string tmp; |
64 | for (; u; u >>= 1) { |
65 | tmp.push_back(u & 1 ? '1' : '0'); |
66 | } |
67 | repr.assign(tmp.rbegin(), tmp.rend()); |
68 | } |
69 | |
70 | EXPECT_EQ(repr, std::string(p)); |
71 | } |
72 | |
73 | TEST(Format, uintToOctal) { |
74 | for (unsigned i = 0; i < (1u << 16) + 2; i++) { |
75 | compareOctal(i); |
76 | } |
77 | } |
78 | |
79 | TEST(Format, uintToHex) { |
80 | for (unsigned i = 0; i < (1u << 16) + 2; i++) { |
81 | compareHex(i); |
82 | } |
83 | } |
84 | |
85 | TEST(Format, uintToBinary) { |
86 | for (unsigned i = 0; i < (1u << 16) + 2; i++) { |
87 | compareBinary(i); |
88 | } |
89 | } |
90 | |
91 | TEST(Format, Simple) { |
92 | EXPECT_EQ("hello" , sformat("hello" )); |
93 | EXPECT_EQ("42" , sformat("{}" , 42)); |
94 | EXPECT_EQ("42 42" , sformat("{0} {0}" , 42)); |
95 | EXPECT_EQ("00042 23 42" , sformat("{0:05} {1:3} {0:4}" , 42, 23)); |
96 | EXPECT_EQ( |
97 | "hello world hello 42" , sformat("{0} {1} {0} {2}" , "hello" , "world" , 42)); |
98 | EXPECT_EQ("XXhelloXX" , sformat("{:X^9}" , "hello" )); |
99 | EXPECT_EQ("XXX42XXXX" , sformat("{:X^9}" , 42)); |
100 | EXPECT_EQ("-0xYYYY2a" , sformat("{:Y=#9x}" , -42)); |
101 | EXPECT_EQ("*" , sformat("{}" , '*')); |
102 | EXPECT_EQ("42" , sformat("{}" , 42)); |
103 | EXPECT_EQ("0042" , sformat("{:04}" , 42)); |
104 | |
105 | EXPECT_EQ("hello " , sformat("{:7}" , "hello" )); |
106 | EXPECT_EQ("hello " , sformat("{:<7}" , "hello" )); |
107 | EXPECT_EQ(" hello" , sformat("{:>7}" , "hello" )); |
108 | |
109 | EXPECT_EQ(" hi" , sformat("{:>*}" , 4, "hi" )); |
110 | EXPECT_EQ(" hi!" , sformat("{:*}{}" , 3, "" , "hi!" )); |
111 | EXPECT_EQ(" 123" , sformat("{:*}" , 7, 123)); |
112 | EXPECT_EQ("123 " , sformat("{:<*}" , 7, 123)); |
113 | EXPECT_EQ("----<=>----" , sformat("{:-^*}" , 11, "<=>" )); |
114 | EXPECT_EQ("+++456+++" , sformat("{2:+^*0}" , 9, "unused" , 456)); |
115 | |
116 | std::vector<int> v1{10, 20, 30}; |
117 | EXPECT_EQ("0020" , sformat("{0[1]:04}" , v1)); |
118 | EXPECT_EQ("0020" , svformat("{1:04}" , v1)); |
119 | EXPECT_EQ("10 20" , svformat("{} {}" , v1)); |
120 | |
121 | const std::vector<int> v2 = v1; |
122 | EXPECT_EQ("0020" , sformat("{0[1]:04}" , v2)); |
123 | EXPECT_EQ("0020" , svformat("{1:04}" , v2)); |
124 | EXPECT_THROW(sformat("{0[3]:04}" , v2), std::out_of_range); |
125 | EXPECT_THROW(svformat("{3:04}" , v2), std::out_of_range); |
126 | EXPECT_EQ("0020" , sformat("{0[1]:04}" , defaulted(v2, 42))); |
127 | EXPECT_EQ("0020" , svformat("{1:04}" , defaulted(v2, 42))); |
128 | EXPECT_EQ("0042" , sformat("{0[3]:04}" , defaulted(v2, 42))); |
129 | EXPECT_EQ("0042" , svformat("{3:04}" , defaulted(v2, 42))); |
130 | |
131 | { |
132 | const int p[] = {10, 20, 30}; |
133 | const int* q = p; |
134 | EXPECT_EQ("0020" , sformat("{0[1]:04}" , p)); |
135 | EXPECT_EQ("0020" , svformat("{1:04}" , p)); |
136 | EXPECT_EQ("0020" , sformat("{0[1]:04}" , q)); |
137 | EXPECT_EQ("0020" , svformat("{1:04}" , q)); |
138 | EXPECT_NE("" , sformat("{}" , q)); |
139 | |
140 | EXPECT_EQ("0x" , sformat("{}" , p).substr(0, 2)); |
141 | EXPECT_EQ("10" , svformat("{}" , p)); |
142 | EXPECT_EQ("0x" , sformat("{}" , q).substr(0, 2)); |
143 | EXPECT_EQ("10" , svformat("{}" , q)); |
144 | q = nullptr; |
145 | EXPECT_EQ("(null)" , sformat("{}" , q)); |
146 | } |
147 | |
148 | std::map<int, std::string> m{{10, "hello" }, {20, "world" }}; |
149 | EXPECT_EQ("worldXX" , sformat("{[20]:X<7}" , m)); |
150 | EXPECT_EQ("worldXX" , svformat("{20:X<7}" , m)); |
151 | EXPECT_THROW(sformat("{[42]:X<7}" , m), std::out_of_range); |
152 | EXPECT_THROW(svformat("{42:X<7}" , m), std::out_of_range); |
153 | EXPECT_EQ("worldXX" , sformat("{[20]:X<7}" , defaulted(m, "meow" ))); |
154 | EXPECT_EQ("worldXX" , svformat("{20:X<7}" , defaulted(m, "meow" ))); |
155 | EXPECT_EQ("meowXXX" , sformat("{[42]:X<7}" , defaulted(m, "meow" ))); |
156 | EXPECT_EQ("meowXXX" , svformat("{42:X<7}" , defaulted(m, "meow" ))); |
157 | |
158 | std::map<std::string, std::string> m2{{"hello" , "world" }}; |
159 | EXPECT_EQ("worldXX" , sformat("{[hello]:X<7}" , m2)); |
160 | EXPECT_EQ("worldXX" , svformat("{hello:X<7}" , m2)); |
161 | EXPECT_THROW(sformat("{[none]:X<7}" , m2), std::out_of_range); |
162 | EXPECT_THROW(svformat("{none:X<7}" , m2), std::out_of_range); |
163 | EXPECT_EQ("worldXX" , sformat("{[hello]:X<7}" , defaulted(m2, "meow" ))); |
164 | EXPECT_EQ("worldXX" , svformat("{hello:X<7}" , defaulted(m2, "meow" ))); |
165 | EXPECT_EQ("meowXXX" , sformat("{[none]:X<7}" , defaulted(m2, "meow" ))); |
166 | EXPECT_EQ("meowXXX" , svformat("{none:X<7}" , defaulted(m2, "meow" ))); |
167 | try { |
168 | svformat("{none:X<7}" , m2); |
169 | EXPECT_FALSE(true) << "svformat should throw on missing key" ; |
170 | } catch (const FormatKeyNotFoundException& e) { |
171 | EXPECT_STREQ("none" , e.key()); |
172 | } |
173 | |
174 | // Test indexing in strings |
175 | EXPECT_EQ("61 62" , sformat("{0[0]:x} {0[1]:x}" , "abcde" )); |
176 | EXPECT_EQ("61 62" , svformat("{0:x} {1:x}" , "abcde" )); |
177 | EXPECT_EQ("61 62" , sformat("{0[0]:x} {0[1]:x}" , std::string("abcde" ))); |
178 | EXPECT_EQ("61 62" , svformat("{0:x} {1:x}" , std::string("abcde" ))); |
179 | |
180 | // Test booleans |
181 | EXPECT_EQ("true" , sformat("{}" , true)); |
182 | EXPECT_EQ("1" , sformat("{:d}" , true)); |
183 | EXPECT_EQ("false" , sformat("{}" , false)); |
184 | EXPECT_EQ("0" , sformat("{:d}" , false)); |
185 | |
186 | // Test pairs |
187 | { |
188 | std::pair<int, std::string> p{42, "hello" }; |
189 | EXPECT_EQ(" 42 hello " , sformat("{0[0]:6} {0[1]:6}" , p)); |
190 | EXPECT_EQ(" 42 hello " , svformat("{:6} {:6}" , p)); |
191 | } |
192 | |
193 | // Test tuples |
194 | { |
195 | std::tuple<int, std::string, int> t{42, "hello" , 23}; |
196 | EXPECT_EQ(" 42 hello 23" , sformat("{0[0]:6} {0[1]:6} {0[2]:6}" , t)); |
197 | EXPECT_EQ(" 42 hello 23" , svformat("{:6} {:6} {:6}" , t)); |
198 | } |
199 | |
200 | // Test writing to stream |
201 | std::ostringstream os; |
202 | os << format("{} {}" , 42, 23); |
203 | EXPECT_EQ("42 23" , os.str()); |
204 | |
205 | // Test appending to string |
206 | std::string s; |
207 | format(&s, "{} {}" , 42, 23); |
208 | format(&s, " hello {:X<7}" , "world" ); |
209 | EXPECT_EQ("42 23 hello worldXX" , s); |
210 | } |
211 | |
212 | TEST(Format, Float) { |
213 | EXPECT_EQ("1" , sformat("{}" , 1.0)); |
214 | EXPECT_EQ("0.1" , sformat("{}" , 0.1)); |
215 | EXPECT_EQ("0.01" , sformat("{}" , 0.01)); |
216 | EXPECT_EQ("0.001" , sformat("{}" , 0.001)); |
217 | EXPECT_EQ("0.0001" , sformat("{}" , 0.0001)); |
218 | EXPECT_EQ("1e-5" , sformat("{}" , 0.00001)); |
219 | EXPECT_EQ("1e-6" , sformat("{}" , 0.000001)); |
220 | |
221 | EXPECT_EQ("10" , sformat("{}" , 10.0)); |
222 | EXPECT_EQ("100" , sformat("{}" , 100.0)); |
223 | EXPECT_EQ("1000" , sformat("{}" , 1000.0)); |
224 | EXPECT_EQ("10000" , sformat("{}" , 10000.0)); |
225 | EXPECT_EQ("100000" , sformat("{}" , 100000.0)); |
226 | EXPECT_EQ("1e+6" , sformat("{}" , 1000000.0)); |
227 | EXPECT_EQ("1e+7" , sformat("{}" , 10000000.0)); |
228 | |
229 | EXPECT_EQ("1.00" , sformat("{:.2f}" , 1.0)); |
230 | EXPECT_EQ("0.10" , sformat("{:.2f}" , 0.1)); |
231 | EXPECT_EQ("0.01" , sformat("{:.2f}" , 0.01)); |
232 | EXPECT_EQ("0.00" , sformat("{:.2f}" , 0.001)); |
233 | |
234 | EXPECT_EQ("100000. !== 100000" , sformat("{:.} !== {:.}" , 100000.0, 100000)); |
235 | EXPECT_EQ("100000." , sformat("{:.}" , 100000.0)); |
236 | EXPECT_EQ("1e+6" , sformat("{:.}" , 1000000.0)); |
237 | EXPECT_EQ(" 100000." , sformat("{:8.}" , 100000.0)); |
238 | EXPECT_EQ("100000." , sformat("{:4.}" , 100000.0)); |
239 | EXPECT_EQ(" 100000" , sformat("{:8.8}" , 100000.0)); |
240 | EXPECT_EQ(" 100000." , sformat("{:8.8.}" , 100000.0)); |
241 | } |
242 | |
243 | TEST(Format, MultiLevel) { |
244 | std::vector<std::map<std::string, std::string>> v = { |
245 | { |
246 | {"hello" , "world" }, |
247 | }, |
248 | }; |
249 | |
250 | EXPECT_EQ("world" , sformat("{[0.hello]}" , v)); |
251 | } |
252 | |
253 | TEST(Format, separatorDecimalInteger) { |
254 | EXPECT_EQ("0" , sformat("{:,d}" , 0)); |
255 | EXPECT_EQ("1" , sformat("{:d}" , 1)); |
256 | EXPECT_EQ("1" , sformat("{:,d}" , 1)); |
257 | EXPECT_EQ("1" , sformat("{:,}" , 1)); |
258 | EXPECT_EQ("123" , sformat("{:d}" , 123)); |
259 | EXPECT_EQ("123" , sformat("{:,d}" , 123)); |
260 | EXPECT_EQ("123" , sformat("{:,}" , 123)); |
261 | EXPECT_EQ("1234" , sformat("{:d}" , 1234)); |
262 | EXPECT_EQ("1,234" , sformat("{:,d}" , 1234)); |
263 | EXPECT_EQ("1,234" , sformat("{:,}" , 1234)); |
264 | EXPECT_EQ("12345678" , sformat("{:d}" , 12345678)); |
265 | EXPECT_EQ("12,345,678" , sformat("{:,d}" , 12345678)); |
266 | EXPECT_EQ("12,345,678" , sformat("{:,}" , 12345678)); |
267 | EXPECT_EQ("-1234" , sformat("{:d}" , -1234)); |
268 | EXPECT_EQ("-1,234" , sformat("{:,d}" , -1234)); |
269 | EXPECT_EQ("-1,234" , sformat("{:,}" , -1234)); |
270 | |
271 | int64_t max_int64_t = std::numeric_limits<int64_t>::max(); |
272 | int64_t min_int64_t = std::numeric_limits<int64_t>::min(); |
273 | uint64_t max_uint64_t = std::numeric_limits<uint64_t>::max(); |
274 | EXPECT_EQ("9223372036854775807" , sformat("{:d}" , max_int64_t)); |
275 | EXPECT_EQ("9,223,372,036,854,775,807" , sformat("{:,d}" , max_int64_t)); |
276 | EXPECT_EQ("9,223,372,036,854,775,807" , sformat("{:,}" , max_int64_t)); |
277 | EXPECT_EQ("-9223372036854775808" , sformat("{:d}" , min_int64_t)); |
278 | EXPECT_EQ("-9,223,372,036,854,775,808" , sformat("{:,d}" , min_int64_t)); |
279 | EXPECT_EQ("-9,223,372,036,854,775,808" , sformat("{:,}" , min_int64_t)); |
280 | EXPECT_EQ("18446744073709551615" , sformat("{:d}" , max_uint64_t)); |
281 | EXPECT_EQ("18,446,744,073,709,551,615" , sformat("{:,d}" , max_uint64_t)); |
282 | EXPECT_EQ("18,446,744,073,709,551,615" , sformat("{:,}" , max_uint64_t)); |
283 | |
284 | EXPECT_EQ(" -1,234" , sformat("{: 8,}" , -1234)); |
285 | EXPECT_EQ("-001,234" , sformat("{:08,d}" , -1234)); |
286 | EXPECT_EQ("-00001,234" , sformat("{:010,d}" , -1234)); |
287 | EXPECT_EQ(" -1,234 " , sformat("{:^ 8,d}" , -1234)); |
288 | } |
289 | |
290 | // Note that sformat("{:n}", ...) uses the current locale setting to insert the |
291 | // appropriate number separator characters. |
292 | TEST(Format, separatorNumber) { |
293 | EXPECT_EQ("0" , sformat("{:n}" , 0)); |
294 | EXPECT_EQ("1" , sformat("{:n}" , 1)); |
295 | EXPECT_EQ("123" , sformat("{:n}" , 123)); |
296 | EXPECT_EQ("1234" , sformat("{:n}" , 1234)); |
297 | EXPECT_EQ("12345678" , sformat("{:n}" , 12345678)); |
298 | EXPECT_EQ("-1234" , sformat("{:n}" , -1234)); |
299 | |
300 | int64_t max_int64_t = std::numeric_limits<int64_t>::max(); |
301 | int64_t min_int64_t = std::numeric_limits<int64_t>::min(); |
302 | uint64_t max_uint64_t = std::numeric_limits<uint64_t>::max(); |
303 | EXPECT_EQ("9223372036854775807" , sformat("{:n}" , max_int64_t)); |
304 | EXPECT_EQ("-9223372036854775808" , sformat("{:n}" , min_int64_t)); |
305 | EXPECT_EQ("18446744073709551615" , sformat("{:n}" , max_uint64_t)); |
306 | |
307 | EXPECT_EQ(" -1234" , sformat("{: 8n}" , -1234)); |
308 | EXPECT_EQ("-0001234" , sformat("{:08n}" , -1234)); |
309 | EXPECT_EQ("-000001234" , sformat("{:010n}" , -1234)); |
310 | EXPECT_EQ(" -1234 " , sformat("{:^ 8n}" , -1234)); |
311 | } |
312 | |
313 | // insertThousandsGroupingUnsafe requires non-const params |
314 | static void testGrouping(const char* a_str, const char* expected) { |
315 | char str[256]; |
316 | char* end_ptr = str + snprintf(str, sizeof(str), "%s" , a_str); |
317 | ASSERT_LT(end_ptr, str + sizeof(str)); |
318 | folly::detail::insertThousandsGroupingUnsafe(str, &end_ptr); |
319 | ASSERT_STREQ(expected, str); |
320 | } |
321 | |
322 | TEST(Format, separatorUnit) { |
323 | testGrouping("0" , "0" ); |
324 | testGrouping("1" , "1" ); |
325 | testGrouping("12" , "12" ); |
326 | testGrouping("123" , "123" ); |
327 | testGrouping("1234" , "1,234" ); |
328 | testGrouping("12345" , "12,345" ); |
329 | testGrouping("123456" , "123,456" ); |
330 | testGrouping("1234567" , "1,234,567" ); |
331 | testGrouping("1234567890" , "1,234,567,890" ); |
332 | testGrouping("9223372036854775807" , "9,223,372,036,854,775,807" ); |
333 | testGrouping("18446744073709551615" , "18,446,744,073,709,551,615" ); |
334 | } |
335 | |
336 | namespace { |
337 | |
338 | struct KeyValue { |
339 | std::string key; |
340 | int value; |
341 | }; |
342 | |
343 | } // namespace |
344 | |
345 | namespace folly { |
346 | |
347 | template <> |
348 | class FormatValue<KeyValue> { |
349 | public: |
350 | explicit FormatValue(const KeyValue& kv) : kv_(kv) {} |
351 | |
352 | template <class FormatCallback> |
353 | void format(FormatArg& arg, FormatCallback& cb) const { |
354 | format_value::formatFormatter( |
355 | folly::format("<key={}, value={}>" , kv_.key, kv_.value), arg, cb); |
356 | } |
357 | |
358 | private: |
359 | const KeyValue& kv_; |
360 | }; |
361 | |
362 | } // namespace folly |
363 | |
364 | TEST(Format, Custom) { |
365 | KeyValue kv{"hello" , 42}; |
366 | |
367 | EXPECT_EQ("<key=hello, value=42>" , sformat("{}" , kv)); |
368 | EXPECT_EQ("<key=hello, value=42>" , sformat("{:10}" , kv)); |
369 | EXPECT_EQ("<key=hello" , sformat("{:.10}" , kv)); |
370 | EXPECT_EQ("<key=hello, value=42>XX" , sformat("{:X<23}" , kv)); |
371 | EXPECT_EQ("XX<key=hello, value=42>" , sformat("{:X>23}" , kv)); |
372 | EXPECT_EQ("<key=hello, value=42>" , sformat("{0[0]}" , &kv)); |
373 | EXPECT_NE("" , sformat("{}" , &kv)); |
374 | } |
375 | |
376 | namespace { |
377 | |
378 | struct Opaque { |
379 | int k; |
380 | }; |
381 | |
382 | } // namespace |
383 | |
384 | #define EXPECT_THROW_STR(code, type, str) \ |
385 | do { \ |
386 | bool caught = false; \ |
387 | try { \ |
388 | code; \ |
389 | } catch (const type& e) { \ |
390 | caught = true; \ |
391 | EXPECT_TRUE(strstr(e.what(), (str)) != nullptr) \ |
392 | << "Expected message [" << (str) << "], actual message [" \ |
393 | << e.what(); \ |
394 | } catch (const std::exception& e) { \ |
395 | caught = true; \ |
396 | ADD_FAILURE() << "Caught different exception type; expected " #type \ |
397 | ", caught " \ |
398 | << folly::demangle(typeid(e)); \ |
399 | } catch (...) { \ |
400 | caught = true; \ |
401 | ADD_FAILURE() << "Caught unknown exception type; expected " #type; \ |
402 | } \ |
403 | if (!caught) { \ |
404 | ADD_FAILURE() << "Expected exception " #type ", caught nothing"; \ |
405 | } \ |
406 | } while (false) |
407 | |
408 | #define EXPECT_FORMAT_ERROR(code, str) \ |
409 | EXPECT_THROW_STR(code, folly::BadFormatArg, (str)) |
410 | |
411 | TEST(Format, Unformatted) { |
412 | Opaque o; |
413 | EXPECT_NE("" , sformat("{}" , &o)); |
414 | EXPECT_FORMAT_ERROR( |
415 | sformat("{0[0]}" , &o), "No formatter available for this type" ); |
416 | } |
417 | |
418 | TEST(Format, Nested) { |
419 | EXPECT_EQ("1 2 3 4" , sformat("{} {} {}" , 1, 2, format("{} {}" , 3, 4))); |
420 | // |
421 | // not copyable, must hold temporary in scope instead. |
422 | auto&& saved = format("{} {}" , 3, 4); |
423 | EXPECT_EQ("1 2 3 4" , sformat("{} {} {}" , 1, 2, saved)); |
424 | } |
425 | |
426 | TEST(Format, OutOfBounds) { |
427 | std::vector<int> ints{1, 2, 3, 4, 5}; |
428 | EXPECT_EQ("1 3 5" , sformat("{0[0]} {0[2]} {0[4]}" , ints)); |
429 | EXPECT_THROW(sformat("{[5]}" , ints), std::out_of_range); |
430 | |
431 | std::map<std::string, int> map{{"hello" , 0}, {"world" , 1}}; |
432 | EXPECT_EQ("hello = 0" , sformat("hello = {[hello]}" , map)); |
433 | EXPECT_THROW(sformat("{[nope]}" , map), std::out_of_range); |
434 | EXPECT_THROW(svformat("{nope}" , map), std::out_of_range); |
435 | } |
436 | |
437 | TEST(Format, BogusFormatString) { |
438 | EXPECT_FORMAT_ERROR(sformat("}" ), "single '}' in format string" ); |
439 | EXPECT_FORMAT_ERROR(sformat("foo}bar" ), "single '}' in format string" ); |
440 | EXPECT_FORMAT_ERROR(sformat("foo{bar" ), "missing ending '}'" ); |
441 | EXPECT_FORMAT_ERROR(sformat("{[test]" ), "missing ending '}'" ); |
442 | EXPECT_FORMAT_ERROR(sformat("{-1.3}" ), "argument index must be non-negative" ); |
443 | EXPECT_FORMAT_ERROR(sformat("{1.3}" , 0, 1, 2), "index not allowed" ); |
444 | EXPECT_FORMAT_ERROR( |
445 | sformat("{0} {} {1}" , 0, 1, 2), |
446 | "may not have both default and explicit arg indexes" ); |
447 | EXPECT_FORMAT_ERROR( |
448 | sformat("{:*}" , 1.2), "dynamic field width argument must be integral" ); |
449 | EXPECT_FORMAT_ERROR( |
450 | sformat("{} {:*}" , "hi" ), "argument index out of range, max=1" ); |
451 | EXPECT_FORMAT_ERROR( |
452 | sformat("{:*0}" , 12, "ok" ), |
453 | "cannot provide width arg index without value arg index" ); |
454 | EXPECT_FORMAT_ERROR( |
455 | sformat("{0:*}" , 12, "ok" ), |
456 | "cannot provide value arg index without width arg index" ); |
457 | |
458 | std::vector<int> v{1, 2, 3}; |
459 | EXPECT_FORMAT_ERROR( |
460 | svformat("{:*}" , v), "dynamic field width not supported in vformat()" ); |
461 | |
462 | // This one fails in detail::enforceWhitespace(), which throws |
463 | // std::range_error |
464 | EXPECT_FORMAT_ERROR(sformat("{0[test}" ), "argument index must be integer" ); |
465 | } |
466 | |
467 | template <bool containerMode, class... Args> |
468 | class TestExtendingFormatter; |
469 | |
470 | template <bool containerMode, class... Args> |
471 | class TestExtendingFormatter |
472 | : public BaseFormatter< |
473 | TestExtendingFormatter<containerMode, Args...>, |
474 | containerMode, |
475 | Args...> { |
476 | private: |
477 | explicit TestExtendingFormatter(StringPiece& str, Args&&... args) |
478 | : BaseFormatter< |
479 | TestExtendingFormatter<containerMode, Args...>, |
480 | containerMode, |
481 | Args...>(str, std::forward<Args>(args)...) {} |
482 | |
483 | template <size_t K, class Callback> |
484 | void doFormatArg(FormatArg& arg, Callback& cb) const { |
485 | std::string result; |
486 | auto appender = [&result](StringPiece s) { |
487 | result.append(s.data(), s.size()); |
488 | }; |
489 | this->template getFormatValue<K>().format(arg, appender); |
490 | result = sformat("{{{}}}" , result); |
491 | cb(StringPiece(result)); |
492 | } |
493 | |
494 | friend class BaseFormatter< |
495 | TestExtendingFormatter<containerMode, Args...>, |
496 | containerMode, |
497 | Args...>; |
498 | |
499 | template <class... A> |
500 | friend std::string texsformat(StringPiece fmt, A&&... arg); |
501 | }; |
502 | |
503 | template <class... Args> |
504 | std::string texsformat(StringPiece fmt, Args&&... args) { |
505 | return TestExtendingFormatter<false, Args...>( |
506 | fmt, std::forward<Args>(args)...) |
507 | .str(); |
508 | } |
509 | |
510 | TEST(Format, Extending) { |
511 | EXPECT_EQ(texsformat("I {} brackets" , "love" ), "I {love} brackets" ); |
512 | EXPECT_EQ( |
513 | texsformat("I {} nesting" , sformat("really {}" , "love" )), |
514 | "I {really love} nesting" ); |
515 | EXPECT_EQ( |
516 | sformat("I also {} nesting" , texsformat("have an {} for" , "affinity" )), |
517 | "I also have an {affinity} for nesting" ); |
518 | EXPECT_EQ( |
519 | texsformat( |
520 | "Extending {} in {}" , |
521 | texsformat("a {}" , "formatter" ), |
522 | "another formatter" ), |
523 | "Extending {a {formatter}} in {another formatter}" ); |
524 | } |
525 | |
526 | TEST(Format, Temporary) { |
527 | constexpr StringPiece kStr = "A long string that should go on the heap" ; |
528 | auto fmt = format("{}" , kStr.str()); // Pass a temporary std::string. |
529 | EXPECT_EQ(fmt.str(), kStr); |
530 | // The formatter can be reused. |
531 | EXPECT_EQ(fmt.str(), kStr); |
532 | } |
533 | |
534 | namespace { |
535 | |
536 | struct NoncopyableInt : MoveOnly { |
537 | explicit NoncopyableInt(int v) : value(v) {} |
538 | int value; |
539 | }; |
540 | |
541 | } // namespace |
542 | |
543 | namespace folly { |
544 | |
545 | template <> |
546 | class FormatValue<NoncopyableInt> { |
547 | public: |
548 | explicit FormatValue(const NoncopyableInt& v) : v_(v) {} |
549 | |
550 | template <class FormatCallback> |
551 | void format(FormatArg& arg, FormatCallback& cb) const { |
552 | FormatValue<int>(v_.value).format(arg, cb); |
553 | } |
554 | |
555 | private: |
556 | const NoncopyableInt& v_; |
557 | }; |
558 | |
559 | } // namespace folly |
560 | |
561 | TEST(Format, NoncopyableArg) { |
562 | { |
563 | // Test that lvalues are held by reference. |
564 | NoncopyableInt v(1); |
565 | auto fmt = format("{}" , v); |
566 | EXPECT_EQ(fmt.str(), "1" ); |
567 | // The formatter can be reused. |
568 | EXPECT_EQ(fmt.str(), "1" ); |
569 | } |
570 | |
571 | { |
572 | // Test that rvalues are moved. |
573 | auto fmt = format("{}" , NoncopyableInt(1)); |
574 | EXPECT_EQ(fmt.str(), "1" ); |
575 | } |
576 | } |
577 | |