/* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ // First include (the generated) my_config.h, to get correct platform defines. #include "my_config.h" #include "my_decimal.h" #include "sql_string.h" #include "sql_time.h" #include "json_binary.h" #include "json_dom.h" #include "base64.h" #include "template_utils.h" // down_cast #include #include "test_utils.h" #include #include /** Test Json_dom class hierarchy API, cf. json_dom.h */ namespace json_dom_unittest { class JsonDomTest : public ::testing::Test { protected: virtual void SetUp() { initializer.SetUp(); } virtual void TearDown() { initializer.TearDown(); } my_testing::Server_initializer initializer; }; /** Format a Json_dom object to JSON text using Json_wrapper's to_string functionality. @param d The DOM object to be formatted */ std::string format(const Json_dom &d) { String buffer; Json_wrapper w(d.clone()); EXPECT_FALSE(w.to_string(&buffer, true, "format")); return std::string(buffer.ptr(), buffer.length()); } std::string format(const Json_dom *ptr) { return format(*ptr); } TEST_F(JsonDomTest, BasicTest) { String buffer; /* string scalar */ const std::string std_s("abc"); Json_string s(std_s); EXPECT_EQ(std_s, s.value()); EXPECT_EQ(Json_dom::J_STRING, s.json_type()); EXPECT_TRUE(s.is_scalar()); EXPECT_EQ(1U, s.depth()); EXPECT_FALSE(s.is_number()); EXPECT_EQ(std::string("\"abc\""), format(s)); /* Escaping in strings, cf. ECMA-404 The JSON Data Interchange Format */ Json_array a; /* double quote and backslash */ Json_string js1(std::string("a\"b\\c")); a.append_clone(&js1); EXPECT_EQ(std::string("[\"a\\\"b\\\\c\"]"), format(a)); a.clear(); /* Printable control characters */ Json_string js2(std::string("a\b\f\n\r\tb")); a.append_clone(&js2); EXPECT_EQ(7U, static_cast(a[0])->size()); EXPECT_EQ(std::string("[\"a\\b\\f\\n\\r\\tb\"]"), format(a)); a.clear(); /* Unprintable control characters and non-ASCII Unicode characters */ Json_string js3(std::string("丳\x13" "丽\x3")); a.append_clone(&js3); EXPECT_EQ(std::string("[\"丳\\u0013丽\\u0003\"]"), format(a)); /* boolean scalar */ const Json_boolean jb(true); EXPECT_EQ(Json_dom::J_BOOLEAN, jb.json_type()); EXPECT_EQ(true, jb.value()); EXPECT_EQ(std::string("true"), format(jb)); /* Integer scalar */ const Json_int ji(-123); EXPECT_EQ(Json_dom::J_INT, ji.json_type()); EXPECT_EQ(-123, ji.value()); EXPECT_EQ(std::string("-123"), format(ji)); const Json_int max_32_int(2147483647); EXPECT_EQ(std::string("2147483647"), format(max_32_int)); const Json_int max_64_int(9223372036854775807LL); EXPECT_EQ(std::string("9223372036854775807"), format(max_64_int)); const Json_uint max_64_uint(18446744073709551615ULL); EXPECT_EQ(Json_dom::J_UINT, max_64_uint.json_type()); EXPECT_EQ(std::string("18446744073709551615"), format(max_64_uint)); /* Double scalar */ const Json_double jdb(-123.45); EXPECT_EQ(Json_dom::J_DOUBLE, jdb.json_type()); EXPECT_EQ(-123.45, jdb.value()); EXPECT_EQ(std::string("-123.45"), format(jdb)); /* Simple array with strings */ a.clear(); EXPECT_EQ(Json_dom::J_ARRAY, a.json_type()); EXPECT_FALSE(a.is_scalar()); EXPECT_EQ(0U, a.size()); Json_string js4(std::string("val1")); a.append_clone(&js4); Json_string js5(std::string("val2")); a.append_clone(&js5); EXPECT_EQ(2U, a.size()); EXPECT_EQ(std::string("[\"val1\", \"val2\"]"), format(a)); EXPECT_EQ(2U, a.depth()); Json_dom *elt0= a[0]; Json_dom *elt1= a[a.size() - 1]; EXPECT_EQ(std::string("\"val1\""), format(elt0)); EXPECT_EQ(std::string("\"val2\""), format(elt1)); /* Simple object with string values, iterator and array cloning */ Json_object o; EXPECT_EQ(Json_dom::J_OBJECT, o.json_type()); EXPECT_FALSE(a.is_scalar()); EXPECT_EQ(0U, o.cardinality()); Json_null null; EXPECT_EQ(Json_dom::J_NULL, null.json_type()); o.add_clone(std::string("key1"), &null); o.add_clone(std::string("key2"), &a); const std::string key_expected[]= {std::string("key1"), std::string("key2")}; const std::string value_expected[]= {std::string("null"), std::string("[\"val1\", \"val2\"]")}; int idx= 0; for (Json_object::const_iterator i= o.begin(); i != o.end(); ++i) { EXPECT_EQ(key_expected[idx], i->first); EXPECT_EQ(value_expected[idx], format(i->second)); idx++; } /* Test uniqueness of keys */ Json_string js6(std::string("should be discarded")); o.add_clone(std::string("key1"), &js6); EXPECT_EQ(2U, o.cardinality()); EXPECT_EQ(std::string("{\"key1\": null, \"key2\": [\"val1\", \"val2\"]}"), format(o)); EXPECT_EQ(3U, o.depth()); /* Nested array inside object and object inside array, * and object cloning */ Json_array level3; level3.append_clone(&o); Json_int ji2(123); level3.insert_clone(0U, &ji2); EXPECT_EQ(std::string("[123, {\"key1\": null, \"key2\": " "[\"val1\", \"val2\"]}]"), format(level3)); EXPECT_EQ(4U, level3.depth()); /* Array access: index */ Json_dom * const elt= level3[1]; EXPECT_EQ(std::string("{\"key1\": null, \"key2\": " "[\"val1\", \"val2\"]}"), format(elt)); /* Object access: key look-up */ EXPECT_EQ(Json_dom::J_OBJECT, elt->json_type()); Json_object * const object_elt= down_cast(elt); EXPECT_TRUE(object_elt != NULL); const Json_dom * const elt2= object_elt->get(std::string("key1")); EXPECT_EQ(std::string("null"), format(elt2)); /* Clear object. */ object_elt->clear(); EXPECT_EQ(0U, object_elt->cardinality()); /* Array remove element */ EXPECT_TRUE(level3.remove(1)); EXPECT_EQ(std::string("[123]"), format(level3)); EXPECT_FALSE(level3.remove(level3.size())); EXPECT_EQ(std::string("[123]"), format(level3)); /* Decimal scalar, including cloning */ my_decimal m; EXPECT_FALSE(double2my_decimal(0, 3.14, &m)); const Json_decimal jd(m); EXPECT_EQ(Json_dom::J_DECIMAL, jd.json_type()); EXPECT_TRUE(jd.is_number()); EXPECT_TRUE(jd.is_scalar()); const my_decimal m_out= *jd.value(); double m_d; double m_out_d; decimal2double(&m, &m_d); decimal2double(&m_out, &m_out_d); EXPECT_EQ(m_d, m_out_d); a.append_clone(&jd); std::auto_ptr b(static_cast(a.clone())); EXPECT_EQ(std::string("[\"val1\", \"val2\", 3.14]"), format(a)); EXPECT_EQ(std::string("[\"val1\", \"val2\", 3.14]"), format(b.get())); /* Array insert beyond end appends at end */ a.clear(); a.insert_alias(0, new (std::nothrow) Json_int(0)); a.insert_alias(2, new (std::nothrow) Json_int(2)); EXPECT_EQ(std::string("[0, 2]"), format(a)); a.clear(); a.insert_alias(0, new (std::nothrow) Json_int(0)); a.insert_alias(1, new (std::nothrow) Json_int(1)); EXPECT_EQ(std::string("[0, 1]"), format(a)); /* Array clear, null type, boolean literals, including cloning */ a.clear(); Json_null jn; Json_boolean jbf(false); Json_boolean jbt(true); a.append_clone(&jn); a.append_clone(&jbf); a.append_clone(&jbt); std::auto_ptr c(a.clone()); EXPECT_EQ(std::string("[null, false, true]"), format(a)); EXPECT_EQ(std::string("[null, false, true]"), format(c.get())); /* DATETIME scalar */ MYSQL_TIME dt; std::memset(&dt, 0, sizeof dt); MYSQL_TIME_STATUS status; EXPECT_FALSE(str_to_datetime(&my_charset_utf8mb4_bin, "19990412", 8, &dt, (my_time_flags_t)0, &status)); const Json_datetime scalar(dt, MYSQL_TYPE_DATETIME); EXPECT_EQ(Json_dom::J_DATETIME, scalar.json_type()); const MYSQL_TIME *dt_out= scalar.value(); EXPECT_FALSE(std::memcmp(&dt, dt_out, sizeof(MYSQL_TIME))); EXPECT_EQ(std::string("\"1999-04-12\""), format(scalar)); a.clear(); a.append_clone(&scalar); EXPECT_EQ(std::string("[\"1999-04-12\"]"), format(a)); EXPECT_FALSE(str_to_datetime(&my_charset_utf8mb4_bin, "14-11-15 12.04.55.123456", 24, &dt, (my_time_flags_t)0, &status)); Json_datetime scalar2(dt, MYSQL_TYPE_DATETIME); EXPECT_EQ(std::string("\"2014-11-15 12:04:55.123456\""), format(scalar2)); /* Opaque type storage scalar */ const uint32 i= 0xCAFEBABE; char i_as_char[4]; int4store(i_as_char, i); Json_opaque opaque(MYSQL_TYPE_TINY_BLOB, i_as_char, sizeof(i_as_char)); EXPECT_EQ(Json_dom::J_OPAQUE, opaque.json_type()); EXPECT_EQ(i, uint4korr(opaque.value())); EXPECT_EQ(MYSQL_TYPE_TINY_BLOB, opaque.type()); EXPECT_EQ(sizeof(i_as_char), opaque.size()); EXPECT_EQ(std::string("\"base64:type249:vrr+yg==\""), format(opaque)); const char *encoded= "vrr+yg=="; char *buff= new char[static_cast (base64_needed_decoded_length(static_cast (std::strlen(encoded))))]; EXPECT_EQ(4, base64_decode(encoded, std::strlen(encoded), buff, NULL, 0)); EXPECT_EQ(0xCAFEBABE, uint4korr(buff)); delete[] buff; /* Build DOM from JSON text using rapdjson */ const char *msg; size_t msg_offset; const char *sample_doc= "{\"abc\": 3, \"foo\": [1, 2, {\"foo\": 3.24}, null]}"; std::auto_ptr dom(Json_dom::parse(sample_doc, std::strlen(sample_doc), &msg, &msg_offset)); EXPECT_TRUE(dom.get() != NULL); EXPECT_EQ(4U, dom->depth()); EXPECT_EQ(std::string(sample_doc), format(dom.get())); const char *sample_array= "[3, {\"abc\": \"\\u0000inTheText\"}]"; dom.reset(Json_dom::parse(sample_array, std::strlen(sample_array), &msg, &msg_offset)); EXPECT_TRUE(dom.get() != NULL); EXPECT_EQ(3U, dom->depth()); EXPECT_EQ(std::string(sample_array), format(dom.get())); const char *sample_scalar_doc= "2"; dom.reset(Json_dom::parse(sample_scalar_doc, std::strlen(sample_scalar_doc), &msg, &msg_offset)); EXPECT_TRUE(dom.get() != NULL); EXPECT_EQ(std::string(sample_scalar_doc), format(dom.get())); const char *max_uint_scalar= "18446744073709551615"; dom.reset(Json_dom::parse(max_uint_scalar, std::strlen(max_uint_scalar), &msg, &msg_offset)); EXPECT_EQ(std::string(max_uint_scalar), format(dom.get())); /* Test that duplicate keys are eliminated, and that the returned keys are in the expected order (sorted on length before contents). */ const char *sample_object= "{\"key1\":1, \"key2\":2, \"key1\":3, " "\"key1\\u0000x\":4, \"key1\\u0000y\":5, \"a\":6, \"ab\":7, \"b\":8, " "\"\":9, \"\":10}"; const std::string expected[8][2]= { { "", "9" }, { "a", "6" }, { "b", "8" }, { "ab", "7" }, { "key1", "1" }, { "key2", "2" }, { std::string("key1\0x", 6), "4" }, { std::string("key1\0y", 6), "5" }, }; dom.reset(Json_dom::parse(sample_object, std::strlen(sample_object), &msg, &msg_offset)); EXPECT_TRUE(dom.get() != NULL); const Json_object *obj= down_cast(dom.get()); EXPECT_EQ(8U, obj->cardinality()); idx= 0; for (Json_object::const_iterator it= obj->begin(); it != obj->end(); ++it) { EXPECT_EQ(expected[idx][0], it->first); EXPECT_EQ(expected[idx][1], format(it->second)); idx++; } EXPECT_EQ(8, idx); /* Try to build DOM for JSON text using rapidjson on invalid text Included so we test error recovery */ const char *half_object_item= "{\"label\": "; dom.reset(Json_dom::parse(half_object_item, std::strlen(half_object_item), &msg, &msg_offset)); const Json_dom *null_dom= NULL; EXPECT_EQ(null_dom, dom.get()); const char *half_array_item= "[1,"; dom.reset(Json_dom::parse(half_array_item, std::strlen(half_array_item), &msg, &msg_offset)); EXPECT_EQ(null_dom, dom.get()); } /* Test that special characters are escaped when a Json_string is converted to text, so that it is possible to parse the resulting string. The JSON parser requires all characters in the range [0x00, 0x1F] and the characters " (double-quote) and \ (backslash) to be escaped. */ TEST_F(JsonDomTest, EscapeSpecialChars) { // Create a JSON string with all characters in the range [0, 127]. char input[128]; for (size_t i= 0; i < sizeof(input); ++i) input[i]= static_cast(i); const Json_string jstr(std::string(input, sizeof(input))); // Now convert that value from JSON to text and back to JSON. std::string str= format(jstr); std::auto_ptr dom(Json_dom::parse(str.c_str(), str.length(), NULL, NULL)); EXPECT_NE(static_cast(NULL), dom.get()); EXPECT_EQ(Json_dom::J_STRING, dom->json_type()); // Expect to get the same string back, including all the special characters. const Json_string *jstr2= down_cast(dom.get()); EXPECT_EQ(jstr.value(), jstr2->value()); } void vet_wrapper_length(char * text, size_t expected_length ) { const char *msg; size_t msg_offset; Json_dom *dom= Json_dom::parse(text, std::strlen(text), &msg, &msg_offset); Json_wrapper dom_wrapper(dom); EXPECT_EQ(expected_length, dom_wrapper.length()) << "Wrapped DOM: " << text << "\n"; String serialized_form; EXPECT_FALSE(json_binary::serialize(dom, &serialized_form)); json_binary::Value binary= json_binary::parse_binary(serialized_form.ptr(), serialized_form.length()); Json_wrapper binary_wrapper(binary); json_binary::Value::enum_type binary_type= binary.type(); if ((binary_type == json_binary::Value::ARRAY) || (binary_type == json_binary::Value::OBJECT)) { EXPECT_EQ(expected_length, binary.element_count()) << "BINARY: " << text << " and data = " << binary.get_data() << "\n"; } EXPECT_EQ(expected_length, binary_wrapper.length()) << "Wrapped BINARY: " << text << "\n"; } TEST_F(JsonDomTest, WrapperTest) { // Constructors, assignment, copy constructors, aliasing Json_dom *d= new (std::nothrow) Json_null(); Json_wrapper w(d); EXPECT_EQ(w.to_dom(), d); Json_wrapper w_2(w); EXPECT_NE(w.to_dom(), w_2.to_dom()); // deep copy Json_wrapper w_2b; EXPECT_TRUE(w_2b.empty()); w_2b= w; EXPECT_NE(w.to_dom(), w_2b.to_dom()); // deep copy w.set_alias(); // d is now "free" again Json_wrapper w_3(w); EXPECT_EQ(w.to_dom(), w_3.to_dom()); // alias copy w_3= w; EXPECT_EQ(w.to_dom(), w_3.to_dom()); // alias copy Json_wrapper w_4(d); // give d a new owner Json_wrapper w_5; w_5.steal(&w_4); // takes over d EXPECT_EQ(w_4.to_dom(), w_5.to_dom()); Json_wrapper w_6; EXPECT_EQ(Json_dom::J_ERROR, w_6.type()); EXPECT_EQ(0U, w_6.length()); EXPECT_EQ(0U, w_6.depth()); Json_dom *i= new (std::nothrow) Json_int(1); Json_wrapper w_7(i); w_5.steal(&w_7); // should deallocate w_5's original // scalars vet_wrapper_length((char *) "false", 1); vet_wrapper_length((char *) "true", 1); vet_wrapper_length((char *) "null", 1); vet_wrapper_length((char *) "1.1", 1); vet_wrapper_length((char *) "\"hello world\"", 1); // objects vet_wrapper_length((char *) "{}", 0); vet_wrapper_length((char *) "{ \"a\" : 100 }", 1); vet_wrapper_length((char *) "{ \"a\" : 100, \"b\" : 200 }", 2); // arrays vet_wrapper_length((char *) "[]", 0); vet_wrapper_length((char *) "[ 100 ]", 1); vet_wrapper_length((char *) "[ 100, 200 ]", 2); // nested objects vet_wrapper_length((char *) "{ \"a\" : 100, \"b\" : { \"c\" : 300 } }", 2); // nested arrays vet_wrapper_length((char *) "[ 100, [ 200, 300 ] ]", 2); } void vet_merge(char * left_text, char * right_text, std::string expected ) { const char *msg; size_t msg_offset; Json_dom *left_dom= Json_dom::parse(left_text, std::strlen(left_text), &msg, &msg_offset); Json_dom *right_dom= Json_dom::parse(right_text, std::strlen(right_text), &msg, &msg_offset); Json_dom *result_dom= merge_doms(left_dom, right_dom); EXPECT_EQ(expected, format(*result_dom)); delete result_dom; } TEST_F(JsonDomTest, MergeTest) { // merge 2 scalars { SCOPED_TRACE(""); vet_merge((char *) "1", (char *) "true", "[1, true]"); } // merge a scalar with an array { SCOPED_TRACE(""); vet_merge((char *) "1", (char *) "[true, false]", "[1, true, false]"); } // merge an array with a scalar { SCOPED_TRACE(""); vet_merge((char *) "[true, false]", (char *) "1", "[true, false, 1]"); } // merge a scalar with an object { SCOPED_TRACE(""); vet_merge((char *) "1", (char *) "{\"a\": 2}", "[1, {\"a\": 2}]"); } // merge an object with a scalar { SCOPED_TRACE(""); vet_merge((char *) "{\"a\": 2}", (char *) "1", "[{\"a\": 2}, 1]"); } // merge 2 arrays { SCOPED_TRACE(""); vet_merge((char *) "[1, 2]", (char *) "[3, 4]", "[1, 2, 3, 4]"); } // merge 2 objects { SCOPED_TRACE(""); vet_merge((char *) "{\"a\": 1, \"b\": 2 }", (char *) "{\"c\": 3, \"d\": 4 }", "{\"a\": 1, \"b\": 2, \"c\": 3, \"d\": 4}"); } // merge an array with an object { SCOPED_TRACE(""); vet_merge((char *) "[1, 2]", (char *) "{\"c\": 3, \"d\": 4 }", "[1, 2, {\"c\": 3, \"d\": 4}]"); } // merge an object with an array { SCOPED_TRACE(""); vet_merge((char *) "{\"c\": 3, \"d\": 4 }", (char *) "[1, 2]", "[{\"c\": 3, \"d\": 4}, 1, 2]"); } // merge two objects which share a key. scalar + scalar { SCOPED_TRACE(""); vet_merge((char *) "{\"a\": 1, \"b\": 2 }", (char *) "{\"b\": 3, \"d\": 4 }", "{\"a\": 1, \"b\": [2, 3], \"d\": 4}"); } // merge two objects which share a key. scalar + array { SCOPED_TRACE(""); vet_merge((char *) "{\"a\": 1, \"b\": 2 }", (char *) "{\"b\": [3, 4], \"d\": 4 }", "{\"a\": 1, \"b\": [2, 3, 4], \"d\": 4}"); } // merge two objects which share a key. array + scalar { SCOPED_TRACE(""); vet_merge((char *) "{\"a\": 1, \"b\": [2, 3] }", (char *) "{\"b\": 4, \"d\": 4 }", "{\"a\": 1, \"b\": [2, 3, 4], \"d\": 4}"); } // merge two objects which share a key. scalar + object { SCOPED_TRACE(""); vet_merge((char *) "{\"a\": 1, \"b\": 2 }", (char *) "{\"b\": {\"e\": 7, \"f\": 8}, \"d\": 4 }", "{\"a\": 1, \"b\": [2, {\"e\": 7, \"f\": 8}], \"d\": 4}"); } // merge two objects which share a key. object + scalar { SCOPED_TRACE(""); vet_merge((char *) (char *) "{\"b\": {\"e\": 7, \"f\": 8}, \"d\": 4 }", (char *) "{\"a\": 1, \"b\": 2 }", "{\"a\": 1, \"b\": [{\"e\": 7, \"f\": 8}, 2], \"d\": 4}"); } // merge two objects which share a key. array + array { SCOPED_TRACE(""); vet_merge((char *) "{\"a\": 1, \"b\": [2, 9] }", (char *) "{\"b\": [10, 11], \"d\": 4 }", "{\"a\": 1, \"b\": [2, 9, 10, 11], \"d\": 4}"); } // merge two objects which share a key. array + object { SCOPED_TRACE(""); vet_merge((char *) "{\"a\": 1, \"b\": [2, 9] }", (char *) "{\"b\": {\"e\": 7, \"f\": 8}, \"d\": 4 }", "{\"a\": 1, \"b\": [2, 9, {\"e\": 7, \"f\": 8}], \"d\": 4}"); } // merge two objects which share a key. object + array { SCOPED_TRACE(""); vet_merge((char *) (char *) "{\"b\": {\"e\": 7, \"f\": 8}, \"d\": 4 }", (char *) "{\"a\": 1, \"b\": [2, 9] }", "{\"a\": 1, \"b\": [{\"e\": 7, \"f\": 8}, 2, 9], \"d\": 4}"); } // merge two objects which share a key. object + object { SCOPED_TRACE(""); vet_merge((char *) (char *) "{\"b\": {\"e\": 7, \"f\": 8}, \"d\": 4 }", (char *) "{\"a\": 1, \"b\": {\"e\": 20, \"g\": 21 } }", "{\"a\": 1, \"b\": {\"e\": [7, 20], \"f\": 8, \"g\": 21}, " "\"d\": 4}"); } } } // namespace