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
23using namespace folly;
24
25template <class Uint>
26void 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
39template <class Uint>
40void 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
53template <class Uint>
54void 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
73TEST(Format, uintToOctal) {
74 for (unsigned i = 0; i < (1u << 16) + 2; i++) {
75 compareOctal(i);
76 }
77}
78
79TEST(Format, uintToHex) {
80 for (unsigned i = 0; i < (1u << 16) + 2; i++) {
81 compareHex(i);
82 }
83}
84
85TEST(Format, uintToBinary) {
86 for (unsigned i = 0; i < (1u << 16) + 2; i++) {
87 compareBinary(i);
88 }
89}
90
91TEST(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
212TEST(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
243TEST(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
253TEST(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.
292TEST(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
314static 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
322TEST(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
336namespace {
337
338struct KeyValue {
339 std::string key;
340 int value;
341};
342
343} // namespace
344
345namespace folly {
346
347template <>
348class 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
364TEST(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
376namespace {
377
378struct 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
411TEST(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
418TEST(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
426TEST(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
437TEST(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
467template <bool containerMode, class... Args>
468class TestExtendingFormatter;
469
470template <bool containerMode, class... Args>
471class 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
503template <class... Args>
504std::string texsformat(StringPiece fmt, Args&&... args) {
505 return TestExtendingFormatter<false, Args...>(
506 fmt, std::forward<Args>(args)...)
507 .str();
508}
509
510TEST(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
526TEST(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
534namespace {
535
536struct NoncopyableInt : MoveOnly {
537 explicit NoncopyableInt(int v) : value(v) {}
538 int value;
539};
540
541} // namespace
542
543namespace folly {
544
545template <>
546class 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
561TEST(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