diff options
Diffstat (limited to 'src/boost/libs/json/test/test.hpp')
-rw-r--r-- | src/boost/libs/json/test/test.hpp | 1163 |
1 files changed, 1163 insertions, 0 deletions
diff --git a/src/boost/libs/json/test/test.hpp b/src/boost/libs/json/test/test.hpp new file mode 100644 index 000000000..fe6dd540c --- /dev/null +++ b/src/boost/libs/json/test/test.hpp @@ -0,0 +1,1163 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +#ifndef BOOST_JSON_TEST_HPP +#define BOOST_JSON_TEST_HPP + +#include <boost/json/basic_parser.hpp> +#include <boost/json/value.hpp> +#include <boost/json/serializer.hpp> +#include <boost/json/storage_ptr.hpp> +#include <boost/json/detail/format.hpp> + +#include <cstddef> +#include <iterator> +#include <memory> +#include <type_traits> + +#include "test_suite.hpp" + +BOOST_JSON_NS_BEGIN + +//---------------------------------------------------------- + +struct test_failure : std::exception +{ + virtual + char const* + what() const noexcept override + { + return "test failure"; + } +}; + +struct fail_resource + : memory_resource +{ + std::size_t fail_max = 0; + std::size_t fail = 0; + std::size_t nalloc = 0; + std::size_t bytes = 0; + + ~fail_resource() + { + BOOST_TEST(nalloc == 0); + } + + void* + do_allocate( + std::size_t n, + std::size_t) override + { + if(++fail == fail_max) + { + ++fail_max; + fail = 0; + throw test_failure{}; + } + auto p = ::operator new(n); + ++nalloc; + bytes += n; + return p; + } + + void + do_deallocate( + void* p, + std::size_t n, + std::size_t) noexcept override + { + if(BOOST_TEST(nalloc > 0)) + --nalloc; + bytes -= n; + ::operator delete(p); + } + + bool + do_is_equal( + memory_resource const& mr) const noexcept override + { + return this == &mr; + } +}; + +template<class F> +void +fail_loop(F&& f) +{ + fail_resource ss; + ss.fail_max = 1; + while(ss.fail < 200) + { + try + { + f(&ss); + } + catch(test_failure const&) + { + continue; + } + break; + } + BOOST_TEST(ss.fail < 200); +} + +//---------------------------------------------------------- + +struct unique_resource + : memory_resource +{ + unique_resource() = default; + + void* + do_allocate( + std::size_t n, + std::size_t) override + { + return ::operator new(n); + } + + void + do_deallocate( + void* p, + std::size_t, + std::size_t) noexcept override + { + return ::operator delete(p); + } + + bool + do_is_equal( + memory_resource const& mr) const noexcept override + { + return this == &mr; + } +}; + +//---------------------------------------------------------- + +// The null parser discards all the data + +class null_parser +{ + struct handler + { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + + bool on_document_begin( error_code& ) { return true; } + bool on_document_end( error_code& ) { return true; } + bool on_object_begin( error_code& ) { return true; } + bool on_object_end( std::size_t, error_code& ) { return true; } + bool on_array_begin( error_code& ) { return true; } + bool on_array_end( std::size_t, error_code& ) { return true; } + bool on_key_part( string_view, std::size_t, error_code& ) { return true; } + bool on_key( string_view, std::size_t, error_code& ) { return true; } + bool on_string_part( string_view, std::size_t, error_code& ) { return true; } + bool on_string( string_view, std::size_t, error_code& ) { return true; } + bool on_number_part( string_view, error_code&) { return true; } + bool on_int64( std::int64_t, string_view, error_code& ) { return true; } + bool on_uint64( std::uint64_t, string_view, error_code& ) { return true; } + bool on_double( double, string_view, error_code& ) { return true; } + bool on_bool( bool, error_code& ) { return true; } + bool on_null( error_code& ) { return true; } + bool on_comment_part( string_view, error_code& ) { return true; } + bool on_comment( string_view, error_code& ) { return true; } + }; + + basic_parser<handler> p_; + +public: + null_parser() + : p_(parse_options()) + { + } + + explicit + null_parser(parse_options po) + : p_(po) + { + } + + void + reset() + { + p_.reset(); + } + + std::size_t + write( + char const* data, + std::size_t size, + error_code& ec) + { + auto const n = p_.write_some( + false, data, size, ec); + if(! ec && n < size) + ec = error::extra_data; + return n; + } +}; + +//---------------------------------------------------------- + +class fail_parser +{ + struct handler + { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + + std::size_t n; + + handler() + : n(std::size_t(-1)) + { + } + + bool + maybe_fail(error_code& ec) + { + if(n && --n > 0) + return true; + ec = error::test_failure; + return false; + } + + bool + on_document_begin( + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_document_end( + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_object_begin( + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_object_end( + std::size_t, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_array_begin( + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_array_end( + std::size_t, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_key_part( + string_view, + std::size_t, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_key( + string_view, + std::size_t, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_string_part( + string_view, + std::size_t, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_string( + string_view, + std::size_t, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_number_part( + string_view, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_int64( + int64_t, + string_view, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_uint64( + uint64_t, + string_view, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_double( + double, + string_view, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_bool( + bool, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_null(error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_comment_part( + string_view, + error_code& ec) + { + return maybe_fail(ec); + } + + bool + on_comment( + string_view, + error_code& ec) + { + return maybe_fail(ec); + } + }; + + basic_parser<handler> p_; + +public: + fail_parser() + : p_(parse_options()) + { + } + + explicit + fail_parser( + std::size_t n, + parse_options po = parse_options()) + : p_(po) + { + p_.handler().n = n; + } + + explicit + fail_parser(parse_options po) + : p_(po) + { + } + + void + reset() + { + p_.reset(); + } + + bool + done() const noexcept + { + return p_.done(); + } + + std::size_t + write_some( + bool more, + char const* data, + std::size_t size, + error_code& ec) + { + return p_.write_some( + more, data, size, ec); + } + + std::size_t + write( + bool more, + char const* data, + std::size_t size, + error_code& ec) + { + auto const n = p_.write_some( + more, data, size, ec); + if(! ec && n < size) + ec = error::extra_data; + return n; + } +}; + +//---------------------------------------------------------- + +struct test_exception + : std::exception +{ + char const* + what() const noexcept + { + return "test exception"; + } +}; + +// Exercises every exception path +class throw_parser +{ + struct handler + { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + + std::size_t n; + + handler() + : n(std::size_t(-1)) + { + } + + bool + maybe_throw() + { + if(n && --n > 0) + return true; + throw test_exception{}; + } + + bool + on_document_begin( + error_code&) + { + return maybe_throw(); + } + + bool + on_document_end( + error_code&) + { + return maybe_throw(); + } + + bool + on_object_begin( + error_code&) + { + return maybe_throw(); + } + + bool + on_object_end( + std::size_t, + error_code&) + { + return maybe_throw(); + } + + bool + on_array_begin( + error_code&) + { + return maybe_throw(); + } + + bool + on_array_end( + std::size_t, + error_code&) + { + return maybe_throw(); + } + + bool + on_key_part( + string_view, + std::size_t, + error_code&) + { + return maybe_throw(); + } + + bool + on_key( + string_view, + std::size_t, + error_code&) + { + return maybe_throw(); + } + + bool + on_string_part( + string_view, + std::size_t, + error_code&) + { + return maybe_throw(); + } + + bool + on_string( + string_view, + std::size_t, + error_code&) + { + return maybe_throw(); + } + + bool + on_number_part( + string_view, + error_code&) + { + return maybe_throw(); + } + + bool + on_int64( + int64_t, + string_view, + error_code&) + { + return maybe_throw(); + } + + bool + on_uint64( + uint64_t, + string_view, + error_code&) + { + return maybe_throw(); + } + + bool + on_double( + double, + string_view, + error_code&) + { + return maybe_throw(); + } + + bool + on_bool( + bool, + error_code&) + { + return maybe_throw(); + } + + bool + on_null(error_code&) + { + return maybe_throw(); + } + + bool + on_comment_part( + string_view, + error_code&) + { + return maybe_throw(); + } + + bool + on_comment( + string_view, + error_code&) + { + return maybe_throw(); + } + }; + + basic_parser<handler> p_; + +public: + throw_parser() + : p_(parse_options()) + { + } + + explicit + throw_parser( + std::size_t n, + parse_options po = parse_options()) + : p_(po) + { + p_.handler().n = n; + } + + explicit + throw_parser(parse_options po) + : p_(po) + { + } + + void + reset() noexcept + { + p_.reset(); + } + + std::size_t + write( + bool more, + char const* data, + std::size_t size, + error_code& ec) + { + auto const n = p_.write_some( + more, data, size, ec); + if(! ec && n < size) + ec = error::extra_data; + return n; + } +}; + +//---------------------------------------------------------- + +// wrap an iterator to make an input iterator +template<class FwdIt> +class input_iterator +{ + FwdIt it_; + +public: + using value_type = typename std::iterator_traits<FwdIt>::value_type; + using pointer = typename std::iterator_traits<FwdIt>::pointer; + using reference = typename std::iterator_traits<FwdIt>::reference; + using difference_type = typename std::iterator_traits<FwdIt>::difference_type; + using iterator_category = std::input_iterator_tag; + + input_iterator() = default; + input_iterator(input_iterator const&) = default; + input_iterator& operator=( + input_iterator const&) = default; + + input_iterator(FwdIt it) + : it_(it) + { + } + + input_iterator& + operator++() noexcept + { + ++it_; + return *this; + } + + input_iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + pointer + operator->() const noexcept + { + return it_.operator->(); + } + + reference + operator*() const noexcept + { + return *it_; + } + + bool + operator==(input_iterator other) const noexcept + { + return it_ == other.it_; + } + + bool + operator!=(input_iterator other) const noexcept + { + return it_ != other.it_; + } +}; + +template<class FwdIt> +input_iterator<FwdIt> +make_input_iterator(FwdIt it) +{ + return input_iterator<FwdIt>(it); +} + +//---------------------------------------------------------- + +inline +bool +equal_storage( + value const& v, + storage_ptr const& sp); + +inline +bool +equal_storage( + object const& o, + storage_ptr const& sp) +{ + if(*o.storage() != *sp) + return false; + for(auto const& e : o) + if(! equal_storage(e.value(), sp)) + return false; + return true; +} + +inline +bool +equal_storage( + array const& a, + storage_ptr const& sp) +{ + if(*a.storage() != *sp) + return false; + for(auto const& v : a) + if(! equal_storage(v, sp)) + return false; + return true; +} + +bool +equal_storage( + value const& v, + storage_ptr const& sp) +{ + switch(v.kind()) + { + case json::kind::object: + if(*v.as_object().storage() != *sp) + return false; + return equal_storage(v.as_object(), sp); + + case json::kind::array: + if(*v.as_array().storage() != *sp) + return false; + return equal_storage(v.as_array(), sp); + + case json::kind::string: + return *v.as_string().storage() == *sp; + + case json::kind::int64: + case json::kind::uint64: + case json::kind::double_: + case json::kind::bool_: + case json::kind::null: + break; + } + + return *v.storage() == *sp; +} + +inline +void +check_storage( + object const& o, + storage_ptr const& sp) +{ + BOOST_TEST(equal_storage(o, sp)); +} + +inline +void +check_storage( + array const& a, + storage_ptr const& sp) +{ + BOOST_TEST(equal_storage(a, sp)); +} + +inline +void +check_storage( + value const& v, + storage_ptr const& sp) +{ + BOOST_TEST(equal_storage(v, sp)); +} + +//---------------------------------------------------------- + +namespace detail { + +inline +void +to_string_test( + string& dest, + json::value const& jv) +{ + switch(jv.kind()) + { + case kind::object: + { + dest.push_back('{'); + auto const& obj( + jv.get_object()); + auto it = obj.begin(); + if(it != obj.end()) + { + goto obj_first; + while(it != obj.end()) + { + dest.push_back(','); + obj_first: + dest.push_back('\"'); + dest.append(it->key()); + dest.push_back('\"'); + dest.push_back(':'); + to_string_test( + dest, it->value()); + ++it; + } + } + dest.push_back('}'); + break; + } + + case kind::array: + { + dest.push_back('['); + auto const& arr( + jv.get_array()); + auto it = arr.begin(); + if(it != arr.end()) + { + goto arr_first; + while(it != arr.end()) + { + dest.push_back(','); + arr_first: + to_string_test( + dest, *it); + ++it; + } + } + dest.push_back(']'); + break; + } + + case kind::string: + #if 1 + // safe, but doesn't handle escsapes + dest.push_back('\"'); + dest.append(jv.get_string()); + dest.push_back('\"'); + #else + dest.append(serialize(jv)); + #endif + break; + + case kind::int64: + { + char buf[detail::max_number_chars]; + auto const n = + detail::format_int64( + buf, jv.as_int64()); + dest.append(string_view(buf).substr(0, n)); + break; + } + + case kind::uint64: + { + char buf[detail::max_number_chars]; + auto const n = + detail::format_uint64( + buf, jv.as_uint64()); + dest.append(string_view(buf).substr(0, n)); + break; + } + + case kind::double_: + { + char buf[detail::max_number_chars]; + auto const n = + detail::format_double( + buf, jv.as_double()); + dest.append(string_view(buf).substr(0, n)); + break; + } + + case kind::bool_: + if(jv.as_bool()) + dest.append("true"); + else + dest.append("false"); + break; + + case kind::null: + dest.append("null"); + break; + + default: + // should never get here + dest.append("?"); + break; + } +} + +} // detail + +inline +string +to_string_test( + json::value const& jv) +{ + string s; + s.reserve(1024); + detail::to_string_test(s, jv); + return s; +} + +//---------------------------------------------------------- + +inline +bool +equal( + value const& lhs, + value const& rhs) +{ + if(lhs.kind() != rhs.kind()) + return false; + switch(lhs.kind()) + { + case kind::object: + { + auto const& obj1 = + lhs.get_object(); + auto const& obj2 = + rhs.get_object(); + auto n = obj1.size(); + if(obj2.size() != n) + return false; + auto it1 = obj1.begin(); + auto it2 = obj2.begin(); + while(n--) + { + if( it1->key() != + it2->key()) + return false; + if(! equal( + it1->value(), + it2->value())) + return false; + ++it1; + ++it2; + } + return true; + } + + case kind::array: + { + auto const& arr1 = + lhs.get_array(); + auto const& arr2 = + rhs.get_array(); + auto n = arr1.size(); + if(arr2.size() != n) + return false; + auto it1 = arr1.begin(); + auto it2 = arr2.begin(); + while(n--) + if(! equal(*it1++, *it2++)) + return false; + return true; + } + + case kind::string: + return + lhs.get_string() == + rhs.get_string(); + + case kind::double_: + return + lhs.get_double() == + rhs.get_double(); + + case kind::int64: + return + lhs.get_int64() == + rhs.get_int64(); + + case kind::uint64: + return + lhs.get_uint64() == + rhs.get_uint64(); + + case kind::bool_: + return + lhs.get_bool() == + rhs.get_bool(); + + case kind::null: + return true; + } + + return false; +} + +template<typename T> +inline +bool +check_hash_equal( + T const& lhs, + T const& rhs) +{ + if(lhs == rhs){ + return (std::hash<T>{}(lhs) == std::hash<T>{}(rhs)); + } + return false; // ensure lhs == rhs intention +} + +template<typename T, typename U> +inline +bool +check_hash_equal( + T const& lhs, + U const& rhs) +{ + if(lhs == rhs){ + return (std::hash<value>{}(lhs) == std::hash<value>{}(rhs)); + } + return false; // ensure lhs == rhs intention +} + +template<typename T> +inline +bool +expect_hash_not_equal( + T const& lhs, + T const& rhs) +{ + if(std::hash<T>{}(lhs) != std::hash<T>{}(rhs)){ + return lhs != rhs; + } + return true; // pass if hash values collide +} + +template<typename T, typename U> +inline +bool +expect_hash_not_equal( + T const& lhs, + U const& rhs) +{ + if(std::hash<value>{}(lhs) != std::hash<value>{}(rhs)){ + return lhs != rhs; + } + return true; // pass if hash values collide +} +//---------------------------------------------------------- + +namespace detail { + +inline +void +check_array_impl(int, value const&) +{ +} + +template<class Arg> +void +check_array_impl( + int i, value const& jv, + Arg const& arg) +{ + BOOST_TEST(equal(jv.at(i), arg)); +} + +template< + class Arg0, + class Arg1, + class... Argn> +void +check_array_impl( + int i, value const& jv, + Arg0 const& arg0, + Arg1 const& arg1, + Argn const&... argn) +{ + BOOST_TEST(equal(jv.at(i), arg0)); + BOOST_TEST(equal(jv.at(i + 1), arg1)); + check_array_impl(i + 2, jv, argn...); +} + +} // detail + +template<class... Argn> +static +void +check_array( + value const& jv, + Argn const&... argn) +{ + if(! BOOST_TEST(jv.is_array())) + return; + if(! BOOST_TEST(sizeof...(argn) == + jv.get_array().size())) + return; + detail::check_array_impl(0, jv, argn...); +} + +//---------------------------------------------------------- + +BOOST_JSON_NS_END + +#endif |