/*
- * Copyright 2015 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.
* limitations under the License.
*/
#include <limits>
-#include <strstream>
#include <boost/next_prior.hpp>
+
#include <folly/json.h>
-#include <gflags/gflags.h>
-#include <gtest/gtest.h>
+#include <folly/portability/GTest.h>
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) {
("junk", 12)
("another", 32.2)
("a",
- {
+ dynamic::array(
dynamic::object("a", "b")
("c", "d"),
12.5,
"Yo Dawg",
- { "heh" },
+ dynamic::array("heh"),
nullptr
- }
+ )
)
;
on.allow_trailing_comma = true;
off.allow_trailing_comma = false;
- dynamic arr { 1, 2 };
+ 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));
}
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<folly::fbstring>(badDouble));
+ EXPECT_EQ(folly::toJson(badDouble), folly::to<std::string>(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<folly::fbstring>(okDouble));
+ EXPECT_EQ(folly::toJson(okDouble), folly::to<std::string>(okDouble));
}
TEST(Json, Produce) {
// Check Infinity/Nan
folly::json::serialization_opts opts;
opts.allow_nan_inf = true;
- EXPECT_EQ("Infinity",
- folly::json::serialize(parseJson("Infinity"), opts).toStdString());
- EXPECT_EQ("NaN",
- folly::json::serialize(parseJson("NaN"), opts).toStdString());
+ EXPECT_EQ("Infinity", folly::json::serialize(parseJson("Infinity"), opts));
+ EXPECT_EQ("NaN", folly::json::serialize(parseJson("NaN"), opts));
}
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;
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);
+ 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);
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);
+ 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);
EXPECT_ANY_THROW(folly::json::serialize("a\xe0\xa0\x80z\xe0\x80\x80", opts));
opts.skip_invalid_utf8 = true;
- EXPECT_EQ(folly::json::serialize("a\xe0\xa0\x80z\xc0\x80", opts),
- "\"a\xe0\xa0\x80z\ufffd\ufffd\"");
- EXPECT_EQ(folly::json::serialize("a\xe0\xa0\x80z\xc0\x80\x80", opts),
- "\"a\xe0\xa0\x80z\ufffd\ufffd\ufffd\"");
- EXPECT_EQ(folly::json::serialize("z\xc0\x80z\xe0\xa0\x80", opts),
- "\"z\ufffd\ufffdz\xe0\xa0\x80\"");
+ 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\"");
opts.encode_non_ascii = true;
EXPECT_EQ(folly::json::serialize("a\xe0\xa0\x80z\xc0\x80", opts),
EXPECT_EQ(847605071342477612345678900000.0,
parseJson("{\"a\":847605071342477612345678912345}",
opts).items().begin()->second.asDouble());
+ EXPECT_EQ(
+ toJson(parseJson(R"({"a":-9223372036854775808})", opts)),
+ R"({"a":-9223372036854775808})");
}
TEST(Json, ParseNumbersAsStrings) {
folly::json::serialization_opts opts;
opts.parse_numbers_as_strings = true;
- auto parse = [&](folly::fbstring number) {
+ auto parse = [&](std::string number) {
return parseJson(number, opts).asString();
};
}
TEST(Json, SortKeys) {
- folly::json::serialization_opts opts_on, opts_off;
+ 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",
- { "heh" },
+ dynamic::array("heh"),
nullptr
- }
+ )
)
;
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));
}
TEST(Json, PrintTo) {
- std::ostrstream oss;
+ std::ostringstream oss;
dynamic value = dynamic::object
("foo", "bar")
(0, 1)
(1, 2)
("a",
- {
+ dynamic::array(
dynamic::object("a", "b")
("c", "d"),
12.5,
"Yo Dawg",
- { "heh" },
+ dynamic::array("heh"),
nullptr
- }
+ )
)
;
EXPECT_EQ(expected, oss.str());
}
-int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
- gflags::ParseCommandLineFlags(&argc, &argv, true);
- return RUN_ALL_TESTS();
+TEST(Json, RecursionLimit) {
+ std::string in;
+ for (int i = 0; i < 1000; i++) {
+ in.append("{\"x\":");
+ }
+ 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);
}