654 lines
21 KiB
C++

/* 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 <gtest/gtest.h>
#include "test_utils.h"
#include <memory>
#include <cstring>
/**
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<Json_string *>(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<Json_object *>(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<Json_array> b(static_cast<Json_array *>(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<const Json_dom> 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<size_t>
(base64_needed_decoded_length(static_cast<int>
(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<Json_dom> 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<const Json_object *>(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<char>(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<Json_dom> dom(Json_dom::parse(str.c_str(), str.length(),
NULL, NULL));
EXPECT_NE(static_cast<Json_dom*>(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<const Json_string *>(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