4d47522f11cc6be24307a556310561010d34b87b
[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, JsonEscape) {
177   folly::json::serialization_opts opts;
178   EXPECT_EQ(
179     folly::json::serialize("\b\f\n\r\x01\t\\\"/\v\a", opts),
180     R"("\b\f\n\r\u0001\t\\\"/\u000b\u0007")");
181 }
182
183 TEST(Json, JsonNonAsciiEncoding) {
184   folly::json::serialization_opts opts;
185   opts.encode_non_ascii = true;
186
187   // simple tests
188   EXPECT_EQ(folly::json::serialize("\x1f", opts), R"("\u001f")");
189   EXPECT_EQ(folly::json::serialize("\xc2\xa2", opts), R"("\u00a2")");
190   EXPECT_EQ(folly::json::serialize("\xe2\x82\xac", opts), R"("\u20ac")");
191
192   // multiple unicode encodings
193   EXPECT_EQ(
194     folly::json::serialize("\x1f\xe2\x82\xac", opts),
195     R"("\u001f\u20ac")");
196   EXPECT_EQ(
197     folly::json::serialize("\x1f\xc2\xa2\xe2\x82\xac", opts),
198     R"("\u001f\u00a2\u20ac")");
199   EXPECT_EQ(
200     folly::json::serialize("\xc2\x80\xef\xbf\xbf", opts),
201     R"("\u0080\uffff")");
202   EXPECT_EQ(
203     folly::json::serialize("\xe0\xa0\x80\xdf\xbf", opts),
204     R"("\u0800\u07ff")");
205
206   // first possible sequence of a certain length
207   EXPECT_EQ(folly::json::serialize("\xc2\x80", opts), R"("\u0080")");
208   EXPECT_EQ(folly::json::serialize("\xe0\xa0\x80", opts), R"("\u0800")");
209
210   // last possible sequence of a certain length
211   EXPECT_EQ(folly::json::serialize("\xdf\xbf", opts), R"("\u07ff")");
212   EXPECT_EQ(folly::json::serialize("\xef\xbf\xbf", opts), R"("\uffff")");
213
214   // other boundary conditions
215   EXPECT_EQ(folly::json::serialize("\xed\x9f\xbf", opts), R"("\ud7ff")");
216   EXPECT_EQ(folly::json::serialize("\xee\x80\x80", opts), R"("\ue000")");
217   EXPECT_EQ(folly::json::serialize("\xef\xbf\xbd", opts), R"("\ufffd")");
218
219   // incomplete sequences
220   EXPECT_ANY_THROW(folly::json::serialize("a\xed\x9f", opts));
221   EXPECT_ANY_THROW(folly::json::serialize("b\xee\x80", opts));
222   EXPECT_ANY_THROW(folly::json::serialize("c\xef\xbf", opts));
223
224   // impossible bytes
225   EXPECT_ANY_THROW(folly::json::serialize("\xfe", opts));
226   EXPECT_ANY_THROW(folly::json::serialize("\xff", opts));
227
228   // Sample overlong sequences
229   EXPECT_ANY_THROW(folly::json::serialize("\xc0\xaf", opts));
230   EXPECT_ANY_THROW(folly::json::serialize("\xe0\x80\xaf", opts));
231
232   // Maximum overlong sequences
233   EXPECT_ANY_THROW(folly::json::serialize("\xc1\xbf", opts));
234   EXPECT_ANY_THROW(folly::json::serialize("\x30\x9f\xbf", opts));
235
236   // illegal code positions
237   EXPECT_ANY_THROW(folly::json::serialize("\xed\xa0\x80", opts));
238   EXPECT_ANY_THROW(folly::json::serialize("\xed\xbf\xbf", opts));
239
240   // Overlong representation of NUL character
241   EXPECT_ANY_THROW(folly::json::serialize("\xc0\x80", opts));
242   EXPECT_ANY_THROW(folly::json::serialize("\xe0\x80\x80", opts));
243
244   // Longer than 3 byte encodings
245   EXPECT_ANY_THROW(folly::json::serialize("\xf4\x8f\xbf\xbf", opts));
246   EXPECT_ANY_THROW(folly::json::serialize("\xed\xaf\xbf\xed\xbf\xbf", opts));
247 }
248
249 TEST(Json, UTF8Validation) {
250   folly::json::serialization_opts opts;
251   opts.validate_utf8 = true;
252
253   // valid utf8 strings
254   EXPECT_EQ(folly::json::serialize("a\xc2\x80z", opts), R"("a\u00c2\u0080z")");
255   EXPECT_EQ(
256     folly::json::serialize("a\xe0\xa0\x80z", opts),
257     R"("a\u00e0\u00a0\u0080z")");
258   EXPECT_EQ(
259     folly::json::serialize("a\xe0\xa0\x80m\xc2\x80z", opts),
260     R"("a\u00e0\u00a0\u0080m\u00c2\u0080z")");
261
262   // test with invalid utf8
263   EXPECT_ANY_THROW(folly::json::serialize("a\xe0\xa0\x80z\xc0\x80", opts));
264   EXPECT_ANY_THROW(folly::json::serialize("a\xe0\xa0\x80z\xe0\x80\x80", opts));
265 }
266
267 BENCHMARK(jsonSerialize, iters) {
268   folly::json::serialization_opts opts;
269   for (int i = 0; i < iters; ++i) {
270     folly::json::serialize(
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       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
275       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
276       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
277       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
278       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
279       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
280       "qwerty \xc2\x80 \xef\xbf\xbf poiuy",
281       opts);
282   }
283 }
284
285 BENCHMARK(jsonSerializeWithNonAsciiEncoding, iters) {
286   folly::json::serialization_opts opts;
287   opts.encode_non_ascii = true;
288
289   for (int i = 0; i < iters; ++i) {
290     folly::json::serialize(
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       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
295       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
296       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
297       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
298       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
299       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
300       "qwerty \xc2\x80 \xef\xbf\xbf poiuy",
301       opts);
302   }
303 }
304
305 BENCHMARK(jsonSerializeWithUtf8Validation, iters) {
306   folly::json::serialization_opts opts;
307   opts.validate_utf8 = true;
308
309   for (int i = 0; i < iters; ++i) {
310     folly::json::serialize(
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       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
315       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
316       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
317       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
318       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
319       "qwerty \xc2\x80 \xef\xbf\xbf poiuy"
320       "qwerty \xc2\x80 \xef\xbf\xbf poiuy",
321       opts);
322   }
323 }
324
325 BENCHMARK(parseSmallStringWithUtf, iters) {
326   for (int i = 0; i < iters << 4; ++i) {
327     parseJson("\"I \\u2665 UTF-8 thjasdhkjh blah blah blah\"");
328   }
329 }
330
331 BENCHMARK(parseNormalString, iters) {
332   for (int i = 0; i < iters << 4; ++i) {
333     parseJson("\"akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk\"");
334   }
335 }
336
337 BENCHMARK(parseBigString, iters) {
338   for (int i = 0; i < iters; ++i) {
339     parseJson("\""
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       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
345       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
346       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
347       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
348       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
349       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
350       "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk"
351       "\"");
352   }
353 }
354
355 int main(int argc, char** argv) {
356   testing::InitGoogleTest(&argc, argv);
357   google::ParseCommandLineFlags(&argc, &argv, true);
358   if (FLAGS_benchmark) {
359     folly::runBenchmarks();
360   }
361   return RUN_ALL_TESTS();
362 }