X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FJsonTest.cpp;h=0145fecbbcd77a6249fc3e875c625c5303fb0b06;hb=7acba7e1a0a75d22087647b3e9e830c9b0e8d41e;hp=ff1c9008649205661c2afa0441e24a41a6fed152;hpb=5c77fedbef46995a71ffa268c9fcaf49efddd01b;p=folly.git diff --git a/folly/test/JsonTest.cpp b/folly/test/JsonTest.cpp index ff1c9008..0145fecb 100644 --- a/folly/test/JsonTest.cpp +++ b/folly/test/JsonTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,30 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#include "folly/json.h" -#include -#include -#include #include -#include + #include -#include "folly/Benchmark.h" + +#include +#include using folly::dynamic; using folly::parseJson; using folly::toJson; TEST(Json, Unicode) { - auto val = parseJson("\"I \u2665 UTF-8\""); - EXPECT_EQ("I \u2665 UTF-8", val.asString()); + auto val = parseJson(u8"\"I \u2665 UTF-8\""); + EXPECT_EQ(u8"I \u2665 UTF-8", val.asString()); val = parseJson("\"I \\u2665 UTF-8\""); - EXPECT_EQ("I \u2665 UTF-8", val.asString()); - val = parseJson("\"I \U0001D11E playing in G-clef\""); - EXPECT_EQ("I \U0001D11E playing in G-clef", val.asString()); + EXPECT_EQ(u8"I \u2665 UTF-8", val.asString()); + val = parseJson(u8"\"I \U0001D11E playing in G-clef\""); + EXPECT_EQ(u8"I \U0001D11E playing in G-clef", val.asString()); val = parseJson("\"I \\uD834\\uDD1E playing in G-clef\""); - EXPECT_EQ("I \U0001D11E playing in G-clef", val.asString()); + EXPECT_EQ(u8"I \U0001D11E playing in G-clef", val.asString()); } TEST(Json, Parse) { @@ -75,9 +72,14 @@ TEST(Json, Parse) { EXPECT_EQ(-std::numeric_limits::infinity(), parseJson("-Infinity").asDouble()); EXPECT_TRUE(std::isnan(parseJson("NaN").asDouble())); + + // case matters EXPECT_THROW(parseJson("infinity"), std::runtime_error); EXPECT_THROW(parseJson("inf"), std::runtime_error); + EXPECT_THROW(parseJson("Inf"), std::runtime_error); + EXPECT_THROW(parseJson("INF"), std::runtime_error); EXPECT_THROW(parseJson("nan"), std::runtime_error); + EXPECT_THROW(parseJson("NAN"), std::runtime_error); auto array = parseJson( "[12,false, false , null , [12e4,32, [], 12]]"); @@ -86,53 +88,37 @@ TEST(Json, Parse) { EXPECT_EQ(boost::prior(array.end())->size(), 4); } - bool caught = false; - try { - parseJson("\n[12,\n\nnotvalidjson"); - } catch (const std::exception& e) { - caught = true; - } - EXPECT_TRUE(caught); + EXPECT_THROW(parseJson("\n[12,\n\nnotvalidjson"), + std::runtime_error); - caught = false; - try { - parseJson("12e2e2"); - } catch (const std::exception& e) { - caught = true; - } - EXPECT_TRUE(caught); - - caught = false; - try { - parseJson("{\"foo\":12,\"bar\":42} \"something\""); - } catch (const std::exception& e) { - // incomplete parse - caught = true; - } - EXPECT_TRUE(caught); + EXPECT_THROW(parseJson("12e2e2"), + std::runtime_error); + + EXPECT_THROW(parseJson("{\"foo\":12,\"bar\":42} \"something\""), + std::runtime_error); - dynamic anotherVal = dynamic::object + dynamic value = dynamic::object ("foo", "bar") ("junk", 12) ("another", 32.2) ("a", - { + dynamic::array( dynamic::object("a", "b") ("c", "d"), 12.5, "Yo Dawg", - { "heh" }, + dynamic::array("heh"), nullptr - } + ) ) ; // Print then parse and get the same thing, hopefully. - auto value = parseJson(toJson(anotherVal)); - EXPECT_EQ(value, anotherVal); + EXPECT_EQ(value, parseJson(toJson(value))); + // Test an object with non-string values. - dynamic something = folly::parseJson( + dynamic something = parseJson( "{\"old_value\":40,\"changed\":true,\"opened\":false}"); dynamic expected = dynamic::object ("old_value", 40) @@ -141,17 +127,43 @@ TEST(Json, Parse) { EXPECT_EQ(something, expected); } +TEST(Json, ParseTrailingComma) { + folly::json::serialization_opts on, off; + on.allow_trailing_comma = true; + off.allow_trailing_comma = false; + + dynamic arr = dynamic::array(1, 2); + EXPECT_EQ(arr, parseJson("[1, 2]", on)); + EXPECT_EQ(arr, parseJson("[1, 2,]", on)); + EXPECT_EQ(arr, parseJson("[1, 2, ]", on)); + EXPECT_EQ(arr, parseJson("[1, 2 , ]", on)); + EXPECT_EQ(arr, parseJson("[1, 2 ,]", on)); + EXPECT_THROW(parseJson("[1, 2,]", off), std::runtime_error); + + dynamic obj = dynamic::object("a", 1); + EXPECT_EQ(obj, parseJson("{\"a\": 1}", on)); + EXPECT_EQ(obj, parseJson("{\"a\": 1,}", on)); + EXPECT_EQ(obj, parseJson("{\"a\": 1, }", on)); + EXPECT_EQ(obj, parseJson("{\"a\": 1 , }", on)); + EXPECT_EQ(obj, parseJson("{\"a\": 1 ,}", on)); + EXPECT_THROW(parseJson("{\"a\":1,}", off), std::runtime_error); +} + +TEST(Json, BoolConversion) { + EXPECT_TRUE(parseJson("42").asBool()); +} + TEST(Json, JavascriptSafe) { - auto badDouble = (1ll << 63ll) + 1; + auto badDouble = int64_t((1ULL << 63ULL) + 1); dynamic badDyn = badDouble; - EXPECT_EQ(folly::toJson(badDouble), folly::to(badDouble)); + EXPECT_EQ(folly::toJson(badDouble), folly::to(badDouble)); folly::json::serialization_opts opts; opts.javascript_safe = true; EXPECT_ANY_THROW(folly::json::serialize(badDouble, opts)); - auto okDouble = 1ll << 63ll; + auto okDouble = int64_t(1ULL << 63ULL); dynamic okDyn = okDouble; - EXPECT_EQ(folly::toJson(okDouble), folly::to(okDouble)); + EXPECT_EQ(folly::toJson(okDouble), folly::to(okDouble)); } TEST(Json, Produce) { @@ -160,17 +172,15 @@ TEST(Json, Produce) { value = parseJson("\"Control code: \001 \002 \x1f\""); EXPECT_EQ(toJson(value), R"("Control code: \u0001 \u0002 \u001f")"); - bool caught = false; - try { - dynamic d = dynamic::object; - d["abc"] = "xyz"; - d[42.33] = "asd"; - auto str = toJson(d); - } catch (std::exception const& e) { - // We're not allowed to have non-string keys in json. - caught = true; - } - EXPECT_TRUE(caught); + // We're not allowed to have non-string keys in json. + EXPECT_THROW(toJson(dynamic::object("abc", "xyz")(42.33, "asd")), + std::runtime_error); + + // Check Infinity/Nan + folly::json::serialization_opts opts; + opts.allow_nan_inf = true; + EXPECT_EQ("Infinity", folly::json::serialize(parseJson("Infinity"), opts)); + EXPECT_EQ("NaN", folly::json::serialize(parseJson("NaN"), opts)); } TEST(Json, JsonEscape) { @@ -180,6 +190,53 @@ TEST(Json, JsonEscape) { R"("\b\f\n\r\u0001\t\\\"/\u000b\u0007")"); } +TEST(Json, EscapeCornerCases) { + // The escaping logic uses some bitwise operations to determine + // which bytes need escaping 8 bytes at a time. Test that this logic + // is correct regardless of positions by planting 2 characters that + // may need escaping at each possible position and checking the + // result, for varying string lengths. + + folly::json::serialization_opts opts; + opts.validate_utf8 = true; + + std::string s; + std::string expected; + for (bool ascii : {true, false}) { + opts.encode_non_ascii = ascii; + + for (size_t len = 2; len < 32; ++len) { + for (size_t i = 0; i < len; ++i) { + for (size_t j = 0; j < len; ++j) { + if (i == j) { + continue; + } + + s.clear(); + expected.clear(); + + expected.push_back('"'); + for (size_t pos = 0; pos < len; ++pos) { + if (pos == i) { + s.push_back('\\'); + expected.append("\\\\"); + } else if (pos == j) { + s.append("\xe2\x82\xac"); + expected.append(ascii ? "\\u20ac" : "\xe2\x82\xac"); + } else { + s.push_back('x'); + expected.push_back('x'); + } + } + expected.push_back('"'); + + EXPECT_EQ(folly::json::serialize(s, opts), expected) << ascii; + } + } + } + } +} + TEST(Json, JsonNonAsciiEncoding) { folly::json::serialization_opts opts; opts.encode_non_ascii = true; @@ -249,15 +306,10 @@ TEST(Json, JsonNonAsciiEncoding) { TEST(Json, UTF8Retention) { // test retention with valid utf8 strings - folly::fbstring input = "\u2665"; - folly::fbstring jsonInput = folly::toJson(input); - folly::fbstring output = folly::parseJson(jsonInput).asString(); - folly::fbstring jsonOutput = folly::toJson(output); - - LOG(INFO) << "input: " << input - <<" => json: " << jsonInput; - LOG(INFO) << "output: " << output - <<" => json: " << jsonOutput; + std::string input = u8"\u2665"; + std::string jsonInput = folly::toJson(input); + std::string output = folly::parseJson(jsonInput).asString(); + std::string jsonOutput = folly::toJson(output); EXPECT_EQ(input, output); EXPECT_EQ(jsonInput, jsonOutput); @@ -276,15 +328,10 @@ TEST(Json, UTF8EncodeNonAsciiRetention) { opts.encode_non_ascii = true; // test encode_non_ascii valid utf8 strings - folly::fbstring input = "\u2665"; - folly::fbstring jsonInput = folly::json::serialize(input, opts); - folly::fbstring output = folly::parseJson(jsonInput).asString(); - folly::fbstring jsonOutput = folly::json::serialize(output, opts); - - LOG(INFO) << "input: " << input - <<" => json: " << jsonInput; - LOG(INFO) << "output: " << output - <<" => json: " << jsonOutput; + std::string input = u8"\u2665"; + std::string jsonInput = folly::json::serialize(input, opts); + std::string output = folly::parseJson(jsonInput).asString(); + std::string jsonOutput = folly::json::serialize(output, opts); EXPECT_EQ(input, output); EXPECT_EQ(jsonInput, jsonOutput); @@ -312,101 +359,246 @@ TEST(Json, UTF8Validation) { // test validate_utf8 with invalid utf8 EXPECT_ANY_THROW(folly::json::serialize("a\xe0\xa0\x80z\xc0\x80", opts)); EXPECT_ANY_THROW(folly::json::serialize("a\xe0\xa0\x80z\xe0\x80\x80", opts)); -} -BENCHMARK(jsonSerialize, iters) { - folly::json::serialization_opts opts; - for (int i = 0; i < iters; ++i) { - folly::json::serialize( - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy", - opts); - } -} + opts.skip_invalid_utf8 = true; + EXPECT_EQ( + folly::json::serialize("a\xe0\xa0\x80z\xc0\x80", opts), + u8"\"a\xe0\xa0\x80z\ufffd\ufffd\""); + EXPECT_EQ( + folly::json::serialize("a\xe0\xa0\x80z\xc0\x80\x80", opts), + u8"\"a\xe0\xa0\x80z\ufffd\ufffd\ufffd\""); + EXPECT_EQ( + folly::json::serialize("z\xc0\x80z\xe0\xa0\x80", opts), + u8"\"z\ufffd\ufffdz\xe0\xa0\x80\""); -BENCHMARK(jsonSerializeWithNonAsciiEncoding, iters) { - folly::json::serialization_opts opts; opts.encode_non_ascii = true; + EXPECT_EQ(folly::json::serialize("a\xe0\xa0\x80z\xc0\x80", opts), + "\"a\\u0800z\\ufffd\\ufffd\""); + EXPECT_EQ(folly::json::serialize("a\xe0\xa0\x80z\xc0\x80\x80", opts), + "\"a\\u0800z\\ufffd\\ufffd\\ufffd\""); + EXPECT_EQ(folly::json::serialize("z\xc0\x80z\xe0\xa0\x80", opts), + "\"z\\ufffd\\ufffdz\\u0800\""); - for (int i = 0; i < iters; ++i) { - folly::json::serialize( - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy", - opts); - } } -BENCHMARK(jsonSerializeWithUtf8Validation, iters) { + +TEST(Json, ParseNonStringKeys) { + // test string keys + EXPECT_EQ("a", parseJson("{\"a\":[]}").items().begin()->first.asString()); + + // check that we don't allow non-string keys as this violates the + // strict JSON spec (though it is emitted by the output of + // folly::dynamic with operator <<). + EXPECT_THROW(parseJson("{1:[]}"), std::runtime_error); + + // check that we can parse colloquial JSON if the option is set folly::json::serialization_opts opts; - opts.validate_utf8 = true; + opts.allow_non_string_keys = true; - for (int i = 0; i < iters; ++i) { - folly::json::serialize( - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy" - "qwerty \xc2\x80 \xef\xbf\xbf poiuy", - opts); - } + auto val = parseJson("{1:[]}", opts); + EXPECT_EQ(1, val.items().begin()->first.asInt()); + + + // test we can still read in strings + auto sval = parseJson("{\"a\":[]}", opts); + EXPECT_EQ("a", sval.items().begin()->first.asString()); + + // test we can read in doubles + auto dval = parseJson("{1.5:[]}", opts); + EXPECT_EQ(1.5, dval.items().begin()->first.asDouble()); } -BENCHMARK(parseSmallStringWithUtf, iters) { - for (int i = 0; i < iters << 4; ++i) { - parseJson("\"I \\u2665 UTF-8 thjasdhkjh blah blah blah\""); - } +TEST(Json, ParseDoubleFallback) { + // default behavior + EXPECT_THROW(parseJson("{\"a\":847605071342477600000000000000}"), + std::range_error); + EXPECT_THROW(parseJson("{\"a\":-9223372036854775809}"), + std::range_error); + EXPECT_THROW(parseJson("{\"a\":9223372036854775808}"), + std::range_error); + EXPECT_EQ(std::numeric_limits::min(), + parseJson("{\"a\":-9223372036854775808}").items().begin() + ->second.asInt()); + EXPECT_EQ(std::numeric_limits::max(), + parseJson("{\"a\":9223372036854775807}").items().begin()->second.asInt()); + // with double_fallback + folly::json::serialization_opts opts; + opts.double_fallback = true; + EXPECT_EQ(847605071342477600000000000000.0, + parseJson("{\"a\":847605071342477600000000000000}", + opts).items().begin()->second.asDouble()); + EXPECT_EQ(847605071342477600000000000000.0, + parseJson("{\"a\": 847605071342477600000000000000}", + opts).items().begin()->second.asDouble()); + EXPECT_EQ(847605071342477600000000000000.0, + parseJson("{\"a\":847605071342477600000000000000 }", + opts).items().begin()->second.asDouble()); + EXPECT_EQ(847605071342477600000000000000.0, + parseJson("{\"a\": 847605071342477600000000000000 }", + opts).items().begin()->second.asDouble()); + EXPECT_EQ(std::numeric_limits::min(), + parseJson("{\"a\":-9223372036854775808}", + opts).items().begin()->second.asInt()); + EXPECT_EQ(std::numeric_limits::max(), + parseJson("{\"a\":9223372036854775807}", + opts).items().begin()->second.asInt()); + // show that some precision gets lost + EXPECT_EQ(847605071342477612345678900000.0, + parseJson("{\"a\":847605071342477612345678912345}", + opts).items().begin()->second.asDouble()); + EXPECT_EQ( + toJson(parseJson(R"({"a":-9223372036854775808})", opts)), + R"({"a":-9223372036854775808})"); } -BENCHMARK(parseNormalString, iters) { - for (int i = 0; i < iters << 4; ++i) { - parseJson("\"akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk\""); - } +TEST(Json, ParseNumbersAsStrings) { + folly::json::serialization_opts opts; + opts.parse_numbers_as_strings = true; + auto parse = [&](std::string number) { + return parseJson(number, opts).asString(); + }; + + EXPECT_EQ("0", parse("0")); + EXPECT_EQ("1234", parse("1234")); + EXPECT_EQ("3.00", parse("3.00")); + EXPECT_EQ("3.14", parse("3.14")); + EXPECT_EQ("0.1234", parse("0.1234")); + EXPECT_EQ("0.0", parse("0.0")); + EXPECT_EQ("46845131213548676854213265486468451312135486768542132", + parse("46845131213548676854213265486468451312135486768542132")); + EXPECT_EQ("-468451312135486768542132654864684513121354867685.5e4", + parse("-468451312135486768542132654864684513121354867685.5e4")); + EXPECT_EQ("6.62607004e-34", parse("6.62607004e-34")); + EXPECT_EQ("6.62607004E+34", parse("6.62607004E+34")); + EXPECT_EQ("Infinity", parse("Infinity")); + EXPECT_EQ("-Infinity", parse("-Infinity")); + EXPECT_EQ("NaN", parse("NaN")); + + EXPECT_THROW(parse("ThisIsWrong"), std::runtime_error); + EXPECT_THROW(parse("34-2"), std::runtime_error); + EXPECT_THROW(parse(""), std::runtime_error); + EXPECT_THROW(parse("-"), std::runtime_error); + EXPECT_THROW(parse("34-e2"), std::runtime_error); + EXPECT_THROW(parse("34e2.4"), std::runtime_error); + EXPECT_THROW(parse("infinity"), std::runtime_error); + EXPECT_THROW(parse("nan"), std::runtime_error); } -BENCHMARK(parseBigString, iters) { - for (int i = 0; i < iters; ++i) { - parseJson("\"" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "akjhfk jhkjlakjhfk jhkjlakjhfk jhkjl akjhfk" - "\""); - } +TEST(Json, SortKeys) { + folly::json::serialization_opts opts_on, opts_off, opts_custom_sort; + opts_on.sort_keys = true; + opts_off.sort_keys = false; + + opts_custom_sort.sort_keys = false; // should not be required + opts_custom_sort.sort_keys_by = []( + folly::dynamic const& a, folly::dynamic const& b) { + // just an inverse sort + return b < a; + }; + + dynamic value = dynamic::object + ("foo", "bar") + ("junk", 12) + ("another", 32.2) + ("a", + dynamic::array( + dynamic::object("a", "b") + ("c", "d"), + 12.5, + "Yo Dawg", + dynamic::array("heh"), + nullptr + ) + ) + ; + + std::string sorted_keys = + R"({"a":[{"a":"b","c":"d"},12.5,"Yo Dawg",["heh"],null],)" + R"("another":32.2,"foo":"bar","junk":12})"; + + std::string inverse_sorted_keys = + R"({"junk":12,"foo":"bar","another":32.2,)" + R"("a":[{"c":"d","a":"b"},12.5,"Yo Dawg",["heh"],null]})"; + + EXPECT_EQ(value, parseJson(folly::json::serialize(value, opts_on))); + EXPECT_EQ(value, parseJson(folly::json::serialize(value, opts_off))); + EXPECT_EQ(value, parseJson(folly::json::serialize(value, opts_custom_sort))); + + EXPECT_EQ(sorted_keys, folly::json::serialize(value, opts_on)); + EXPECT_NE(sorted_keys, folly::json::serialize(value, opts_off)); + EXPECT_EQ( + inverse_sorted_keys, folly::json::serialize(value, opts_custom_sort)); } -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - google::ParseCommandLineFlags(&argc, &argv, true); - if (FLAGS_benchmark) { - folly::runBenchmarks(); +TEST(Json, PrintTo) { + std::ostringstream oss; + + dynamic value = dynamic::object + ("foo", "bar") + ("junk", 12) + ("another", 32.2) + (true, false) // include non-string keys + (false, true) + (2, 3) + (0, 1) + (1, 2) + (1.5, 2.25) + (0.5, 0.25) + (0, 1) + (1, 2) + ("a", + dynamic::array( + dynamic::object("a", "b") + ("c", "d"), + 12.5, + "Yo Dawg", + dynamic::array("heh"), + nullptr + ) + ) + ; + + std::string expected = + R"({ + false : true, + true : false, + 0.5 : 0.25, + 1.5 : 2.25, + 0 : 1, + 1 : 2, + 2 : 3, + "a" : [ + { + "a" : "b", + "c" : "d" + }, + 12.5, + "Yo Dawg", + [ + "heh" + ], + null + ], + "another" : 32.2, + "foo" : "bar", + "junk" : 12 +})"; + PrintTo(value, &oss); + EXPECT_EQ(expected, oss.str()); +} + +TEST(Json, RecursionLimit) { + std::string in; + for (int i = 0; i < 1000; i++) { + in.append("{\"x\":"); } - return RUN_ALL_TESTS(); + in.append("\"hi\""); + for (int i = 0; i < 1000; i++) { + in.append("}"); + } + EXPECT_ANY_THROW(parseJson(in)); + + folly::json::serialization_opts opts_high_recursion_limit; + opts_high_recursion_limit.recursion_limit = 10000; + parseJson(in, opts_high_recursion_limit); }