2 * Copyright 2014 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "folly/Format.h"
19 #include <glog/logging.h>
20 #include <gflags/gflags.h>
21 #include <gtest/gtest.h>
23 #include "folly/FBVector.h"
24 #include "folly/FileUtil.h"
25 #include "folly/dynamic.h"
26 #include "folly/json.h"
28 using namespace folly;
30 template <class... Args>
31 std::string fstr(StringPiece fmt, Args&&... args) {
32 return format(fmt, std::forward<Args>(args)...).str();
36 std::string vstr(StringPiece fmt, const C& c) {
37 return vformat(fmt, c).str();
41 void compareOctal(Uint u) {
42 char buf1[detail::kMaxOctalLength + 1];
43 buf1[detail::kMaxOctalLength] = '\0';
44 char* p = buf1 + detail::uintToOctal(buf1, detail::kMaxOctalLength, u);
46 char buf2[detail::kMaxOctalLength + 1];
47 sprintf(buf2, "%jo", static_cast<uintmax_t>(u));
49 EXPECT_EQ(std::string(buf2), std::string(p));
53 void compareHex(Uint u) {
54 char buf1[detail::kMaxHexLength + 1];
55 buf1[detail::kMaxHexLength] = '\0';
56 char* p = buf1 + detail::uintToHexLower(buf1, detail::kMaxHexLength, u);
58 char buf2[detail::kMaxHexLength + 1];
59 sprintf(buf2, "%jx", static_cast<uintmax_t>(u));
61 EXPECT_EQ(std::string(buf2), std::string(p));
65 void compareBinary(Uint u) {
66 char buf[detail::kMaxBinaryLength + 1];
67 buf[detail::kMaxBinaryLength] = '\0';
68 char* p = buf + detail::uintToBinary(buf, detail::kMaxBinaryLength, u);
76 tmp.push_back(u & 1 ? '1' : '0');
78 repr.assign(tmp.rbegin(), tmp.rend());
81 EXPECT_EQ(repr, std::string(p));
84 TEST(Format, uintToOctal) {
85 for (unsigned i = 0; i < (1u << 16) + 2; i++) {
90 TEST(Format, uintToHex) {
91 for (unsigned i = 0; i < (1u << 16) + 2; i++) {
96 TEST(Format, uintToBinary) {
97 for (unsigned i = 0; i < (1u << 16) + 2; i++) {
102 TEST(Format, Simple) {
103 EXPECT_EQ("hello", fstr("hello"));
104 EXPECT_EQ("42", fstr("{}", 42));
105 EXPECT_EQ("42 42", fstr("{0} {0}", 42));
106 EXPECT_EQ("00042 23 42", fstr("{0:05} {1:3} {0:4}", 42, 23));
107 EXPECT_EQ("hello world hello 42",
108 fstr("{0} {1} {0} {2}", "hello", "world", 42));
109 EXPECT_EQ("XXhelloXX", fstr("{:X^9}", "hello"));
110 EXPECT_EQ("XXX42XXXX", fstr("{:X^9}", 42));
111 EXPECT_EQ("-0xYYYY2a", fstr("{:Y=#9x}", -42));
112 EXPECT_EQ("*", fstr("{}", '*'));
113 EXPECT_EQ("42", fstr("{}", 42));
114 EXPECT_EQ("0042", fstr("{:04}", 42));
116 EXPECT_EQ("hello ", fstr("{:7}", "hello"));
117 EXPECT_EQ("hello ", fstr("{:<7}", "hello"));
118 EXPECT_EQ(" hello", fstr("{:>7}", "hello"));
120 std::vector<int> v1 {10, 20, 30};
121 EXPECT_EQ("0020", fstr("{0[1]:04}", v1));
122 EXPECT_EQ("0020", vstr("{1:04}", v1));
123 EXPECT_EQ("10 20", vstr("{} {}", v1));
125 const std::vector<int> v2 = v1;
126 EXPECT_EQ("0020", fstr("{0[1]:04}", v2));
127 EXPECT_EQ("0020", vstr("{1:04}", v2));
129 const int p[] = {10, 20, 30};
131 EXPECT_EQ("0020", fstr("{0[1]:04}", p));
132 EXPECT_EQ("0020", vstr("{1:04}", p));
133 EXPECT_EQ("0020", fstr("{0[1]:04}", q));
134 EXPECT_EQ("0020", vstr("{1:04}", q));
135 EXPECT_NE("", fstr("{}", q));
137 EXPECT_EQ("0x", fstr("{}", p).substr(0, 2));
138 EXPECT_EQ("10", vstr("{}", p));
139 EXPECT_EQ("0x", fstr("{}", q).substr(0, 2));
140 EXPECT_EQ("10", vstr("{}", q));
142 EXPECT_EQ("(null)", fstr("{}", q));
144 std::map<int, std::string> m { {10, "hello"}, {20, "world"} };
145 EXPECT_EQ("worldXX", fstr("{[20]:X<7}", m));
146 EXPECT_EQ("worldXX", vstr("{20:X<7}", m));
148 std::map<std::string, std::string> m2 { {"hello", "world"} };
149 EXPECT_EQ("worldXX", fstr("{[hello]:X<7}", m2));
150 EXPECT_EQ("worldXX", vstr("{hello:X<7}", m2));
152 // Test indexing in strings
153 EXPECT_EQ("61 62", fstr("{0[0]:x} {0[1]:x}", "abcde"));
154 EXPECT_EQ("61 62", vstr("{0:x} {1:x}", "abcde"));
155 EXPECT_EQ("61 62", fstr("{0[0]:x} {0[1]:x}", std::string("abcde")));
156 EXPECT_EQ("61 62", vstr("{0:x} {1:x}", std::string("abcde")));
159 EXPECT_EQ("true", fstr("{}", true));
160 EXPECT_EQ("1", fstr("{:d}", true));
161 EXPECT_EQ("false", fstr("{}", false));
162 EXPECT_EQ("0", fstr("{:d}", false));
166 std::pair<int, std::string> p {42, "hello"};
167 EXPECT_EQ(" 42 hello ", fstr("{0[0]:6} {0[1]:6}", p));
168 EXPECT_EQ(" 42 hello ", vstr("{:6} {:6}", p));
173 std::tuple<int, std::string, int> t { 42, "hello", 23 };
174 EXPECT_EQ(" 42 hello 23", fstr("{0[0]:6} {0[1]:6} {0[2]:6}", t));
175 EXPECT_EQ(" 42 hello 23", vstr("{:6} {:6} {:6}", t));
178 // Test writing to stream
179 std::ostringstream os;
180 os << format("{} {}", 42, 23);
181 EXPECT_EQ("42 23", os.str());
183 // Test appending to string
185 format(&s, "{} {}", 42, 23);
186 format(&s, " hello {:X<7}", "world");
187 EXPECT_EQ("42 23 hello worldXX", s);
189 // Test writing to FILE. I'd use open_memstream but that's not available
190 // outside of Linux (even though it's in POSIX.1-2008).
193 CHECK_ERR(pipe(fds));
194 SCOPE_EXIT { closeNoInt(fds[1]); };
196 FILE* fp = fdopen(fds[1], "wb");
198 SCOPE_EXIT { fclose(fp); };
199 writeTo(fp, format("{} {}", 42, 23)); // <= 512 bytes (PIPE_BUF)
203 ssize_t n = readFull(fds[0], buf, sizeof(buf));
206 EXPECT_EQ("42 23", std::string(buf, n));
210 TEST(Format, Float) {
212 EXPECT_EQ("1", fstr("{}", 1.0));
213 EXPECT_EQ("0.1", fstr("{}", 0.1));
214 EXPECT_EQ("0.01", fstr("{}", 0.01));
215 EXPECT_EQ("0.001", fstr("{}", 0.001));
216 EXPECT_EQ("0.0001", fstr("{}", 0.0001));
217 EXPECT_EQ("1e-5", fstr("{}", 0.00001));
218 EXPECT_EQ("1e-6", fstr("{}", 0.000001));
220 EXPECT_EQ("10", fstr("{}", 10.0));
221 EXPECT_EQ("100", fstr("{}", 100.0));
222 EXPECT_EQ("1000", fstr("{}", 1000.0));
223 EXPECT_EQ("10000", fstr("{}", 10000.0));
224 EXPECT_EQ("100000", fstr("{}", 100000.0));
225 EXPECT_EQ("1e+6", fstr("{}", 1000000.0));
226 EXPECT_EQ("1e+7", fstr("{}", 10000000.0));
228 EXPECT_EQ("1.00", fstr("{:.2f}", 1.0));
229 EXPECT_EQ("0.10", fstr("{:.2f}", 0.1));
230 EXPECT_EQ("0.01", fstr("{:.2f}", 0.01));
231 EXPECT_EQ("0.00", fstr("{:.2f}", 0.001));
234 TEST(Format, MultiLevel) {
235 std::vector<std::map<std::string, std::string>> v = {
241 EXPECT_EQ("world", fstr("{[0.hello]}", v));
244 TEST(Format, dynamic) {
245 auto dyn = parseJson(
247 " \"hello\": \"world\",\n"
248 " \"x\": [20, 30],\n"
249 " \"y\": {\"a\" : 42}\n"
252 EXPECT_EQ("world", fstr("{0[hello]}", dyn));
253 EXPECT_EQ("20", fstr("{0[x.0]}", dyn));
254 EXPECT_EQ("42", fstr("{0[y.a]}", dyn));
256 EXPECT_EQ("(null)", fstr("{}", dynamic(nullptr)));
270 template <> class FormatValue<KeyValue> {
272 explicit FormatValue(const KeyValue& kv) : kv_(kv) { }
274 template <class FormatCallback>
275 void format(FormatArg& arg, FormatCallback& cb) const {
276 format_value::formatFormatter(
277 folly::format("<key={}, value={}>", kv_.key, kv_.value),
287 TEST(Format, Custom) {
288 KeyValue kv { "hello", 42 };
290 EXPECT_EQ("<key=hello, value=42>", fstr("{}", kv));
291 EXPECT_EQ("<key=hello, value=42>", fstr("{:10}", kv));
292 EXPECT_EQ("<key=hello", fstr("{:.10}", kv));
293 EXPECT_EQ("<key=hello, value=42>XX", fstr("{:X<23}", kv));
294 EXPECT_EQ("XX<key=hello, value=42>", fstr("{:X>23}", kv));
295 EXPECT_EQ("<key=hello, value=42>", fstr("{0[0]}", &kv));
296 EXPECT_NE("", fstr("{}", &kv));
307 TEST(Format, Unformatted) {
309 EXPECT_NE("", fstr("{}", &o));
310 EXPECT_THROW(fstr("{0[0]}", &o), std::invalid_argument);
313 TEST(Format, Nested) {
314 EXPECT_EQ("1 2 3 4", fstr("{} {} {}", 1, 2, format("{} {}", 3, 4)));
316 // not copyable, must hold temporary in scope instead.
317 auto&& saved = format("{} {}", 3, 4);
318 EXPECT_EQ("1 2 3 4", fstr("{} {} {}", 1, 2, saved));
321 TEST(Format, OutOfBounds) {
322 std::vector<int> ints{1, 2, 3, 4, 5};
323 EXPECT_EQ("1 3 5", fstr("{0[0]} {0[2]} {0[4]}", ints));
324 EXPECT_THROW(fstr("{[5]}", ints), std::out_of_range);
326 std::map<std::string, int> map{{"hello", 0}, {"world", 1}};
327 EXPECT_EQ("hello = 0", fstr("hello = {[hello]}", map));
328 EXPECT_THROW(fstr("{[nope]}", map), std::out_of_range);
329 EXPECT_THROW(vstr("{nope}", map), std::out_of_range);
332 TEST(Format, BogusFormatString) {
333 EXPECT_THROW(fstr("}"), std::invalid_argument);
334 EXPECT_THROW(fstr("foo}bar"), std::invalid_argument);
335 EXPECT_THROW(fstr("foo{bar"), std::invalid_argument);
336 EXPECT_THROW(fstr("{[test]"), std::invalid_argument);
338 // This one fails in detail::enforceWhitespace(), which throws
340 EXPECT_THROW(fstr("{0[test}"), std::exception);
343 int main(int argc, char *argv[]) {
344 testing::InitGoogleTest(&argc, argv);
345 google::ParseCommandLineFlags(&argc, &argv, true);
346 return RUN_ALL_TESTS();