1104 lines
30 KiB
C++
1104 lines
30 KiB
C++
#include <boost/property_tree/json_parser/detail/parser.hpp>
|
|
#include <boost/property_tree/json_parser/detail/narrow_encoding.hpp>
|
|
#include <boost/property_tree/json_parser/detail/wide_encoding.hpp>
|
|
#include <boost/property_tree/json_parser/detail/standard_callbacks.hpp>
|
|
#include "prefixing_callbacks.hpp"
|
|
|
|
#include <boost/core/lightweight_test.hpp>
|
|
|
|
#include <boost/property_tree/ptree.hpp>
|
|
#include <boost/range/iterator_range.hpp>
|
|
|
|
#include <cassert>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
template <typename Ch> struct encoding;
|
|
template <> struct encoding<char>
|
|
: json_parser::detail::utf8_utf8_encoding
|
|
{};
|
|
template <> struct encoding<wchar_t>
|
|
: json_parser::detail::wide_wide_encoding
|
|
{};
|
|
|
|
template <typename Callbacks, typename Ch>
|
|
struct test_parser
|
|
{
|
|
Callbacks callbacks;
|
|
::encoding<Ch> encoding;
|
|
typedef std::basic_string<Ch> string;
|
|
typedef basic_ptree<string, string> tree;
|
|
json_parser::detail::parser<Callbacks, ::encoding<Ch>,
|
|
typename string::const_iterator,
|
|
typename string::const_iterator>
|
|
parser;
|
|
|
|
test_parser() : parser(callbacks, encoding) {}
|
|
|
|
bool parse_null(const string& input, string& output) {
|
|
parser.set_input("", input);
|
|
bool result = parser.parse_null();
|
|
if (result) {
|
|
parser.finish();
|
|
output = callbacks.output().data();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool parse_boolean(const string& input, string& output) {
|
|
parser.set_input("", input);
|
|
bool result = parser.parse_boolean();
|
|
if (result) {
|
|
parser.finish();
|
|
output = callbacks.output().data();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool parse_number(const string& input, string& output) {
|
|
parser.set_input("", input);
|
|
bool result = parser.parse_number();
|
|
if (result) {
|
|
parser.finish();
|
|
output = callbacks.output().data();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool parse_string(const string& input, string& output) {
|
|
parser.set_input("", input);
|
|
bool result = parser.parse_string();
|
|
if (result) {
|
|
parser.finish();
|
|
output = callbacks.output().data();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool parse_array(const string& input, tree& output) {
|
|
parser.set_input("", input);
|
|
bool result = parser.parse_array();
|
|
if (result) {
|
|
parser.finish();
|
|
output = callbacks.output();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool parse_object(const string& input, tree& output) {
|
|
parser.set_input("", input);
|
|
bool result = parser.parse_object();
|
|
if (result) {
|
|
parser.finish();
|
|
output = callbacks.output();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void parse_value(const string& input, tree& output) {
|
|
parser.set_input("", input);
|
|
parser.parse_value();
|
|
parser.finish();
|
|
output = callbacks.output();
|
|
}
|
|
};
|
|
|
|
template <typename Ch>
|
|
struct standard_parser
|
|
: test_parser<
|
|
json_parser::detail::standard_callbacks<
|
|
basic_ptree<std::basic_string<Ch>, std::basic_string<Ch> > >,
|
|
Ch>
|
|
{};
|
|
|
|
template <typename Ch>
|
|
struct prefixing_parser
|
|
: test_parser<
|
|
prefixing_callbacks<
|
|
basic_ptree<std::basic_string<Ch>, std::basic_string<Ch> > >,
|
|
Ch>
|
|
{};
|
|
|
|
#define BOM_N "\xef\xbb\xbf"
|
|
#define BOM_W L"\xfeff"
|
|
|
|
static void
|
|
test_null_parse_result_is_input()
|
|
{
|
|
std::string parsed;
|
|
standard_parser<char> p;
|
|
BOOST_TEST(p.parse_null("null", parsed));
|
|
BOOST_TEST_EQ("null", parsed);
|
|
}
|
|
|
|
static void
|
|
test_uses_traits_from_null()
|
|
{
|
|
std::string parsed;
|
|
prefixing_parser<char> p;
|
|
BOOST_TEST(p.parse_null("null", parsed));
|
|
BOOST_TEST_EQ("_:null", parsed);
|
|
}
|
|
|
|
static void
|
|
test_null_parse_skips_bom()
|
|
{
|
|
std::string parsed;
|
|
standard_parser<char> p;
|
|
BOOST_TEST(p.parse_null(BOM_N "null", parsed));
|
|
BOOST_TEST_EQ("null", parsed);
|
|
}
|
|
|
|
static void
|
|
test_null_parse_result_is_input_w()
|
|
{
|
|
std::wstring parsed;
|
|
standard_parser<wchar_t> p;
|
|
BOOST_TEST(p.parse_null(L"null", parsed));
|
|
BOOST_TEST(parsed == L"null");
|
|
}
|
|
|
|
static void
|
|
test_uses_traits_from_null_w()
|
|
{
|
|
std::wstring parsed;
|
|
prefixing_parser<wchar_t> p;
|
|
BOOST_TEST(p.parse_null(L"null", parsed));
|
|
BOOST_TEST(parsed == L"_:null");
|
|
}
|
|
|
|
static void
|
|
test_null_parse_skips_bom_w()
|
|
{
|
|
std::wstring parsed;
|
|
standard_parser<wchar_t> p;
|
|
BOOST_TEST(p.parse_null(BOM_W L"null", parsed));
|
|
BOOST_TEST(parsed == L"null");
|
|
}
|
|
|
|
template<std::size_t N>
|
|
static void
|
|
test_boolean_parse_result_is_input_n(const std::string (¶m)[N])
|
|
{
|
|
for(std::size_t i = 0 ; i < N ; ++i)
|
|
{
|
|
std::string parsed;
|
|
standard_parser<char> p;
|
|
BOOST_TEST(p.parse_boolean(param[i], parsed));
|
|
BOOST_TEST_EQ(param[i], parsed);
|
|
}
|
|
}
|
|
|
|
const std::string
|
|
booleans_n[] = { "true", "false" };
|
|
|
|
static void
|
|
test_uses_traits_from_boolean_n()
|
|
{
|
|
std::string parsed;
|
|
prefixing_parser<char> p;
|
|
BOOST_TEST(p.parse_boolean("true", parsed));
|
|
BOOST_TEST_EQ("b:true", parsed);
|
|
}
|
|
|
|
template<std::size_t N>
|
|
static void
|
|
test_boolean_parse_result_is_input_w(const std::wstring (¶m)[N])
|
|
{
|
|
for(std::size_t i = 0 ; i < N ; ++i)
|
|
{
|
|
std::wstring parsed;
|
|
standard_parser<wchar_t> p;
|
|
BOOST_TEST(p.parse_boolean(param[i], parsed));
|
|
BOOST_TEST(param[i] == parsed);
|
|
}
|
|
}
|
|
|
|
const std::wstring
|
|
booleans_w[] = { L"true", L"false" };
|
|
|
|
static void
|
|
test_uses_traits_from_boolean_w()
|
|
{
|
|
std::wstring parsed;
|
|
prefixing_parser<wchar_t> p;
|
|
BOOST_TEST(p.parse_boolean(L"true", parsed));
|
|
BOOST_TEST(parsed == L"b:true");
|
|
}
|
|
|
|
template<std::size_t N>
|
|
static void
|
|
test_number_parse_result_is_input_n(std::string const (¶m)[N])
|
|
{
|
|
for(std::size_t i = 0 ; i < N ; ++i)
|
|
{
|
|
std::string parsed;
|
|
standard_parser<char> p;
|
|
BOOST_TEST(p.parse_number(param[i], parsed));
|
|
BOOST_TEST_EQ(param[i], parsed);
|
|
}
|
|
}
|
|
|
|
std::string const
|
|
numbers_n[] = {
|
|
"0",
|
|
"-0",
|
|
"1824",
|
|
"-0.1",
|
|
"123.142",
|
|
"1e+0",
|
|
"1E-0",
|
|
"1.1e134"
|
|
};
|
|
|
|
static void
|
|
test_uses_traits_from_number_n()
|
|
{
|
|
std::string parsed;
|
|
prefixing_parser<char> p;
|
|
BOOST_TEST(p.parse_number("12345", parsed));
|
|
BOOST_TEST_EQ("n:12345", parsed);
|
|
}
|
|
|
|
template<std::size_t N>
|
|
static void
|
|
test_number_parse_result_is_input_w(const std::wstring (¶m)[N])
|
|
{
|
|
for(std::size_t i = 0 ; i < N ; ++i)
|
|
{
|
|
std::wstring parsed;
|
|
standard_parser<wchar_t> p;
|
|
BOOST_TEST(p.parse_number(param[i], parsed));
|
|
BOOST_TEST(parsed == param[i]);
|
|
}
|
|
}
|
|
|
|
std::wstring const numbers_w[] = {
|
|
L"0",
|
|
L"-0",
|
|
L"1824",
|
|
L"-0.1",
|
|
L"123.142",
|
|
L"1e+0",
|
|
L"1E-0",
|
|
L"1.1e134"
|
|
};
|
|
|
|
static void
|
|
test_uses_traits_from_number_w()
|
|
{
|
|
std::wstring parsed;
|
|
prefixing_parser<wchar_t> p;
|
|
BOOST_TEST(p.parse_number(L"12345", parsed));
|
|
BOOST_TEST(parsed == L"n:12345");
|
|
}
|
|
|
|
struct string_input_n {
|
|
const char* encoded;
|
|
const char* expected;
|
|
};
|
|
|
|
template<std::size_t N>
|
|
void test_string_parsed_correctly_n(string_input_n const (¶m)[N])
|
|
{
|
|
for(std::size_t i = 0 ; i < N ; ++i)
|
|
{
|
|
std::string parsed;
|
|
standard_parser<char> p;
|
|
BOOST_TEST(p.parse_string(param[i].encoded, parsed));
|
|
BOOST_TEST_EQ(param[i].expected, parsed);
|
|
}
|
|
}
|
|
|
|
const string_input_n strings_n[] = {
|
|
{"\"\"", ""},
|
|
{"\"abc\"", "abc"},
|
|
{"\"a\\nb\"", "a\nb"},
|
|
{"\"\\\"\"", "\""},
|
|
{"\"\\\\\"", "\\"},
|
|
{"\"\\/\"", "/"},
|
|
{"\"\\b\"", "\b"},
|
|
{"\"\\f\"", "\f"},
|
|
{"\"\\r\"", "\r"},
|
|
{"\"\\t\"", "\t"},
|
|
{"\"\\u0001\\u00f2\\u28Ec\"", "\x01" "\xC3\xB2" "\xE2\xA3\xAC"},
|
|
{"\"\\ud801\\udc37\"", "\xf0\x90\x90\xb7"}, // U+10437
|
|
{"\xef\xbb\xbf\"\"", ""} // BOM
|
|
};
|
|
|
|
static void
|
|
test_uses_string_callbacks()
|
|
{
|
|
std::string parsed;
|
|
prefixing_parser<char> p;
|
|
BOOST_TEST(p.parse_string("\"a\"", parsed));
|
|
BOOST_TEST_EQ("s:a", parsed);
|
|
}
|
|
|
|
struct string_input_w {
|
|
const wchar_t* encoded;
|
|
const wchar_t* expected;
|
|
};
|
|
|
|
template<std::size_t N>
|
|
void
|
|
test_string_parsed_correctly_w(string_input_w const (¶m)[N])
|
|
{
|
|
for(std::size_t i = 0 ; i < N ; ++i)
|
|
{
|
|
std::wstring parsed;
|
|
standard_parser<wchar_t> p;
|
|
if(BOOST_TEST(p.parse_string(param[i].encoded, parsed)))
|
|
BOOST_TEST(param[i].expected == parsed);
|
|
}
|
|
}
|
|
|
|
const string_input_w strings_w[] = {
|
|
{L"\"\"", L""},
|
|
{L"\"abc\"", L"abc"},
|
|
{L"\"a\\nb\"", L"a\nb"},
|
|
{L"\"\\\"\"", L"\""},
|
|
{L"\"\\\\\"", L"\\"},
|
|
{L"\"\\/\"", L"/"},
|
|
{L"\"\\b\"", L"\b"},
|
|
{L"\"\\f\"", L"\f"},
|
|
{L"\"\\r\"", L"\r"},
|
|
{L"\"\\t\"", L"\t"},
|
|
{L"\"\\u0001\\u00f2\\u28Ec\"", L"\x0001" L"\x00F2" L"\x28EC"},
|
|
{L"\xfeff\"\"", L""} // BOM
|
|
};
|
|
|
|
static void
|
|
test_empty_array()
|
|
{
|
|
ptree tree;
|
|
standard_parser<char> p;
|
|
const char* input = " [ ]";
|
|
BOOST_TEST(p.parse_array(input, tree));
|
|
BOOST_TEST_EQ("", tree.data());
|
|
BOOST_TEST_EQ(0u, tree.size());
|
|
}
|
|
|
|
static void
|
|
test_array_gets_tagged()
|
|
{
|
|
wptree tree;
|
|
prefixing_parser<wchar_t> p;
|
|
const wchar_t* input = L" [ ]";
|
|
BOOST_TEST(p.parse_array(input, tree));
|
|
BOOST_TEST(tree.data() == L"a:");
|
|
BOOST_TEST_EQ(0u, tree.size());
|
|
}
|
|
|
|
static void
|
|
test_array_with_values()
|
|
{
|
|
wptree tree;
|
|
standard_parser<wchar_t> p;
|
|
const wchar_t* input = L"[\n"
|
|
L" 123, \"abc\" ,true ,\n"
|
|
L" null\n"
|
|
L" ]";
|
|
if(!BOOST_TEST(p.parse_array(input, tree)))
|
|
return;
|
|
if(!BOOST_TEST_EQ(4u, tree.size()))
|
|
return;
|
|
wptree::iterator it = tree.begin();
|
|
BOOST_TEST(it->first == L"");
|
|
BOOST_TEST(it->second.data() == L"123");
|
|
++it;
|
|
BOOST_TEST(it->first == L"");
|
|
BOOST_TEST(it->second.data() == L"abc");
|
|
++it;
|
|
BOOST_TEST(it->first == L"");
|
|
BOOST_TEST(it->second.data() == L"true");
|
|
++it;
|
|
BOOST_TEST(it->first == L"");
|
|
BOOST_TEST(it->second.data() == L"null");
|
|
++it;
|
|
BOOST_TEST(tree.end() == it);
|
|
}
|
|
|
|
static void
|
|
test_array_values_get_tagged()
|
|
{
|
|
ptree tree;
|
|
prefixing_parser<char> p;
|
|
const char* input = "[\n"
|
|
" 123, \"abc\" ,true ,\n"
|
|
" null\n"
|
|
" ]";
|
|
if(BOOST_TEST(p.parse_array(input, tree)))
|
|
if(BOOST_TEST_EQ(4u, tree.size()))
|
|
{
|
|
BOOST_TEST_EQ("a:", tree.data());
|
|
ptree::iterator it = tree.begin();
|
|
BOOST_TEST_EQ("", it->first);
|
|
BOOST_TEST_EQ("n:123", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("", it->first);
|
|
BOOST_TEST_EQ("s:abc", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("", it->first);
|
|
BOOST_TEST_EQ("b:true", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("", it->first);
|
|
BOOST_TEST_EQ("_:null", it->second.data());
|
|
++it;
|
|
BOOST_TEST(tree.end() == it);
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_nested_array()
|
|
{
|
|
ptree tree;
|
|
standard_parser<char> p;
|
|
const char* input = "[[1,2],3,[4,5]]";
|
|
if(!BOOST_TEST(p.parse_array(input, tree)))
|
|
return;
|
|
if(!BOOST_TEST_EQ(3u, tree.size()))
|
|
return;
|
|
ptree::iterator it = tree.begin();
|
|
BOOST_TEST_EQ("", it->first);
|
|
{
|
|
ptree& sub = it->second;
|
|
BOOST_TEST_EQ("", sub.data());
|
|
if(!BOOST_TEST_EQ(2u, sub.size()))
|
|
return;
|
|
ptree::iterator iit = sub.begin();
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("1", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("2", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST(sub.end() == iit);
|
|
}
|
|
++it;
|
|
BOOST_TEST_EQ("", it->first);
|
|
BOOST_TEST_EQ("3", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("", it->first);
|
|
{
|
|
ptree& sub = it->second;
|
|
BOOST_TEST_EQ("", sub.data());
|
|
if(!BOOST_TEST_EQ(2u, sub.size()))
|
|
return;
|
|
ptree::iterator iit = sub.begin();
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("4", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("5", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST(sub.end() == iit);
|
|
}
|
|
++it;
|
|
BOOST_TEST(tree.end() == it);
|
|
}
|
|
|
|
static void
|
|
test_empty_object()
|
|
{
|
|
ptree tree;
|
|
standard_parser<char> p;
|
|
const char* input = " { }";
|
|
if(BOOST_TEST(p.parse_object(input, tree)))
|
|
{
|
|
BOOST_TEST_EQ("", tree.data());
|
|
BOOST_TEST_EQ(0u, tree.size());
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_object_gets_tagged()
|
|
{
|
|
wptree tree;
|
|
prefixing_parser<wchar_t> p;
|
|
const wchar_t* input = L" { }";
|
|
if(BOOST_TEST(p.parse_object(input, tree)))
|
|
{
|
|
BOOST_TEST(tree.data() == L"o:");
|
|
BOOST_TEST_EQ(0u, tree.size());
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_object_with_values()
|
|
{
|
|
wptree tree;
|
|
standard_parser<wchar_t> p;
|
|
const wchar_t* input = L"{\n"
|
|
L" \"1\":123, \"2\"\n"
|
|
L" :\"abc\" ,\"3\": true ,\n"
|
|
L" \"4\" : null\n"
|
|
L" }";
|
|
if(BOOST_TEST(p.parse_object(input, tree)))
|
|
if(BOOST_TEST_EQ(4u, tree.size()))
|
|
{
|
|
wptree::iterator it = tree.begin();
|
|
BOOST_TEST(it->first == L"1");
|
|
BOOST_TEST(it->second.data() == L"123");
|
|
++it;
|
|
BOOST_TEST(it->first == L"2");
|
|
BOOST_TEST(it->second.data() == L"abc");
|
|
++it;
|
|
BOOST_TEST(it->first == L"3");
|
|
BOOST_TEST(it->second.data() == L"true");
|
|
++it;
|
|
BOOST_TEST(it->first == L"4");
|
|
BOOST_TEST(it->second.data() == L"null");
|
|
++it;
|
|
BOOST_TEST(tree.end() == it);
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_object_values_get_tagged()
|
|
{
|
|
ptree tree;
|
|
prefixing_parser<char> p;
|
|
const char* input = "{\n"
|
|
"\"1\": 123, \"2\": \"abc\" ,\"3\": true ,\n"
|
|
"\"4\": null\n"
|
|
"}";
|
|
if(BOOST_TEST(p.parse_object(input, tree)))
|
|
if(BOOST_TEST_EQ(4u, tree.size()))
|
|
{
|
|
BOOST_TEST_EQ("o:", tree.data());
|
|
ptree::iterator it = tree.begin();
|
|
BOOST_TEST_EQ("1", it->first);
|
|
BOOST_TEST_EQ("n:123", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("2", it->first);
|
|
BOOST_TEST_EQ("s:abc", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("3", it->first);
|
|
BOOST_TEST_EQ("b:true", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("4", it->first);
|
|
BOOST_TEST_EQ("_:null", it->second.data());
|
|
++it;
|
|
BOOST_TEST(tree.end() == it);
|
|
}
|
|
}
|
|
|
|
static void
|
|
test_nested_object()
|
|
{
|
|
ptree tree;
|
|
standard_parser<char> p;
|
|
const char* input = "{\"a\":{\"b\":1,\"c\":2},\"d\":3,\"e\":{\"f\":4,\"g\":5}}";
|
|
if(!BOOST_TEST(p.parse_object(input, tree)))
|
|
return;
|
|
if(!BOOST_TEST_EQ(3u, tree.size()))
|
|
return;
|
|
ptree::iterator it = tree.begin();
|
|
BOOST_TEST_EQ("a", it->first);
|
|
{
|
|
ptree& sub = it->second;
|
|
BOOST_TEST_EQ("", sub.data());
|
|
if(!BOOST_TEST_EQ(2u, sub.size()))
|
|
return;
|
|
ptree::iterator iit = sub.begin();
|
|
BOOST_TEST_EQ("b", iit->first);
|
|
BOOST_TEST_EQ("1", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("c", iit->first);
|
|
BOOST_TEST_EQ("2", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST(sub.end() == iit);
|
|
}
|
|
++it;
|
|
BOOST_TEST_EQ("d", it->first);
|
|
BOOST_TEST_EQ("3", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("e", it->first);
|
|
{
|
|
ptree& sub = it->second;
|
|
BOOST_TEST_EQ("", sub.data());
|
|
if(!BOOST_TEST_EQ(2u, sub.size()))
|
|
return;
|
|
ptree::iterator iit = sub.begin();
|
|
BOOST_TEST_EQ("f", iit->first);
|
|
BOOST_TEST_EQ("4", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("g", iit->first);
|
|
BOOST_TEST_EQ("5", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST(sub.end() == iit);
|
|
}
|
|
++it;
|
|
BOOST_TEST(tree.end() == it);
|
|
}
|
|
|
|
static void
|
|
test_array_in_object()
|
|
{
|
|
ptree tree;
|
|
standard_parser<char> p;
|
|
const char* input = "{\"a\":[1,2],\"b\":3,\"c\":[4,5]}";
|
|
if(!BOOST_TEST(p.parse_object(input, tree)))
|
|
return;
|
|
if(!BOOST_TEST_EQ(3u, tree.size()))
|
|
return;
|
|
ptree::iterator it = tree.begin();
|
|
BOOST_TEST_EQ("a", it->first);
|
|
{
|
|
ptree& sub = it->second;
|
|
BOOST_TEST_EQ("", sub.data());
|
|
if(BOOST_TEST_EQ(2u, sub.size()))
|
|
{
|
|
ptree::iterator iit = sub.begin();
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("1", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("2", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST(sub.end() == iit);
|
|
}
|
|
}
|
|
++it;
|
|
BOOST_TEST_EQ("b", it->first);
|
|
BOOST_TEST_EQ("3", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("c", it->first);
|
|
{
|
|
ptree& sub = it->second;
|
|
BOOST_TEST_EQ("", sub.data());
|
|
if(BOOST_TEST_EQ(2u, sub.size()))
|
|
{
|
|
ptree::iterator iit = sub.begin();
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("4", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("5", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST(sub.end() == iit);
|
|
}
|
|
}
|
|
++it;
|
|
BOOST_TEST(tree.end() == it);
|
|
}
|
|
|
|
static void
|
|
test_object_in_array()
|
|
{
|
|
ptree tree;
|
|
standard_parser<char> p;
|
|
const char* input = "[{\"a\":1,\"b\":2},3,{\"c\":4,\"d\":5}]";
|
|
if(!BOOST_TEST(p.parse_array(input, tree)))
|
|
return;
|
|
if(!BOOST_TEST_EQ(3u, tree.size()))
|
|
return;
|
|
ptree::iterator it = tree.begin();
|
|
BOOST_TEST_EQ("", it->first);
|
|
{
|
|
ptree& sub = it->second;
|
|
BOOST_TEST_EQ("", sub.data());
|
|
if(BOOST_TEST_EQ(2u, sub.size()))
|
|
{
|
|
ptree::iterator iit = sub.begin();
|
|
BOOST_TEST_EQ("a", iit->first);
|
|
BOOST_TEST_EQ("1", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("b", iit->first);
|
|
BOOST_TEST_EQ("2", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST(sub.end() == iit);
|
|
}
|
|
}
|
|
++it;
|
|
BOOST_TEST_EQ("", it->first);
|
|
BOOST_TEST_EQ("3", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("", it->first);
|
|
{
|
|
ptree& sub = it->second;
|
|
BOOST_TEST_EQ("", sub.data());
|
|
if(BOOST_TEST_EQ(2u, sub.size()))
|
|
{
|
|
ptree::iterator iit = sub.begin();
|
|
BOOST_TEST_EQ("c", iit->first);
|
|
BOOST_TEST_EQ("4", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("d", iit->first);
|
|
BOOST_TEST_EQ("5", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST(sub.end() == iit);
|
|
}
|
|
}
|
|
++it;
|
|
BOOST_TEST(tree.end() == it);
|
|
}
|
|
|
|
static void
|
|
test_parser_works_with_input_iterators()
|
|
{
|
|
const char* input = " {\n"
|
|
" \"1\":123, \"2\"\n"
|
|
" :\"abc\" ,\"3\": true ,\n"
|
|
" \"4\" : null, \"5\" : [ 1, 23\n"
|
|
" , 456 ]\n"
|
|
" }";
|
|
|
|
std::istringstream is(input);
|
|
typedef std::istreambuf_iterator<char> iterator;
|
|
json_parser::detail::standard_callbacks<ptree> callbacks;
|
|
json_parser::detail::utf8_utf8_encoding encoding;
|
|
json_parser::detail::parser<json_parser::detail::standard_callbacks<ptree>,
|
|
json_parser::detail::utf8_utf8_encoding,
|
|
iterator, iterator>
|
|
p(callbacks, encoding);
|
|
|
|
p.set_input("", boost::make_iterator_range(iterator(is), iterator()));
|
|
p.parse_value();
|
|
|
|
const ptree& tree = callbacks.output();
|
|
if(!BOOST_TEST_EQ(5u, tree.size()))
|
|
return;
|
|
ptree::const_iterator it = tree.begin();
|
|
BOOST_TEST_EQ("1", it->first);
|
|
BOOST_TEST_EQ("123", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("2", it->first);
|
|
BOOST_TEST_EQ("abc", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("3", it->first);
|
|
BOOST_TEST_EQ("true", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("4", it->first);
|
|
BOOST_TEST_EQ("null", it->second.data());
|
|
++it;
|
|
BOOST_TEST_EQ("5", it->first);
|
|
{
|
|
const ptree& sub = it->second;
|
|
BOOST_TEST_EQ("", sub.data());
|
|
if(!BOOST_TEST_EQ(3u, sub.size()))
|
|
return;
|
|
ptree::const_iterator iit = sub.begin();
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("1", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("23", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST_EQ("", iit->first);
|
|
BOOST_TEST_EQ("456", iit->second.data());
|
|
++iit;
|
|
BOOST_TEST(sub.end() == iit);
|
|
}
|
|
++it;
|
|
BOOST_TEST(tree.end() == it);
|
|
}
|
|
|
|
struct bad_parse_n {
|
|
const char* json;
|
|
const char* message_substring;
|
|
};
|
|
|
|
template<std::size_t N>
|
|
void test_parse_error_thrown_with_message_n(bad_parse_n const (¶m)[N])
|
|
{
|
|
for(std::size_t i = 0 ; i < N ; ++i)
|
|
{
|
|
try {
|
|
standard_parser<char> p;
|
|
ptree dummy;
|
|
p.parse_value(param[i].json, dummy);
|
|
BOOST_ERROR("expected exception");
|
|
} catch (json_parser::json_parser_error& e) {
|
|
std::string message = e.message();
|
|
if(message.find(param[i].message_substring) ==
|
|
std::string::npos)
|
|
{
|
|
std::ostringstream ss;
|
|
ss << "bad error message on input '" << param[i].json
|
|
<< "', need: '" << param[i].message_substring
|
|
<< "' but found '" << message << "'";
|
|
BOOST_ERROR(ss.str().c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const bad_parse_n errors_n[] = {
|
|
{"", "expected value"},
|
|
{"(", "expected value"},
|
|
|
|
{"n", "expected 'null'"},
|
|
{"nu", "expected 'null'"},
|
|
{"nul", "expected 'null'"},
|
|
{"n ", "expected 'null'"},
|
|
{"nu ", "expected 'null'"},
|
|
{"nul ", "expected 'null'"},
|
|
{"nx", "expected 'null'"},
|
|
{"nux", "expected 'null'"},
|
|
{"nulx", "expected 'null'"},
|
|
|
|
{"t", "expected 'true'"},
|
|
{"tr", "expected 'true'"},
|
|
{"tu", "expected 'true'"},
|
|
{"t ", "expected 'true'"},
|
|
{"tr ", "expected 'true'"},
|
|
{"tru ", "expected 'true'"},
|
|
{"tx", "expected 'true'"},
|
|
{"trx", "expected 'true'"},
|
|
{"trux", "expected 'true'"},
|
|
|
|
{"f", "expected 'false'"},
|
|
{"fa", "expected 'false'"},
|
|
{"fal", "expected 'false'"},
|
|
{"fals", "expected 'false'"},
|
|
{"f ", "expected 'false'"},
|
|
{"fa ", "expected 'false'"},
|
|
{"fal ", "expected 'false'"},
|
|
{"fals ", "expected 'false'"},
|
|
{"fx", "expected 'false'"},
|
|
{"fax", "expected 'false'"},
|
|
{"falx", "expected 'false'"},
|
|
{"falsx", "expected 'false'"},
|
|
|
|
{"-", "expected digits"},
|
|
{"01", "garbage after data"},
|
|
{"0.", "need at least one digit after '.'"},
|
|
{"0e", "need at least one digit in exponent"},
|
|
{"0e-", "need at least one digit in exponent"},
|
|
|
|
{"\"", "unterminated string"},
|
|
{"\"asd", "unterminated string"},
|
|
{"\"\n\"", "invalid code sequence"}, // control character
|
|
{"\"\xff\"", "invalid code sequence"}, // bad lead byte
|
|
{"\"\x80\"", "invalid code sequence"}, // stray trail byte
|
|
{"\"\xc0", "invalid code sequence"}, // eos after lead byte
|
|
{"\"\xc0\"", "invalid code sequence"}, // bad trail byte
|
|
{"\"\xc0m\"", "invalid code sequence"}, // also bad trail byte
|
|
{"\"\\", "invalid escape sequence"},
|
|
{"\"\\p\"", "invalid escape sequence"},
|
|
{"\"\\u", "invalid escape sequence"},
|
|
{"\"\\u\"", "invalid escape sequence"},
|
|
{"\"\\ug\"", "invalid escape sequence"},
|
|
{"\"\\u1\"", "invalid escape sequence"},
|
|
{"\"\\u1g\"", "invalid escape sequence"},
|
|
{"\"\\u11\"", "invalid escape sequence"},
|
|
{"\"\\u11g\"", "invalid escape sequence"},
|
|
{"\"\\u111\"", "invalid escape sequence"},
|
|
{"\"\\u111g\"", "invalid escape sequence"},
|
|
{"\"\\ude00\"", "stray low surrogate"},
|
|
{"\"\\ud900", "stray high surrogate"},
|
|
{"\"\\ud900foo\"", "stray high surrogate"},
|
|
{"\"\\ud900\\", "expected codepoint reference"},
|
|
{"\"\\ud900\\n\"", "expected codepoint reference"},
|
|
{"\"\\ud900\\u1000\"", "expected low surrogate"},
|
|
|
|
{"[", "expected value"},
|
|
{"[1", "expected ']' or ','"},
|
|
{"[1,", "expected value"},
|
|
{"[1,]", "expected value"},
|
|
{"[1}", "expected ']' or ','"},
|
|
|
|
{"{", "expected key string"},
|
|
{"{1:2}", "expected key string"},
|
|
{"{\"\"", "expected ':'"},
|
|
{"{\"\"}", "expected ':'"},
|
|
{"{\"\":", "expected value"},
|
|
{"{\"\":}", "expected value"},
|
|
{"{\"\":0", "expected '}' or ','"},
|
|
{"{\"\":0]", "expected '}' or ','"},
|
|
{"{\"\":0,", "expected key string"},
|
|
{"{\"\":0,}", "expected key string"},
|
|
};
|
|
|
|
struct bad_parse_w {
|
|
const wchar_t* json;
|
|
const char* message_substring;
|
|
};
|
|
|
|
struct do_narrow
|
|
{
|
|
char operator()(std::wstring::value_type w) const
|
|
{
|
|
unsigned long u = static_cast<unsigned long>(w);
|
|
if (u < 32 || u > 126)
|
|
return '?';
|
|
else
|
|
return static_cast<char>(u);
|
|
}
|
|
};
|
|
static std::string
|
|
make_narrow(std::wstring const& in)
|
|
{
|
|
std::string result(in.size(), ' ');
|
|
std::transform(in.begin(), in.end(), result.begin(), do_narrow());
|
|
return result;
|
|
}
|
|
|
|
template<std::size_t N>
|
|
void
|
|
test_parse_error_thrown_with_message_w(bad_parse_w const (¶m)[N])
|
|
{
|
|
for(std::size_t i = 0 ; i < N ; ++i)
|
|
{
|
|
try {
|
|
standard_parser<wchar_t> p;
|
|
wptree dummy;
|
|
p.parse_value(param[i].json, dummy);
|
|
BOOST_ERROR("expected exception");
|
|
} catch (json_parser::json_parser_error& e) {
|
|
std::string message = e.message();
|
|
if (message.find(param[i].message_substring) ==
|
|
std::string::npos)
|
|
{
|
|
std::ostringstream ss;
|
|
ss << "bad error message on input '" << make_narrow(param[i].json)
|
|
<< "', need: '" << param[i].message_substring
|
|
<< "' but found '" << message << "'";
|
|
BOOST_ERROR(ss.str().c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const bad_parse_w
|
|
errors_w[] = {
|
|
{L"", "expected value"},
|
|
{L"(", "expected value"},
|
|
|
|
{L"n", "expected 'null'"},
|
|
{L"nu", "expected 'null'"},
|
|
{L"nul", "expected 'null'"},
|
|
{L"n ", "expected 'null'"},
|
|
{L"nu ", "expected 'null'"},
|
|
{L"nul ", "expected 'null'"},
|
|
{L"nx", "expected 'null'"},
|
|
{L"nux", "expected 'null'"},
|
|
{L"nulx", "expected 'null'"},
|
|
|
|
{L"t", "expected 'true'"},
|
|
{L"tr", "expected 'true'"},
|
|
{L"tu", "expected 'true'"},
|
|
{L"t ", "expected 'true'"},
|
|
{L"tr ", "expected 'true'"},
|
|
{L"tru ", "expected 'true'"},
|
|
{L"tx", "expected 'true'"},
|
|
{L"trx", "expected 'true'"},
|
|
{L"trux", "expected 'true'"},
|
|
|
|
{L"f", "expected 'false'"},
|
|
{L"fa", "expected 'false'"},
|
|
{L"fal", "expected 'false'"},
|
|
{L"fals", "expected 'false'"},
|
|
{L"f ", "expected 'false'"},
|
|
{L"fa ", "expected 'false'"},
|
|
{L"fal ", "expected 'false'"},
|
|
{L"fals ", "expected 'false'"},
|
|
{L"fx", "expected 'false'"},
|
|
{L"fax", "expected 'false'"},
|
|
{L"falx", "expected 'false'"},
|
|
{L"falsx", "expected 'false'"},
|
|
|
|
{L"-", "expected digits"},
|
|
{L"01", "garbage after data"},
|
|
{L"0.", "need at least one digit after '.'"},
|
|
{L"0e", "need at least one digit in exponent"},
|
|
{L"0e-", "need at least one digit in exponent"},
|
|
|
|
{L"\"", "unterminated string"},
|
|
{L"\"asd", "unterminated string"},
|
|
{L"\"\n\"", "invalid code sequence"}, // control character
|
|
// Encoding not known, so no UTF-16 encoding error tests.
|
|
{L"\"\\", "invalid escape sequence"},
|
|
{L"\"\\p\"", "invalid escape sequence"},
|
|
{L"\"\\u", "invalid escape sequence"},
|
|
{L"\"\\u\"", "invalid escape sequence"},
|
|
{L"\"\\ug\"", "invalid escape sequence"},
|
|
{L"\"\\u1\"", "invalid escape sequence"},
|
|
{L"\"\\u1g\"", "invalid escape sequence"},
|
|
{L"\"\\u11\"", "invalid escape sequence"},
|
|
{L"\"\\u11g\"", "invalid escape sequence"},
|
|
{L"\"\\u111\"", "invalid escape sequence"},
|
|
{L"\"\\u111g\"", "invalid escape sequence"},
|
|
{L"\"\\ude00\"", "stray low surrogate"},
|
|
{L"\"\\ud900", "stray high surrogate"},
|
|
{L"\"\\ud900foo\"", "stray high surrogate"},
|
|
{L"\"\\ud900\\", "expected codepoint reference"},
|
|
{L"\"\\ud900\\n\"", "expected codepoint reference"},
|
|
{L"\"\\ud900\\u1000\"", "expected low surrogate"},
|
|
|
|
{L"[", "expected value"},
|
|
{L"[1", "expected ']' or ','"},
|
|
{L"[1,", "expected value"},
|
|
{L"[1,]", "expected value"},
|
|
{L"[1}", "expected ']' or ','"},
|
|
|
|
{L"{", "expected key string"},
|
|
{L"{1:2}", "expected key string"},
|
|
{L"{\"\"", "expected ':'"},
|
|
{L"{\"\"}", "expected ':'"},
|
|
{L"{\"\":", "expected value"},
|
|
{L"{\"\":}", "expected value"},
|
|
{L"{\"\":0", "expected '}' or ','"},
|
|
{L"{\"\":0]", "expected '}' or ','"},
|
|
{L"{\"\":0,", "expected key string"},
|
|
{L"{\"\":0,}", "expected key string"},
|
|
};
|
|
|
|
int main()
|
|
{
|
|
test_null_parse_result_is_input();
|
|
test_uses_traits_from_null();
|
|
test_null_parse_skips_bom();
|
|
test_null_parse_result_is_input_w();
|
|
test_null_parse_skips_bom_w();
|
|
test_uses_traits_from_boolean_n();
|
|
test_uses_traits_from_boolean_w();
|
|
test_boolean_parse_result_is_input_n(booleans_n);
|
|
test_boolean_parse_result_is_input_w(booleans_w);
|
|
test_number_parse_result_is_input_n(numbers_n);
|
|
test_number_parse_result_is_input_w(numbers_w);
|
|
test_uses_traits_from_number_n();
|
|
test_string_parsed_correctly_n(strings_n);
|
|
test_string_parsed_correctly_w(strings_w);
|
|
test_parse_error_thrown_with_message_n(errors_n);
|
|
test_parse_error_thrown_with_message_w(errors_w);
|
|
test_uses_string_callbacks();
|
|
test_empty_array();
|
|
test_array_with_values();
|
|
test_array_values_get_tagged();
|
|
test_nested_array();
|
|
test_empty_object();
|
|
test_object_gets_tagged();
|
|
test_object_with_values();
|
|
test_object_values_get_tagged();
|
|
test_nested_object();
|
|
test_array_in_object();
|
|
test_object_in_array();
|
|
test_parser_works_with_input_iterators();
|
|
test_uses_traits_from_null_w();
|
|
test_uses_traits_from_number_w();
|
|
test_array_gets_tagged();
|
|
return boost::report_errors();
|
|
}
|
|
|
|
/*
|
|
test_suite* init_unit_test_suite(int, char*[])
|
|
{
|
|
master_test_suite_t& ts = boost::unit_test::framework::master_test_suite();
|
|
ts.add(ARRAY_TEST_CASE(boolean_parse_result_is_input_n, booleans_n));
|
|
ts.add(ARRAY_TEST_CASE(boolean_parse_result_is_input_w, booleans_w));
|
|
ts.add(ARRAY_TEST_CASE(number_parse_result_is_input_n, numbers_n));
|
|
ts.add(ARRAY_TEST_CASE(number_parse_result_is_input_w, numbers_w));
|
|
ts.add(ARRAY_TEST_CASE(string_parsed_correctly_n, strings_n));
|
|
ts.add(ARRAY_TEST_CASE(string_parsed_correctly_w, strings_w));
|
|
ts.add(ARRAY_TEST_CASE(parse_error_thrown_with_message_n, errors_n));
|
|
ts.add(ARRAY_TEST_CASE(parse_error_thrown_with_message_w, errors_w));
|
|
|
|
return 0;
|
|
}
|
|
*/ |