Pull from FB rev 63ce89e2f2301e6bba44a111cc7d4218022156f6
[folly.git] / folly / test / JsonTest.cpp
1 /*
2  * Copyright 2012 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/json.h"
18 #include <gtest/gtest.h>
19 #include <gflags/gflags.h>
20 #include <cmath>
21 #include <limits>
22 #include <iostream>
23 #include <boost/next_prior.hpp>
24 #include "folly/Benchmark.h"
25
26 using folly::dynamic;
27 using folly::parseJson;
28 using folly::toJson;
29
30 TEST(Json, Unicode) {
31   auto val = parseJson("\"I \u2665 UTF-8\"");
32   EXPECT_EQ("I \u2665 UTF-8", val.asString());
33   val = parseJson("\"I \\u2665 UTF-8\"");
34   EXPECT_EQ("I \u2665 UTF-8", val.asString());
35   val = parseJson("\"I \U0001D11E playing in G-clef\"");
36   EXPECT_EQ("I \U0001D11E playing in G-clef", val.asString());
37
38   val = parseJson("\"I \\uD834\\uDD1E playing in G-clef\"");
39   EXPECT_EQ("I \U0001D11E playing in G-clef", val.asString());
40 }
41
42 TEST(Json, Parse) {
43   auto num = parseJson("12");
44   EXPECT_TRUE(num.isInt());
45   EXPECT_EQ(num, 12);
46   num = parseJson("12e5");
47   EXPECT_TRUE(num.isDouble());
48   EXPECT_EQ(num, 12e5);
49   auto numAs1 = num.asDouble();
50   EXPECT_EQ(numAs1, 12e5);
51   EXPECT_EQ(num, 12e5);
52   EXPECT_EQ(num, 1200000);
53
54   auto largeNumber = parseJson("4611686018427387904");
55   EXPECT_TRUE(largeNumber.isInt());
56   EXPECT_EQ(largeNumber, 4611686018427387904L);
57
58   auto negative = parseJson("-123");
59   EXPECT_EQ(negative, -123);
60
61   auto bfalse = parseJson("false");
62   auto btrue = parseJson("true");
63   EXPECT_EQ(bfalse, false);
64   EXPECT_EQ(btrue, true);
65
66   auto null = parseJson("null");
67   EXPECT_TRUE(null == nullptr);
68
69   auto doub1 = parseJson("12.0");
70   auto doub2 = parseJson("12e2");
71   EXPECT_EQ(doub1, 12.0);
72   EXPECT_EQ(doub2, 12e2);
73   EXPECT_EQ(std::numeric_limits<double>::infinity(),
74             parseJson("Infinity").asDouble());
75   EXPECT_EQ(-std::numeric_limits<double>::infinity(),
76             parseJson("-Infinity").asDouble());
77   EXPECT_TRUE(std::isnan(parseJson("NaN").asDouble()));
78   EXPECT_THROW(parseJson("infinity"), std::runtime_error);
79   EXPECT_THROW(parseJson("inf"), std::runtime_error);
80   EXPECT_THROW(parseJson("nan"), std::runtime_error);
81
82   auto array = parseJson(
83     "[12,false, false  , null , [12e4,32, [], 12]]");
84   EXPECT_EQ(array.size(), 5);
85   if (array.size() == 5) {
86     EXPECT_EQ(boost::prior(array.end())->size(), 4);
87   }
88
89   bool caught = false;
90   try {
91     parseJson("\n[12,\n\nnotvalidjson");
92   } catch (const std::exception& e) {
93     caught = true;
94   }
95   EXPECT_TRUE(caught);
96
97   caught = false;
98   try {
99     parseJson("12e2e2");
100   } catch (const std::exception& e) {
101     caught = true;
102   }
103   EXPECT_TRUE(caught);
104
105   caught = false;
106   try {
107     parseJson("{\"foo\":12,\"bar\":42} \"something\"");
108   } catch (const std::exception& e) {
109     // incomplete parse
110     caught = true;
111   }
112   EXPECT_TRUE(caught);
113
114   dynamic anotherVal = dynamic::object
115     ("foo", "bar")
116     ("junk", 12)
117     ("another", 32.2)
118     ("a",
119       {
120         dynamic::object("a", "b")
121                        ("c", "d"),
122         12.5,
123         "Yo Dawg",
124         { "heh" },
125         nullptr
126       }
127     )
128     ;
129
130   // Print then parse and get the same thing, hopefully.
131   auto value = parseJson(toJson(anotherVal));
132   EXPECT_EQ(value, anotherVal);
133
134   // Test an object with non-string values.
135   dynamic something = folly::parseJson(
136     "{\"old_value\":40,\"changed\":true,\"opened\":false}");
137   dynamic expected = dynamic::object
138     ("old_value", 40)
139     ("changed", true)
140     ("opened", false);
141   EXPECT_EQ(something, expected);
142 }
143
144 TEST(Json, JavascriptSafe) {
145   auto badDouble = (1ll << 63ll) + 1;
146   dynamic badDyn = badDouble;
147   EXPECT_EQ(folly::toJson(badDouble), folly::to<folly::fbstring>(badDouble));
148   folly::json::serialization_opts opts;
149   opts.javascript_safe = true;
150   EXPECT_ANY_THROW(folly::json::serialize(badDouble, opts));
151
152   auto okDouble = 1ll << 63ll;
153   dynamic okDyn = okDouble;
154   EXPECT_EQ(folly::toJson(okDouble), folly::to<folly::fbstring>(okDouble));
155 }
156
157 TEST(Json, Produce) {
158   auto value = parseJson(R"( "f\"oo" )");
159   EXPECT_EQ(toJson(value), R"("f\"oo")");
160   value = parseJson("\"Control code: \001 \002 \x1f\"");
161   EXPECT_EQ(toJson(value), R"("Control code: \u0001 \u0002 \u001f")");
162
163   bool caught = false;
164   try {
165     dynamic d = dynamic::object;
166     d["abc"] = "xyz";
167     d[42.33] = "asd";
168     auto str = toJson(d);
169   } catch (std::exception const& e) {
170     // We're not allowed to have non-string keys in json.
171     caught = true;
172   }
173   EXPECT_TRUE(caught);
174 }
175
176 TEST(Json, JsonNonAsciiEncoding) {
177   folly::json::serialization_opts opts;
178   opts.encode_non_ascii = true;
179
180   // simple tests
181   EXPECT_EQ(folly::json::serialize("\x1f", opts), R"("\u001f")");
182   EXPECT_EQ(folly::json::serialize("\xc2\xa2", opts), R"("\u00a2")");
183   EXPECT_EQ(folly::json::serialize("\xe2\x82\xac", opts), R"("\u20ac")");
184
185   // multiple unicode encodings
186   EXPECT_EQ(
187     folly::json::serialize("\x1f\xe2\x82\xac", opts),
188     R"("\u001f\u20ac")");
189   EXPECT_EQ(
190     folly::json::serialize("\x1f\xc2\xa2\xe2\x82\xac", opts),
191     R"("\u001f\u00a2\u20ac")");
192   EXPECT_EQ(
193     folly::json::serialize("\xc2\x80\xef\xbf\xbf", opts),
194     R"("\u0080\uffff")");
195   EXPECT_EQ(
196     folly::json::serialize("\xe0\xa0\x80\xdf\xbf", opts),
197     R"("\u0800\u07ff")");
198
199   // first possible sequence of a certain length
200   EXPECT_EQ(folly::json::serialize("\xc2\x80", opts), R"("\u0080")");
201   EXPECT_EQ(folly::json::serialize("\xe0\xa0\x80", opts), R"("\u0800")");
202
203   // last possible sequence of a certain length
204   EXPECT_EQ(folly::json::serialize("\xdf\xbf", opts), R"("\u07ff")");
205   EXPECT_EQ(folly::json::serialize("\xef\xbf\xbf", opts), R"("\uffff")");
206
207   // other boundary conditions
208   EXPECT_EQ(folly::json::serialize("\xed\x9f\xbf", opts), R"("\ud7ff")");
209   EXPECT_EQ(folly::json::serialize("\xee\x80\x80", opts), R"("\ue000")");
210   EXPECT_EQ(folly::json::serialize("\xef\xbf\xbd", opts), R"("\ufffd")");
211
212   // incomplete sequences
213   EXPECT_ANY_THROW(folly::json::serialize("a\xed\x9f", opts));
214   EXPECT_ANY_THROW(folly::json::serialize("b\xee\x80", opts));
215   EXPECT_ANY_THROW(folly::json::serialize("c\xef\xbf", opts));
216
217   // impossible bytes
218   EXPECT_ANY_THROW(folly::json::serialize("\xfe", opts));
219   EXPECT_ANY_THROW(folly::json::serialize("\xff", opts));
220
221   // Sample overlong sequences
222   EXPECT_ANY_THROW(folly::json::serialize("\xc0\xaf", opts));
223   EXPECT_ANY_THROW(folly::json::serialize("\xe0\x80\xaf", opts));
224
225   // Maximum overlong sequences
226   EXPECT_ANY_THROW(folly::json::serialize("\xc1\xbf", opts));
227   EXPECT_ANY_THROW(folly::json::serialize("\x30\x9f\xbf", opts));
228
229   // illegal code positions
230   EXPECT_ANY_THROW(folly::json::serialize("\xed\xa0\x80", opts));
231   EXPECT_ANY_THROW(folly::json::serialize("\xed\xbf\xbf", opts));
232
233   // Overlong representation of NUL character
234   EXPECT_ANY_THROW(folly::json::serialize("\xc0\x80", opts));
235   EXPECT_ANY_THROW(folly::json::serialize("\xe0\x80\x80", opts));
236
237   // Longer than 3 byte encodings
238   EXPECT_ANY_THROW(folly::json::serialize("\xf4\x8f\xbf\xbf", opts));
239   EXPECT_ANY_THROW(folly::json::serialize("\xed\xaf\xbf\xed\xbf\xbf", opts));
240 }
241
242 TEST(Json, UTF8Validation) {
243   folly::json::serialization_opts opts;
244   opts.validate_utf8 = true;
245
246   // valid utf8 strings
247   EXPECT_EQ(folly::json::serialize("a\xc2\x80z", opts), R"("a\u00c2\u0080z")");
248   EXPECT_EQ(
249     folly::json::serialize("a\xe0\xa0\x80z", opts),
250     R"("a\u00e0\u00a0\u0080z")");
251   EXPECT_EQ(
252     folly::json::serialize("a\xe0\xa0\x80m\xc2\x80z", opts),
253     R"("a\u00e0\u00a0\u0080m\u00c2\u0080z")");
254
255   // test with invalid utf8
256   EXPECT_ANY_THROW(folly::json::serialize("a\xe0\xa0\x80z\xc0\x80", opts));
257   EXPECT_ANY_THROW(folly::json::serialize("a\xe0\xa0\x80z\xe0\x80\x80", opts));
258 }
259
260 BENCHMARK(jsonSerialize, iters) {
261   folly::json::serialization_opts opts;
262   for (int i = 0; i < iters; ++i) {
263     folly::json::serialize(
264       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
265       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
266       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
267       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
268       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
269       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
270       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
271       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
272       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
273       "qwerty \xc2\x80 \xef\xbf\xbf poiuy",
274       opts);
275   }
276 }
277
278 BENCHMARK(jsonSerializeWithNonAsciiEncoding, iters) {
279   folly::json::serialization_opts opts;
280   opts.encode_non_ascii = true;
281
282   for (int i = 0; i < iters; ++i) {
283     folly::json::serialize(
284       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
285       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
286       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
287       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
288       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
289       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
290       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
291       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
292       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
293       "qwerty \xc2\x80 \xef\xbf\xbf poiuy",
294       opts);
295   }
296 }
297
298 BENCHMARK(jsonSerializeWithUtf8Validation, iters) {
299   folly::json::serialization_opts opts;
300   opts.validate_utf8 = true;
301
302   for (int i = 0; i < iters; ++i) {
303     folly::json::serialize(
304       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
305       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
306       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
307       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
308       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
309       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
310       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
311       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
312       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
313       "qwerty \xc2\x80 \xef\xbf\xbf poiuy",
314       opts);
315   }
316 }
317
318 BENCHMARK(parseSmallStringWithUtf, iters) {
319   for (int i = 0; i < iters << 4; ++i) {
320     parseJson("\"I \\u2665 UTF-8 thjasdhkjh blah blah blah\"");
321   }
322 }
323
324 BENCHMARK(parseNormalString, iters) {
325   for (int i = 0; i < iters << 4; ++i) {
326     parseJson("\"akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk\"");
327   }
328 }
329
330 BENCHMARK(parseBigString, iters) {
331   for (int i = 0; i < iters; ++i) {
332     parseJson("\""
333       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
334       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
335       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
336       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
337       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
338       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
339       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
340       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
341       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
342       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
343       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
344       "\"");
345   }
346 }
347
348 int main(int argc, char** argv) {
349   testing::InitGoogleTest(&argc, argv);
350   google::ParseCommandLineFlags(&argc, &argv, true);
351   if (FLAGS_benchmark) {
352     folly::runBenchmarks();
353   }
354   return RUN_ALL_TESTS();
355 }