763 lines
23 KiB
C++
763 lines
23 KiB
C++
/***
|
|
* ==++==
|
|
*
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* ==--==
|
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
|
*
|
|
* Tests for JSON parsing.
|
|
*
|
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
****/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include <array>
|
|
#include <iomanip>
|
|
|
|
#if defined(_WIN32) || defined(__APPLE__)
|
|
#include <regex>
|
|
#elif (defined(ANDROID) || defined(__ANDROID__))
|
|
#else
|
|
// GCC 4.8 doesn't support regex very well, fall back to Boost. Revist in GCC 4.9.
|
|
#include <boost/regex.hpp>
|
|
#endif
|
|
|
|
using namespace web; using namespace utility;
|
|
using namespace utility::conversions;
|
|
|
|
namespace tests { namespace functional { namespace json_tests {
|
|
|
|
inline bool verify_parsing_error_msg(const std::string &str)
|
|
{
|
|
#if defined(_WIN32) || defined(__APPLE__)
|
|
auto spattern = "^\\* Line \\d+, Column \\d+ Syntax error: .+";
|
|
static std::regex pattern(spattern);
|
|
return std::regex_match(str, pattern, std::regex_constants::match_flag_type::match_not_null);
|
|
#elif (defined(ANDROID) || defined(__ANDROID__))
|
|
return str.find("Syntax error: ") != std::string::npos;
|
|
#else
|
|
auto spattern = "^\\* Line \\d+, Column \\d+ Syntax error: .+";
|
|
static boost::regex pattern(spattern);
|
|
return boost::regex_match(str, pattern, boost::regex_constants::match_flag_type::match_not_null);
|
|
#endif
|
|
}
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma warning (disable: 4127) // const expression
|
|
#endif
|
|
#define VERIFY_PARSING_THROW(target) \
|
|
do { \
|
|
try { \
|
|
target; \
|
|
VERIFY_IS_TRUE(false); \
|
|
} \
|
|
catch (const json::json_exception &e) { \
|
|
VERIFY_IS_TRUE(verify_parsing_error_msg(e.what())); \
|
|
} \
|
|
catch (...) { \
|
|
VERIFY_IS_TRUE(false); \
|
|
} \
|
|
} while (false)
|
|
|
|
SUITE(parsing_tests)
|
|
{
|
|
|
|
TEST(stringstream_t)
|
|
{
|
|
utility::stringstream_t ss0;
|
|
ss0 << U("null");
|
|
json::value v0 = json::value::parse(ss0);
|
|
|
|
utility::stringstream_t ss1;
|
|
ss1 << U("17");
|
|
json::value v1 = json::value::parse(ss1);
|
|
|
|
utility::stringstream_t ss2;
|
|
ss2 << U("3.1415");
|
|
json::value v2 = json::value::parse(ss2);
|
|
|
|
utility::stringstream_t ss3;
|
|
ss3 << U("true");
|
|
json::value v3 = json::value::parse(ss3);
|
|
|
|
utility::stringstream_t ss4;
|
|
ss4 << U("\"Hello!\"");
|
|
json::value v4 = json::value::parse(ss4);
|
|
|
|
utility::stringstream_t ss8;
|
|
ss8 << U("{ \"a\" : 10 }");
|
|
json::value v8 = json::value::parse(ss8);
|
|
|
|
utility::stringstream_t ss9;
|
|
ss9 << U("[1,2,3,true]");
|
|
json::value v9 = json::value::parse(ss9);
|
|
|
|
VERIFY_ARE_EQUAL(v1.type(), json::value::Number);
|
|
VERIFY_ARE_EQUAL(v2.type(), json::value::Number);
|
|
VERIFY_ARE_EQUAL(v3.type(), json::value::Boolean);
|
|
VERIFY_ARE_EQUAL(v4.type(), json::value::String);
|
|
VERIFY_ARE_EQUAL(v8.type(), json::value::Object);
|
|
VERIFY_ARE_EQUAL(v9.type(), json::value::Array);
|
|
}
|
|
|
|
TEST(whitespace_failure)
|
|
{
|
|
VERIFY_PARSING_THROW(json::value::parse(U(" ")));
|
|
}
|
|
|
|
static const std::array<char, 4> whitespace_chars = { { 0x20, 0x09, 0x0A, 0x0D } };
|
|
|
|
TEST(whitespace_array)
|
|
{
|
|
// Try all the whitespace characters before/after all the structural characters
|
|
// whitespace characters according to RFC4627: space, horizontal tab, line feed or new line, carriage return
|
|
// structural characters: [{]}:,
|
|
|
|
// [,]
|
|
for(auto ch : whitespace_chars)
|
|
{
|
|
utility::string_t input;
|
|
input.append(2, ch);
|
|
input.append(U("["));
|
|
input.append(2, ch);
|
|
input.append(U("1"));
|
|
input.append(1, ch);
|
|
input.append(U(","));
|
|
input.append(4, ch);
|
|
input.append(U("2"));
|
|
input.append(1, ch);
|
|
input.append(U("]"));
|
|
input.append(2, ch);
|
|
json::value val = json::value::parse(input);
|
|
VERIFY_IS_TRUE(val.is_array());
|
|
VERIFY_ARE_EQUAL(U("1"), val[0].serialize());
|
|
VERIFY_ARE_EQUAL(U("2"), val[1].serialize());
|
|
}
|
|
}
|
|
|
|
TEST(whitespace_object)
|
|
{
|
|
// {:}
|
|
for(auto ch : whitespace_chars)
|
|
{
|
|
utility::string_t input;
|
|
input.append(2, ch);
|
|
input.append(U("{"));
|
|
input.append(2, ch);
|
|
input.append(U("\"1\""));
|
|
input.append(1, ch);
|
|
input.append(U(":"));
|
|
input.append(4, ch);
|
|
input.append(U("2"));
|
|
input.append(1, ch);
|
|
input.append(U("}"));
|
|
input.append(2, ch);
|
|
json::value val = json::value::parse(input);
|
|
VERIFY_IS_TRUE(val.is_object());
|
|
VERIFY_ARE_EQUAL(U("2"), val[U("1"]).serialize());
|
|
}
|
|
}
|
|
|
|
TEST(string_t)
|
|
{
|
|
json::value str = json::value::parse(U("\"\\\"\""));
|
|
VERIFY_ARE_EQUAL(U("\""), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\""));
|
|
VERIFY_ARE_EQUAL(U(""), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\\"ds\""));
|
|
VERIFY_ARE_EQUAL(U("\"ds"), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\\"\\\"\""));
|
|
VERIFY_ARE_EQUAL(U("\"\""), str.as_string());
|
|
|
|
// two character escapes
|
|
str = json::value::parse(U("\"\\\\\""));
|
|
VERIFY_ARE_EQUAL(U("\\"), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\/\""));
|
|
VERIFY_ARE_EQUAL(U("/"), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\b\""));
|
|
VERIFY_ARE_EQUAL(U("\b"), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\f\""));
|
|
VERIFY_ARE_EQUAL(U("\f"), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\n\""));
|
|
VERIFY_ARE_EQUAL(U("\n"), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\r\""));
|
|
VERIFY_ARE_EQUAL(U("\r"), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\t\""));
|
|
VERIFY_ARE_EQUAL(U("\t"), str.as_string());
|
|
}
|
|
|
|
TEST(escaped_unicode_string)
|
|
{
|
|
auto str = json::value::parse(U("\"\\u0041\""));
|
|
VERIFY_ARE_EQUAL(U("A"), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\u004B\""));
|
|
VERIFY_ARE_EQUAL(U("K"), str.as_string());
|
|
|
|
str = json::value::parse(U("\"\\u20AC\""));
|
|
// Euro sign as a hexidecmial UTF-8
|
|
const auto euro = to_string_t("\xE2\x82\xAC");
|
|
VERIFY_ARE_EQUAL(euro, str.as_string());
|
|
|
|
VERIFY_PARSING_THROW(json::value::parse(U("\"\\u0klB\"")));
|
|
}
|
|
|
|
TEST(escaping_control_characters)
|
|
{
|
|
std::vector<int> chars;
|
|
for (int i = 0; i <= 0x1F; ++i)
|
|
{
|
|
chars.push_back(i);
|
|
}
|
|
chars.push_back(0x5C); // backslash '\'
|
|
chars.push_back(0x22); // quotation '"'
|
|
|
|
for (int i : chars)
|
|
{
|
|
::utility::stringstream_t ss;
|
|
ss << U("\"\\u") << std::uppercase << std::setfill(U('0')) << std::setw(4) << std::hex << i << U("\"");
|
|
const auto &str = ss.str();
|
|
auto expectedStr = str;
|
|
if (i == 0x08)
|
|
{
|
|
expectedStr = U("\"\\b\"");
|
|
}
|
|
else if (i == 0x09)
|
|
{
|
|
expectedStr = U("\"\\t\"");
|
|
}
|
|
else if (i == 0x0A)
|
|
{
|
|
expectedStr = U("\"\\n\"");
|
|
}
|
|
else if (i == 0x0C)
|
|
{
|
|
expectedStr = U("\"\\f\"");
|
|
}
|
|
else if (i == 0x0D)
|
|
{
|
|
expectedStr = U("\"\\r\"");
|
|
}
|
|
else if (i == 0x5C)
|
|
{
|
|
expectedStr = U("\"\\\\\"");
|
|
}
|
|
else if (i == 0x22)
|
|
{
|
|
expectedStr = U("\"\\\"\"");
|
|
}
|
|
|
|
// Try constructing a json string value directly.
|
|
::utility::string_t schar;
|
|
schar.push_back(static_cast<::utility::string_t::value_type>(i));
|
|
const auto &sv = json::value::string(schar);
|
|
VERIFY_ARE_EQUAL(expectedStr, sv.serialize());
|
|
|
|
// Try parsing a string
|
|
const auto &v = json::value::parse(str);
|
|
VERIFY_IS_TRUE(v.is_string());
|
|
VERIFY_ARE_EQUAL(expectedStr, v.serialize());
|
|
|
|
// Try parsing a stringstream.
|
|
const auto &ssv = json::value::parse(ss);
|
|
VERIFY_ARE_EQUAL(expectedStr, ssv.serialize());
|
|
}
|
|
}
|
|
|
|
TEST(comments_string)
|
|
{
|
|
// Nothing but a comment
|
|
VERIFY_PARSING_THROW(json::value::parse(U(" /* There's nothing but a comment here */ ")));
|
|
VERIFY_PARSING_THROW(json::value::parse(U(" // There's nothing but a comment here\n")));
|
|
|
|
// Some invalid comments
|
|
VERIFY_PARSING_THROW(json::value::parse(U(" -22 /*/")));
|
|
VERIFY_PARSING_THROW(json::value::parse(U(" -22 /* /* nested */ */")));
|
|
|
|
// Correctly placed comments
|
|
json::value num1 = json::value::parse(U("-22 // This is a trailing comment\n"));
|
|
VERIFY_ARE_EQUAL(-22, num1.as_double());
|
|
num1 = json::value::parse(U(" -22 /* This is a trailing comment with a // nested\n comment */"));
|
|
VERIFY_ARE_EQUAL(-22, num1.as_double());
|
|
json::value num2 = json::value::parse(U("// This is a leading comment\n -22"));
|
|
VERIFY_ARE_EQUAL(-22, num2.as_double());
|
|
json::value num3 = json::value::parse(U("-22 /* This is a trailing comment */"));
|
|
VERIFY_ARE_EQUAL(-22, num3.as_double());
|
|
json::value num4 = json::value::parse(U("/* This is a leading comment */ -22"));
|
|
VERIFY_ARE_EQUAL(-22, num4.as_double());
|
|
json::value num5 = json::value::parse(U("-22 /***/"));
|
|
VERIFY_ARE_EQUAL(-22, num5.as_double());
|
|
|
|
json::value obj1 = json::value::parse(U("{// A comment in the middle of an empty object\n}"));
|
|
VERIFY_IS_TRUE(obj1.is_object());
|
|
VERIFY_ARE_EQUAL(0u, obj1.size());
|
|
json::value obj2 = json::value::parse(U("{/* A comment in the middle of an empty object */}"));
|
|
VERIFY_IS_TRUE(obj2.is_object());
|
|
VERIFY_ARE_EQUAL(0u, obj2.size());
|
|
json::value obj3 = json::value::parse(U("{ \"test\" : // A comment in the middle of a non-empty object\n 2}"));
|
|
VERIFY_IS_TRUE(obj3.is_object());
|
|
VERIFY_ARE_EQUAL(1u, obj3.size());
|
|
json::value obj4 = json::value::parse(U("{ \"test\" : /* A comment in the middle of a non-empty object */ 2}"));
|
|
VERIFY_IS_TRUE(obj4.is_object());
|
|
VERIFY_ARE_EQUAL(1u, obj4.size());
|
|
|
|
json::value arr1 = json::value::parse(U("[// A comment in the middle of an empty array\n]"));
|
|
VERIFY_IS_TRUE(arr1.is_array());
|
|
VERIFY_ARE_EQUAL(0u, arr1.size());
|
|
json::value arr2 = json::value::parse(U("[/* A comment in the middle of an empty array */]"));
|
|
VERIFY_IS_TRUE(arr2.is_array());
|
|
VERIFY_ARE_EQUAL(0u, arr2.size());
|
|
json::value arr3 = json::value::parse(U("[ 1, // A comment in the middle of a non-array\n 2]"));
|
|
VERIFY_IS_TRUE(arr3.is_array());
|
|
VERIFY_ARE_EQUAL(2u, arr3.size());
|
|
json::value arr4 = json::value::parse(U("[ 1, /* A comment in the middle of a non-empty array */ 2]"));
|
|
VERIFY_IS_TRUE(arr4.is_array());
|
|
VERIFY_ARE_EQUAL(2u, arr4.size());
|
|
}
|
|
|
|
TEST(comments_stream)
|
|
{
|
|
// Nothing but a comment
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U(" /* There's nothing but a comment here */ ");
|
|
VERIFY_PARSING_THROW(json::value::parse(stream));
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U(" // There's nothing but a comment here\n ");
|
|
VERIFY_PARSING_THROW(json::value::parse(stream));
|
|
}
|
|
|
|
// Some invalid comments
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U(" -22 /*/");
|
|
VERIFY_PARSING_THROW(json::value::parse(stream));
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U(" -22 /* /* nested */ */");
|
|
VERIFY_PARSING_THROW(json::value::parse(stream));
|
|
}
|
|
|
|
// Correctly placed comments
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("-22 // This is a trailing comment\n");
|
|
json::value num1 = json::value::parse(stream);
|
|
VERIFY_ARE_EQUAL(-22, num1.as_double());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U(" -22 /* This is a trailing comment with a // nested\n comment */");
|
|
json::value num1 = json::value::parse(stream);
|
|
VERIFY_ARE_EQUAL(-22, num1.as_double());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("// This is a leading comment\n -22");
|
|
json::value num2 = json::value::parse(stream);
|
|
VERIFY_ARE_EQUAL(-22, num2.as_double());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("-22 /* This is a trailing comment */");
|
|
json::value num3 = json::value::parse(stream);
|
|
VERIFY_ARE_EQUAL(-22, num3.as_double());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("/* This is a leading comment */ -22");
|
|
json::value num4 = json::value::parse(stream);
|
|
VERIFY_ARE_EQUAL(-22, num4.as_double());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("-22 /***/");
|
|
json::value num4 = json::value::parse(stream);
|
|
VERIFY_ARE_EQUAL(-22, num4.as_double());
|
|
}
|
|
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("{// A comment in the middle of an empty object\n}");
|
|
json::value obj1 = json::value::parse(stream);
|
|
VERIFY_IS_TRUE(obj1.is_object());
|
|
VERIFY_ARE_EQUAL(0u, obj1.size());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("{/* A comment in the middle of an empty object */}");
|
|
json::value obj2 = json::value::parse(stream);
|
|
VERIFY_IS_TRUE(obj2.is_object());
|
|
VERIFY_ARE_EQUAL(0u, obj2.size());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("{ \"test1\" : // A comment in the middle of a non-empty object\n 2}");
|
|
json::value obj3 = json::value::parse(stream);
|
|
VERIFY_IS_TRUE(obj3.is_object());
|
|
VERIFY_ARE_EQUAL(1u, obj3.size());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("{ \"test1\" : /* A comment in the middle of a non-empty object */ 2}");
|
|
json::value obj4 = json::value::parse(stream);
|
|
VERIFY_IS_TRUE(obj4.is_object());
|
|
VERIFY_ARE_EQUAL(1u, obj4.size());
|
|
}
|
|
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("[// A comment in the middle of an empty array\n]");
|
|
json::value arr1 = json::value::parse(stream);
|
|
VERIFY_IS_TRUE(arr1.is_array());
|
|
VERIFY_ARE_EQUAL(0u, arr1.size());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("[/* A comment in the middle of an empty array */]");
|
|
json::value arr2 = json::value::parse(stream);
|
|
VERIFY_IS_TRUE(arr2.is_array());
|
|
VERIFY_ARE_EQUAL(0u, arr2.size());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("[ 1, // A comment in the middle of a non-array\n 2]");
|
|
json::value arr3 = json::value::parse(stream);
|
|
VERIFY_IS_TRUE(arr3.is_array());
|
|
VERIFY_ARE_EQUAL(2u, arr3.size());
|
|
}
|
|
{
|
|
std::basic_stringstream<utility::char_t> stream;
|
|
stream << U("[ 1, /* A comment in the middle of a non-empty array */ 2]");
|
|
json::value arr4 = json::value::parse(stream);
|
|
VERIFY_IS_TRUE(arr4.is_array());
|
|
VERIFY_ARE_EQUAL(2u, arr4.size());
|
|
}
|
|
}
|
|
|
|
TEST(empty_object_array)
|
|
{
|
|
json::value obj = json::value::parse(U("{}"));
|
|
VERIFY_IS_TRUE(obj.is_object());
|
|
VERIFY_ARE_EQUAL(0u, obj.size());
|
|
|
|
json::value arr = json::value::parse(U("[]"));
|
|
VERIFY_IS_TRUE(arr.is_array());
|
|
VERIFY_ARE_EQUAL(0u, arr.size());
|
|
}
|
|
|
|
TEST(bug_416116)
|
|
{
|
|
json::value data2 = json::value::parse(U("\"δοκιμή\""));
|
|
auto s = data2.serialize();
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma warning( push )
|
|
#pragma warning( disable : 4566 )
|
|
#endif
|
|
VERIFY_ARE_EQUAL(s, U("\"δοκιμή\""));
|
|
#if defined(_MSC_VER)
|
|
#pragma warning( pop )
|
|
#endif
|
|
}
|
|
|
|
TEST(byte_ptr_parsing_array)
|
|
{
|
|
char s[] = "[ \"test1\",true]";
|
|
std::stringstream ss;
|
|
ss << s;
|
|
json::value v = json::value::parse(ss);
|
|
auto s2 = v.serialize();
|
|
|
|
VERIFY_ARE_EQUAL(s2, U("[\"test1\",true]"));
|
|
|
|
std::stringstream os;
|
|
v.serialize(os);
|
|
VERIFY_ARE_EQUAL(s2, to_string_t(os.str()));
|
|
}
|
|
|
|
TEST(byte_ptr_parsing_object)
|
|
{
|
|
char s[] = "{\"test1\":true }";
|
|
std::stringstream ss;
|
|
ss << s;
|
|
json::value v = json::value::parse(ss);
|
|
auto s2 = v.serialize();
|
|
|
|
VERIFY_ARE_EQUAL(s2, U("{\"test1\":true}"));
|
|
|
|
std::stringstream os;
|
|
v.serialize(os);
|
|
VERIFY_ARE_EQUAL(s2, to_string_t(os.str()));
|
|
}
|
|
|
|
TEST(Japanese)
|
|
{
|
|
utility::string_t ws = U("\"こんにちは\"");
|
|
std::string s = to_utf8string(ws);
|
|
|
|
std::stringstream ss;
|
|
ss << s;
|
|
json::value v = json::value::parse(ss);
|
|
auto s2 = v.serialize();
|
|
|
|
VERIFY_ARE_EQUAL(s2, ws);
|
|
|
|
std::stringstream os;
|
|
v.serialize(os);
|
|
VERIFY_ARE_EQUAL(s2, to_string_t(os.str()));
|
|
}
|
|
|
|
TEST(Russian)
|
|
{
|
|
utility::string_t ws = U("{\"results\":[{\"id\":272655310,\"name\":\"Андрей Ив´анов\"}]}");
|
|
json::value v1 = json::value::parse(ws);
|
|
auto s2 = v1.serialize();
|
|
|
|
VERIFY_ARE_EQUAL(s2, ws);
|
|
|
|
std::string s = to_utf8string(ws);
|
|
|
|
std::stringstream ss;
|
|
ss << s;
|
|
json::value v2 = json::value::parse(ss);
|
|
auto s3 = v2.serialize();
|
|
|
|
VERIFY_ARE_EQUAL(s3, ws);
|
|
}
|
|
|
|
utility::string_t make_deep_json_string(size_t depth)
|
|
{
|
|
utility::string_t strval;
|
|
for(size_t i=0; i<depth; ++i)
|
|
{
|
|
strval += U("{ \"a\" : 10, \"b\" : ");
|
|
}
|
|
strval += U("20");
|
|
for(size_t i=0; i<depth; ++i)
|
|
{
|
|
strval += U("}");
|
|
}
|
|
return strval;
|
|
}
|
|
|
|
TEST(deeply_nested)
|
|
{
|
|
#if defined(__APPLE__)
|
|
size_t safeDepth = 32;
|
|
size_t overDepth = 33;
|
|
#else
|
|
size_t safeDepth = 128;
|
|
size_t overDepth = 129;
|
|
#endif
|
|
|
|
// This should parse without issues:
|
|
auto strGood = make_deep_json_string(safeDepth);
|
|
json::value::parse(strGood);
|
|
|
|
// But this one should throw:
|
|
auto strBad = make_deep_json_string(overDepth);
|
|
VERIFY_PARSING_THROW(json::value::parse(strBad));
|
|
}
|
|
|
|
static bool compare_pairs(const std::pair<utility::string_t, json::value>& p1,
|
|
const std::pair<utility::string_t, json::value>& p2)
|
|
{
|
|
return p1.first < p2.first;
|
|
}
|
|
|
|
TEST(unsorted_object_parsing)
|
|
{
|
|
utility::stringstream_t ss;
|
|
ss << U("{\"z\":2, \"a\":1}");
|
|
json::value v = json::value::parse(ss);
|
|
auto& obj = v.as_object();
|
|
|
|
VERIFY_ARE_NOT_EQUAL(obj.find(U("a")), obj.end());
|
|
VERIFY_ARE_NOT_EQUAL(obj.find(U("z")), obj.end());
|
|
VERIFY_ARE_EQUAL(obj[U("a")], 1);
|
|
VERIFY_ARE_EQUAL(obj[U("z")], 2);
|
|
VERIFY_ARE_EQUAL(obj.size(), 2);
|
|
|
|
VERIFY_IS_TRUE(::std::is_sorted(obj.begin(), obj.end(), compare_pairs));
|
|
}
|
|
|
|
TEST(keep_order_while_parsing)
|
|
{
|
|
utility::stringstream_t ss;
|
|
ss << U("{\"k\":3, \"j\":2, \"i\":1}");
|
|
|
|
json::keep_object_element_order(true);
|
|
struct restore {
|
|
~restore() {
|
|
json::keep_object_element_order(false);
|
|
}
|
|
}_;
|
|
|
|
json::value v = json::value::parse(ss);
|
|
auto& obj = v.as_object();
|
|
|
|
// Make sure collection stays unsorted:
|
|
auto b = obj.begin();
|
|
VERIFY_ARE_EQUAL(b[0].first, U("k"));
|
|
VERIFY_ARE_EQUAL(b[1].first, U("j"));
|
|
VERIFY_ARE_EQUAL(b[2].first, U("i"));
|
|
|
|
// Make sure lookup still works:
|
|
auto val_i = obj[U("i")];
|
|
VERIFY_ARE_EQUAL(val_i.as_integer(), 1);
|
|
|
|
auto val_j = obj[U("j")];
|
|
VERIFY_ARE_EQUAL(val_j.as_integer(), 2);
|
|
|
|
// Make sure 'a' goes to the back of the collection, and
|
|
// can be looked up
|
|
obj[U("a")] = 4;
|
|
b = obj.begin();
|
|
VERIFY_ARE_EQUAL(b[3].first, U("a"));
|
|
VERIFY_ARE_EQUAL(obj[U("a")].as_integer(), 4);
|
|
}
|
|
|
|
TEST(non_default_locale, "Ignore:Android", "Locale unsupported on Android")
|
|
{
|
|
std::string originalLocale = setlocale(LC_ALL, nullptr);
|
|
#ifdef _WIN32
|
|
std::string changedLocale("fr-FR");
|
|
#else
|
|
std::string changedLocale("fr_FR.utf8");
|
|
#endif
|
|
|
|
// If locale isn't installed on system just silently pass.
|
|
if(setlocale(LC_ALL, changedLocale.c_str()) != nullptr)
|
|
{
|
|
// string serialize
|
|
utility::string_t str(U("[true,false,-1.55,5,null,{\"abc\":5555}]"));
|
|
json::value v = json::value::parse(str);
|
|
VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
|
|
VERIFY_ARE_EQUAL(str, v.serialize());
|
|
VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
|
|
|
|
setlocale(LC_ALL, originalLocale.c_str());
|
|
setlocale(LC_NUMERIC, changedLocale.c_str());
|
|
|
|
// cpprestsdk stream serialize
|
|
utility::stringstream_t stream;
|
|
stream << v;
|
|
utility::string_t serializedStr;
|
|
stream >> serializedStr;
|
|
VERIFY_ARE_EQUAL(str, serializedStr);
|
|
|
|
// std stream serialize
|
|
std::stringstream stdStream;
|
|
v.serialize(stdStream);
|
|
std::string stdStr;
|
|
stdStream >> stdStr;
|
|
VERIFY_ARE_EQUAL(str, utility::conversions::to_string_t(stdStr));
|
|
|
|
setlocale(LC_ALL, originalLocale.c_str());
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void error_code_helper(T &jsonData)
|
|
{
|
|
std::error_code err;
|
|
auto parsedObject = web::json::value::parse(jsonData, err);
|
|
VERIFY_IS_TRUE(err.value() == 0);
|
|
VERIFY_IS_TRUE(!parsedObject.is_null());
|
|
}
|
|
|
|
TEST(parse_overload_success)
|
|
{
|
|
std::error_code err;
|
|
utility::string_t valueStr(U("\"JSONString\""));
|
|
utility::string_t arrStr(U("[true,false,-1.55,5,null,{\"abc\":5555}]"));
|
|
utility::string_t objStr(U("{\"k\":3, \"j\":2, \"i\":1}"));
|
|
|
|
error_code_helper(valueStr);
|
|
error_code_helper(arrStr);
|
|
error_code_helper(objStr);
|
|
|
|
utility::stringstream_t valueStringStream;
|
|
utility::stringstream_t arrayStringStream;
|
|
utility::stringstream_t objStringStream;
|
|
|
|
valueStringStream << valueStr;
|
|
arrayStringStream << arrStr;
|
|
objStringStream << objStr;
|
|
|
|
error_code_helper(valueStringStream);
|
|
error_code_helper(arrayStringStream);
|
|
error_code_helper(objStringStream);
|
|
|
|
#ifdef _WIN32
|
|
std::wstringbuf buf;
|
|
|
|
buf.sputn(valueStr.c_str(), valueStr.size());
|
|
std::wistream valStream(&buf);
|
|
error_code_helper(valStream);
|
|
|
|
buf.sputn(arrStr.c_str(), arrStr.size());
|
|
std::wistream arrStream(&buf);
|
|
error_code_helper(arrStream);
|
|
|
|
buf.sputn(objStr.c_str(), objStr.size());
|
|
std::wistream objStream(&buf);
|
|
error_code_helper(objStream);
|
|
#endif
|
|
}
|
|
|
|
TEST(parse_overload_failed)
|
|
{
|
|
std::error_code err, streamErr, iStreamErr;
|
|
utility::string_t str(U("JSONString"));
|
|
utility::string_t arrStr(U("[true, false"));
|
|
json::value parsedObject = json::value::parse(str, err);
|
|
|
|
VERIFY_IS_TRUE(err.value() > 0);
|
|
VERIFY_IS_TRUE(parsedObject.is_null());
|
|
|
|
utility::stringstream_t stream;
|
|
stream << str;
|
|
|
|
parsedObject = json::value::parse(arrStr, streamErr);
|
|
VERIFY_IS_TRUE(streamErr.value() > 0);
|
|
VERIFY_IS_TRUE(parsedObject.is_null());
|
|
|
|
#ifdef _WIN32
|
|
std::wstringbuf buf;
|
|
buf.sputn(str.c_str(), str.size());
|
|
std::wistream iStream(&buf);
|
|
parsedObject = json::value::parse(str, iStreamErr);
|
|
VERIFY_IS_TRUE(iStreamErr.value() > 0);
|
|
VERIFY_IS_TRUE(parsedObject.is_null());
|
|
#endif
|
|
}
|
|
|
|
} // SUITE(parsing_tests)
|
|
|
|
}}}
|