2
0
mirror of https://github.com/boostorg/json.git synced 2026-02-12 12:12:15 +00:00
Files
json/test/test.hpp
Vinnie Falco 733d5d9a43 doc work
2019-10-23 18:53:20 -07:00

737 lines
12 KiB
C++

//
// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/vinniefalco/json
//
#ifndef BOOST_JSON_TEST_STORAGE_HPP
#define BOOST_JSON_TEST_STORAGE_HPP
#include <boost/json/basic_parser.hpp>
#include <boost/json/value.hpp>
#include <boost/json/storage.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <cstddef>
#include <iterator>
#include <memory>
#include <type_traits>
namespace boost {
namespace json {
//----------------------------------------------------------
// unique_storage
struct unique_storage : storage
{
void*
do_allocate(
std::size_t n,
std::size_t) override
{
return std::allocator<
char>{}.allocate(n);
}
void
do_deallocate(
void* p,
std::size_t n,
std::size_t) noexcept override
{
auto cp =
reinterpret_cast<char*>(p);
return std::allocator<
char>{}.deallocate(cp, n);
}
};
//----------------------------------------------------------
struct test_failure : std::exception
{
virtual
char const*
what() const noexcept override
{
return "test failure";
}
};
struct fail_storage : storage
{
std::size_t fail_max = 1;
std::size_t fail = 0;
~fail_storage()
{
}
void*
do_allocate(
std::size_t n,
std::size_t) override
{
if(++fail == fail_max)
{
++fail_max;
fail = 0;
throw test_failure{};
}
return std::allocator<
char>{}.allocate(n);
}
void
do_deallocate(
void* p,
std::size_t n,
std::size_t) noexcept override
{
auto cp =
reinterpret_cast<char*>(p);
return std::allocator<
char>{}.deallocate(cp, n);
}
};
//----------------------------------------------------------
namespace detail {
#if 1
template<class F>
void
fail_loop(F&& f)
{
auto sp = make_storage<fail_storage>();
while(sp->fail < 200)
{
try
{
f(sp);
}
catch(test_failure const&)
{
continue;
}
break;
}
BEAST_EXPECT(sp->fail < 200);
}
#else
template<class F>
typename std::enable_if<
std::is_same<void,
decltype(std::declval<F const&>()(
std::declval<storage_ptr>()))>::value
>::type
fail_loop(F&& f)
{
auto sp = make_storage<fail_storage>();
while(sp->fail < 200)
{
try
{
f(sp);
}
catch(test_failure const&)
{
continue;
}
break;
}
BEAST_EXPECT(sp->fail < 200);
}
template<class F>
typename std::enable_if<
std::is_same<void, decltype(
std::declval<F const&>()())>::value
>::type
fail_loop(F&& f)
{
auto saved = default_storage();
auto sp =
make_storage<fail_storage>();
default_storage(sp);
while(sp->fail < 200)
{
try
{
f();
}
catch(test_failure const&)
{
continue;
}
break;
}
BEAST_EXPECT(sp->fail < 200);
default_storage(saved);
}
#endif
} // detail
template<class F>
void
fail_loop(F&& f)
{
detail::fail_loop(
std::forward<F>(f));
}
//----------------------------------------------------------
class fail_parser
: public basic_parser
{
std::size_t n_ = std::size_t(-1);
char buf[256];
void
maybe_fail(error_code& ec)
{
if(n_ && --n_ > 0)
return;
ec = error::test_failure;
}
void
on_stack_info(
stack& s) noexcept override
{
s.base = buf;
s.capacity = sizeof(buf);
}
void
on_stack_grow(
stack&,
unsigned,
error_code& ec) override
{
ec = error::too_deep;
}
void
on_document_begin(
error_code& ec) override
{
maybe_fail(ec);
}
void
on_object_begin(
error_code& ec) override
{
maybe_fail(ec);
}
void
on_object_end(
error_code& ec) override
{
maybe_fail(ec);
}
void
on_array_begin(
error_code& ec) override
{
maybe_fail(ec);
}
void
on_array_end(
error_code& ec) override
{
maybe_fail(ec);
}
void
on_key_data(
string_view,
error_code& ec) override
{
maybe_fail(ec);
}
void
on_key_end(
string_view,
error_code& ec) override
{
maybe_fail(ec);
}
void
on_string_data(
string_view,
error_code& ec) override
{
maybe_fail(ec);
}
void
on_string_end(
string_view,
error_code& ec) override
{
maybe_fail(ec);
}
void
on_number(
ieee_decimal,
error_code& ec) override
{
maybe_fail(ec);
}
void
on_bool(
bool,
error_code& ec) override
{
maybe_fail(ec);
}
void
on_null(error_code& ec) override
{
maybe_fail(ec);
}
public:
fail_parser() = default;
explicit
fail_parser(
std::size_t n)
: n_(n)
{
}
};
//----------------------------------------------------------
struct test_exception
: std::exception
{
char const*
what() const noexcept
{
return "test exception";
}
};
// Exercises every exception path
class throw_parser
: public basic_parser
{
std::size_t n_ = std::size_t(-1);
char buf[256];
void
maybe_throw()
{
if(n_ && --n_ > 0)
return;
throw test_exception{};
}
void
on_stack_info(
stack& s) noexcept override
{
s.base = buf;
s.capacity = sizeof(buf);
}
void
on_stack_grow(
stack&,
unsigned,
error_code& ec) override
{
ec = error::too_deep;
}
void
on_document_begin(
error_code&) override
{
maybe_throw();
}
void
on_object_begin(
error_code&) override
{
maybe_throw();
}
void
on_object_end(
error_code&) override
{
maybe_throw();
}
void
on_array_begin(
error_code&) override
{
maybe_throw();
}
void
on_array_end(
error_code&) override
{
maybe_throw();
}
void
on_key_data(
string_view,
error_code&) override
{
maybe_throw();
}
void
on_key_end(
string_view,
error_code&) override
{
maybe_throw();
}
void
on_string_data(
string_view,
error_code&) override
{
maybe_throw();
}
void
on_string_end(
string_view,
error_code&) override
{
maybe_throw();
}
void
on_number(
ieee_decimal,
error_code&) override
{
maybe_throw();
}
void
on_bool(
bool,
error_code&) override
{
maybe_throw();
}
void
on_null(error_code&) override
{
maybe_throw();
}
public:
throw_parser() = default;
explicit
throw_parser(
std::size_t n)
: n_(n)
{
}
};
//----------------------------------------------------------
inline
ieee_decimal
parse_ieee_decimal(string_view s)
{
struct number_parser : basic_parser
{
std::size_t n_ = std::size_t(-1);
char buf[256];
void
on_stack_info(
stack& s) noexcept override
{
s.base = buf;
s.capacity = sizeof(buf);
}
void
on_stack_grow(
stack&,
unsigned,
error_code& ec) override
{
ec = error::too_deep;
}
void
on_document_begin(
error_code&) override
{
}
void
on_object_begin(
error_code&) override
{
}
void
on_object_end(
error_code&) override
{
}
void
on_array_begin(
error_code&) override
{
}
void
on_array_end(
error_code&) override
{
}
void
on_key_data(
string_view,
error_code&) override
{
}
void
on_key_end(
string_view,
error_code&) override
{
}
void
on_string_data(
string_view,
error_code&) override
{
}
void
on_string_end(
string_view,
error_code&) override
{
}
void
on_number(
ieee_decimal dec_,
error_code&) override
{
dec = dec_;
}
void
on_bool(
bool,
error_code&) override
{
}
void
on_null(error_code&) override
{
}
ieee_decimal dec;
number_parser() = default;
};
error_code ec;
number_parser p;
p.write(s.data(), s.size(), ec);
if(ec)
throw system_error(ec);
return p.dec;
}
//----------------------------------------------------------
// 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(
array const& a,
storage_ptr const& sp)
{
if(*a.get_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().get_storage() != *sp)
return false;
for(auto const& e : v.as_object())
if(! equal_storage(e.second, sp))
return false;
return true;
case json::kind::array:
if(*v.as_array().get_storage() != *sp)
return false;
return equal_storage(v.as_array(), sp);
case json::kind::string:
return *v.as_string().get_storage() == *sp;
case json::kind::number:
case json::kind::boolean:
case json::kind::null:
break;
}
return *v.get_storage() == *sp;
}
inline
void
check_storage(
array const& a,
storage_ptr const& sp)
{
BEAST_EXPECT(equal_storage(a, sp));
}
inline
void
check_storage(
value const& v,
storage_ptr const& sp)
{
BEAST_EXPECT(equal_storage(v, sp));
}
//----------------------------------------------------------
} // json
} // boost
#endif