abort instead of throwing an exception on bad format strings
[folly.git] / folly / test / FormatTest.cpp
1 /*
2  * Copyright 2013 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
19 #include <glog/logging.h>
20 #include <gflags/gflags.h>
21 #include <gtest/gtest.h>
22
23 #include "folly/FBVector.h"
24 #include "folly/dynamic.h"
25 #include "folly/json.h"
26
27 using namespace folly;
28
29 template <class... Args>
30 std::string fstr(StringPiece fmt, Args&&... args) {
31   return format(fmt, std::forward<Args>(args)...).str();
32 }
33
34 template <class C>
35 std::string vstr(StringPiece fmt, const C& c) {
36   return vformat(fmt, c).str();
37 }
38
39 template <class Uint>
40 void compareOctal(Uint u) {
41   char buf1[detail::kMaxOctalLength + 1];
42   buf1[detail::kMaxOctalLength] = '\0';
43   char* p = buf1 + detail::uintToOctal(buf1, detail::kMaxOctalLength, u);
44
45   char buf2[detail::kMaxOctalLength + 1];
46   sprintf(buf2, "%jo", static_cast<uintmax_t>(u));
47
48   EXPECT_EQ(std::string(buf2), std::string(p));
49 }
50
51 template <class Uint>
52 void compareHex(Uint u) {
53   char buf1[detail::kMaxHexLength + 1];
54   buf1[detail::kMaxHexLength] = '\0';
55   char* p = buf1 + detail::uintToHexLower(buf1, detail::kMaxHexLength, u);
56
57   char buf2[detail::kMaxHexLength + 1];
58   sprintf(buf2, "%jx", static_cast<uintmax_t>(u));
59
60   EXPECT_EQ(std::string(buf2), std::string(p));
61 }
62
63 template <class Uint>
64 void compareBinary(Uint u) {
65   char buf[detail::kMaxBinaryLength + 1];
66   buf[detail::kMaxBinaryLength] = '\0';
67   char* p = buf + detail::uintToBinary(buf, detail::kMaxBinaryLength, u);
68
69   std::string repr;
70   if (u == 0) {
71     repr = '0';
72   } else {
73     std::string tmp;
74     for (; u; u >>= 1) {
75       tmp.push_back(u & 1 ? '1' : '0');
76     }
77     repr.assign(tmp.rbegin(), tmp.rend());
78   }
79
80   EXPECT_EQ(repr, std::string(p));
81 }
82
83 TEST(Format, uintToOctal) {
84   for (unsigned i = 0; i < (1u << 16) + 2; i++) {
85     compareOctal(i);
86   }
87 }
88
89 TEST(Format, uintToHex) {
90   for (unsigned i = 0; i < (1u << 16) + 2; i++) {
91     compareHex(i);
92   }
93 }
94
95 TEST(Format, uintToBinary) {
96   for (unsigned i = 0; i < (1u << 16) + 2; i++) {
97     compareBinary(i);
98   }
99 }
100
101 TEST(Format, Simple) {
102   EXPECT_EQ("hello", fstr("hello"));
103   EXPECT_EQ("42", fstr("{}", 42));
104   EXPECT_EQ("42 42", fstr("{0} {0}", 42));
105   EXPECT_EQ("00042  23   42", fstr("{0:05} {1:3} {0:4}", 42, 23));
106   EXPECT_EQ("hello world hello 42",
107             fstr("{0} {1} {0} {2}", "hello", "world", 42));
108   EXPECT_EQ("XXhelloXX", fstr("{:X^9}", "hello"));
109   EXPECT_EQ("XXX42XXXX", fstr("{:X^9}", 42));
110   EXPECT_EQ("-0xYYYY2a", fstr("{:Y=#9x}", -42));
111   EXPECT_EQ("*", fstr("{}", '*'));
112   EXPECT_EQ("42", fstr("{}", 42));
113   EXPECT_EQ("0042", fstr("{:04}", 42));
114
115   EXPECT_EQ("hello  ", fstr("{:7}", "hello"));
116   EXPECT_EQ("hello  ", fstr("{:<7}", "hello"));
117   EXPECT_EQ("  hello", fstr("{:>7}", "hello"));
118
119   std::vector<int> v1 {10, 20, 30};
120   EXPECT_EQ("0020", fstr("{0[1]:04}", v1));
121   EXPECT_EQ("0020", vstr("{1:04}", v1));
122   EXPECT_EQ("10 20", vstr("{} {}", v1));
123
124   const std::vector<int> v2 = v1;
125   EXPECT_EQ("0020", fstr("{0[1]:04}", v2));
126   EXPECT_EQ("0020", vstr("{1:04}", v2));
127
128   const int p[] = {10, 20, 30};
129   const int* q = p;
130   EXPECT_EQ("0020", fstr("{0[1]:04}", p));
131   EXPECT_EQ("0020", vstr("{1:04}", p));
132   EXPECT_EQ("0020", fstr("{0[1]:04}", q));
133   EXPECT_EQ("0020", vstr("{1:04}", q));
134   EXPECT_NE("", fstr("{}", q));
135
136   EXPECT_EQ("0x", fstr("{}", p).substr(0, 2));
137   EXPECT_EQ("10", vstr("{}", p));
138   EXPECT_EQ("0x", fstr("{}", q).substr(0, 2));
139   EXPECT_EQ("10", vstr("{}", q));
140   q = nullptr;
141   EXPECT_EQ("(null)", fstr("{}", q));
142
143   std::map<int, std::string> m { {10, "hello"}, {20, "world"} };
144   EXPECT_EQ("worldXX", fstr("{[20]:X<7}", m));
145   EXPECT_EQ("worldXX", vstr("{20:X<7}", m));
146
147   std::map<std::string, std::string> m2 { {"hello", "world"} };
148   EXPECT_EQ("worldXX", fstr("{[hello]:X<7}", m2));
149   EXPECT_EQ("worldXX", vstr("{hello:X<7}", m2));
150
151   // Test indexing in strings
152   EXPECT_EQ("61 62", fstr("{0[0]:x} {0[1]:x}", "abcde"));
153   EXPECT_EQ("61 62", vstr("{0:x} {1:x}", "abcde"));
154   EXPECT_EQ("61 62", fstr("{0[0]:x} {0[1]:x}", std::string("abcde")));
155   EXPECT_EQ("61 62", vstr("{0:x} {1:x}", std::string("abcde")));
156
157   // Test booleans
158   EXPECT_EQ("true", fstr("{}", true));
159   EXPECT_EQ("1", fstr("{:d}", true));
160   EXPECT_EQ("false", fstr("{}", false));
161   EXPECT_EQ("0", fstr("{:d}", false));
162
163   // Test pairs
164   {
165     std::pair<int, std::string> p {42, "hello"};
166     EXPECT_EQ("    42 hello ", fstr("{0[0]:6} {0[1]:6}", p));
167     EXPECT_EQ("    42 hello ", vstr("{:6} {:6}", p));
168   }
169
170   // Test tuples
171   {
172     std::tuple<int, std::string, int> t { 42, "hello", 23 };
173     EXPECT_EQ("    42 hello      23", fstr("{0[0]:6} {0[1]:6} {0[2]:6}", t));
174     EXPECT_EQ("    42 hello      23", vstr("{:6} {:6} {:6}", t));
175   }
176
177   // Test writing to stream
178   std::ostringstream os;
179   os << format("{} {}", 42, 23);
180   EXPECT_EQ("42 23", os.str());
181
182   // Test appending to string
183   std::string s;
184   format(&s, "{} {}", 42, 23);
185   format(&s, " hello {:X<7}", "world");
186   EXPECT_EQ("42 23 hello worldXX", s);
187 }
188
189 TEST(Format, Float) {
190   double d = 1;
191   EXPECT_EQ("1", fstr("{}", 1.0));
192   EXPECT_EQ("0.1", fstr("{}", 0.1));
193   EXPECT_EQ("0.01", fstr("{}", 0.01));
194   EXPECT_EQ("0.001", fstr("{}", 0.001));
195   EXPECT_EQ("0.0001", fstr("{}", 0.0001));
196   EXPECT_EQ("1e-5", fstr("{}", 0.00001));
197   EXPECT_EQ("1e-6", fstr("{}", 0.000001));
198
199   EXPECT_EQ("10", fstr("{}", 10.0));
200   EXPECT_EQ("100", fstr("{}", 100.0));
201   EXPECT_EQ("1000", fstr("{}", 1000.0));
202   EXPECT_EQ("10000", fstr("{}", 10000.0));
203   EXPECT_EQ("100000", fstr("{}", 100000.0));
204   EXPECT_EQ("1e+6", fstr("{}", 1000000.0));
205   EXPECT_EQ("1e+7", fstr("{}", 10000000.0));
206
207   EXPECT_EQ("1.00", fstr("{:.2f}", 1.0));
208   EXPECT_EQ("0.10", fstr("{:.2f}", 0.1));
209   EXPECT_EQ("0.01", fstr("{:.2f}", 0.01));
210   EXPECT_EQ("0.00", fstr("{:.2f}", 0.001));
211 }
212
213 TEST(Format, MultiLevel) {
214   std::vector<std::map<std::string, std::string>> v = {
215     {
216       {"hello", "world"},
217     },
218   };
219
220   EXPECT_EQ("world", fstr("{[0.hello]}", v));
221 }
222
223 TEST(Format, dynamic) {
224   auto dyn = parseJson(
225       "{\n"
226       "  \"hello\": \"world\",\n"
227       "  \"x\": [20, 30],\n"
228       "  \"y\": {\"a\" : 42}\n"
229       "}");
230
231   EXPECT_EQ("world", fstr("{0[hello]}", dyn));
232   EXPECT_EQ("20", fstr("{0[x.0]}", dyn));
233   EXPECT_EQ("42", fstr("{0[y.a]}", dyn));
234
235   EXPECT_EQ("(null)", fstr("{}", dynamic(nullptr)));
236 }
237
238 namespace {
239
240 struct KeyValue {
241   std::string key;
242   int value;
243 };
244
245 }  // namespace
246
247 namespace folly {
248
249 template <> class FormatValue<KeyValue> {
250  public:
251   explicit FormatValue(const KeyValue& kv) : kv_(kv) { }
252
253   template <class FormatCallback>
254   void format(FormatArg& arg, FormatCallback& cb) const {
255     format_value::formatFormatter(
256         folly::format("<key={}, value={}>", kv_.key, kv_.value),
257         arg, cb);
258   }
259
260  private:
261   const KeyValue& kv_;
262 };
263
264 }  // namespace
265
266 TEST(Format, Custom) {
267   KeyValue kv { "hello", 42 };
268
269   EXPECT_EQ("<key=hello, value=42>", fstr("{}", kv));
270   EXPECT_EQ("<key=hello, value=42>", fstr("{:10}", kv));
271   EXPECT_EQ("<key=hello", fstr("{:.10}", kv));
272   EXPECT_EQ("<key=hello, value=42>XX", fstr("{:X<23}", kv));
273   EXPECT_EQ("XX<key=hello, value=42>", fstr("{:X>23}", kv));
274   EXPECT_EQ("<key=hello, value=42>", fstr("{0[0]}", &kv));
275   EXPECT_NE("", fstr("{}", &kv));
276 }
277
278 TEST(Format, Nested) {
279   EXPECT_EQ("1 2 3 4", fstr("{} {} {}", 1, 2, format("{} {}", 3, 4)));
280   //
281   // not copyable, must hold temporary in scope instead.
282   auto&& saved = format("{} {}", 3, 4);
283   EXPECT_EQ("1 2 3 4", fstr("{} {} {}", 1, 2, saved));
284 }
285
286 TEST(Format, OutOfBounds) {
287   std::vector<int> ints{1, 2, 3, 4, 5};
288   EXPECT_EQ("1 3 5", fstr("{0[0]} {0[2]} {0[4]}", ints));
289   EXPECT_THROW(fstr("{[5]}", ints), std::out_of_range);
290
291   std::map<std::string, int> map{{"hello", 0}, {"world", 1}};
292   EXPECT_EQ("hello = 0", fstr("hello = {[hello]}", map));
293   EXPECT_THROW(fstr("{[nope]}", map), std::out_of_range);
294 }
295
296 int main(int argc, char *argv[]) {
297   testing::InitGoogleTest(&argc, argv);
298   google::ParseCommandLineFlags(&argc, &argv, true);
299   return RUN_ALL_TESTS();
300 }
301