//------------------------------------------------------------------------------
/*
    This file is part of rippled: https://github.com/ripple/rippled
    Copyright (c) 2012, 2013 Ripple Labs Inc.

    Permission to use, copy, modify, and/or distribute this software for any
    purpose  with  or without fee is hereby granted, provided that the above
    copyright notice and this permission notice appear in all copies.

    THE  SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    WITH  REGARD  TO  THIS  SOFTWARE  INCLUDING  ALL  IMPLIED  WARRANTIES  OF
    MERCHANTABILITY  AND  FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    ANY  SPECIAL ,  DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    WHATSOEVER  RESULTING  FROM  LOSS  OF USE, DATA OR PROFITS, WHETHER IN AN
    ACTION  OF  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================

#include <ripple/beast/core/LexicalCast.h>
#include <ripple/beast/type_name.h>
#include <ripple/beast/unit_test.h>
#include <ripple/json/json_reader.h>
#include <ripple/json/json_value.h>
#include <ripple/json/json_writer.h>

#include <algorithm>
#include <regex>

namespace ripple {

struct json_value_test : beast::unit_test::suite
{
    void
    test_StaticString()
    {
        static constexpr char sample[]{"Contents of a Json::StaticString"};

        static constexpr Json::StaticString test1(sample);
        char const* addrTest1{test1};

        BEAST_EXPECT(addrTest1 == &sample[0]);
        BEAST_EXPECT(test1.c_str() == &sample[0]);

        static constexpr Json::StaticString test2{
            "Contents of a Json::StaticString"};
        static constexpr Json::StaticString test3{"Another StaticString"};

        BEAST_EXPECT(test1 == test2);
        BEAST_EXPECT(test1 != test3);

        std::string str{sample};
        BEAST_EXPECT(str == test2);
        BEAST_EXPECT(str != test3);
        BEAST_EXPECT(test2 == str);
        BEAST_EXPECT(test3 != str);
    }

    void
    test_types()
    {
        // Exercise ValueType constructor
        static constexpr Json::StaticString staticStr{"staticStr"};

        auto testCopy = [this](Json::ValueType typ) {
            Json::Value val{typ};
            Json::Value cpy{val};
            BEAST_EXPECT(val.type() == typ);
            BEAST_EXPECT(cpy.type() == typ);
            return val;
        };
        {
            Json::Value const nullV{testCopy(Json::nullValue)};
            BEAST_EXPECT(nullV.isNull());
            BEAST_EXPECT(!nullV.isBool());
            BEAST_EXPECT(!nullV.isInt());
            BEAST_EXPECT(!nullV.isUInt());
            BEAST_EXPECT(!nullV.isIntegral());
            BEAST_EXPECT(!nullV.isDouble());
            BEAST_EXPECT(!nullV.isNumeric());
            BEAST_EXPECT(!nullV.isString());
            BEAST_EXPECT(!nullV.isArray());
            BEAST_EXPECT(nullV.isArrayOrNull());
            BEAST_EXPECT(!nullV.isObject());
            BEAST_EXPECT(nullV.isObjectOrNull());
        }
        {
            Json::Value const intV{testCopy(Json::intValue)};
            BEAST_EXPECT(!intV.isNull());
            BEAST_EXPECT(!intV.isBool());
            BEAST_EXPECT(intV.isInt());
            BEAST_EXPECT(!intV.isUInt());
            BEAST_EXPECT(intV.isIntegral());
            BEAST_EXPECT(!intV.isDouble());
            BEAST_EXPECT(intV.isNumeric());
            BEAST_EXPECT(!intV.isString());
            BEAST_EXPECT(!intV.isArray());
            BEAST_EXPECT(!intV.isArrayOrNull());
            BEAST_EXPECT(!intV.isObject());
            BEAST_EXPECT(!intV.isObjectOrNull());
        }
        {
            Json::Value const uintV{testCopy(Json::uintValue)};
            BEAST_EXPECT(!uintV.isNull());
            BEAST_EXPECT(!uintV.isBool());
            BEAST_EXPECT(!uintV.isInt());
            BEAST_EXPECT(uintV.isUInt());
            BEAST_EXPECT(uintV.isIntegral());
            BEAST_EXPECT(!uintV.isDouble());
            BEAST_EXPECT(uintV.isNumeric());
            BEAST_EXPECT(!uintV.isString());
            BEAST_EXPECT(!uintV.isArray());
            BEAST_EXPECT(!uintV.isArrayOrNull());
            BEAST_EXPECT(!uintV.isObject());
            BEAST_EXPECT(!uintV.isObjectOrNull());
        }
        {
            Json::Value const realV{testCopy(Json::realValue)};
            BEAST_EXPECT(!realV.isNull());
            BEAST_EXPECT(!realV.isBool());
            BEAST_EXPECT(!realV.isInt());
            BEAST_EXPECT(!realV.isUInt());
            BEAST_EXPECT(!realV.isIntegral());
            BEAST_EXPECT(realV.isDouble());
            BEAST_EXPECT(realV.isNumeric());
            BEAST_EXPECT(!realV.isString());
            BEAST_EXPECT(!realV.isArray());
            BEAST_EXPECT(!realV.isArrayOrNull());
            BEAST_EXPECT(!realV.isObject());
            BEAST_EXPECT(!realV.isObjectOrNull());
        }
        {
            Json::Value const stringV{testCopy(Json::stringValue)};
            BEAST_EXPECT(!stringV.isNull());
            BEAST_EXPECT(!stringV.isBool());
            BEAST_EXPECT(!stringV.isInt());
            BEAST_EXPECT(!stringV.isUInt());
            BEAST_EXPECT(!stringV.isIntegral());
            BEAST_EXPECT(!stringV.isDouble());
            BEAST_EXPECT(!stringV.isNumeric());
            BEAST_EXPECT(stringV.isString());
            BEAST_EXPECT(!stringV.isArray());
            BEAST_EXPECT(!stringV.isArrayOrNull());
            BEAST_EXPECT(!stringV.isObject());
            BEAST_EXPECT(!stringV.isObjectOrNull());
        }
        {
            Json::Value const staticStrV{staticStr};
            {
                Json::Value cpy{staticStrV};
                BEAST_EXPECT(staticStrV.type() == Json::stringValue);
                BEAST_EXPECT(cpy.type() == Json::stringValue);
            }
            BEAST_EXPECT(!staticStrV.isNull());
            BEAST_EXPECT(!staticStrV.isBool());
            BEAST_EXPECT(!staticStrV.isInt());
            BEAST_EXPECT(!staticStrV.isUInt());
            BEAST_EXPECT(!staticStrV.isIntegral());
            BEAST_EXPECT(!staticStrV.isDouble());
            BEAST_EXPECT(!staticStrV.isNumeric());
            BEAST_EXPECT(staticStrV.isString());
            BEAST_EXPECT(!staticStrV.isArray());
            BEAST_EXPECT(!staticStrV.isArrayOrNull());
            BEAST_EXPECT(!staticStrV.isObject());
            BEAST_EXPECT(!staticStrV.isObjectOrNull());
        }
        {
            Json::Value const boolV{testCopy(Json::booleanValue)};
            BEAST_EXPECT(!boolV.isNull());
            BEAST_EXPECT(boolV.isBool());
            BEAST_EXPECT(!boolV.isInt());
            BEAST_EXPECT(!boolV.isUInt());
            BEAST_EXPECT(boolV.isIntegral());
            BEAST_EXPECT(!boolV.isDouble());
            BEAST_EXPECT(boolV.isNumeric());
            BEAST_EXPECT(!boolV.isString());
            BEAST_EXPECT(!boolV.isArray());
            BEAST_EXPECT(!boolV.isArrayOrNull());
            BEAST_EXPECT(!boolV.isObject());
            BEAST_EXPECT(!boolV.isObjectOrNull());
        }
        {
            Json::Value const arrayV{testCopy(Json::arrayValue)};
            BEAST_EXPECT(!arrayV.isNull());
            BEAST_EXPECT(!arrayV.isBool());
            BEAST_EXPECT(!arrayV.isInt());
            BEAST_EXPECT(!arrayV.isUInt());
            BEAST_EXPECT(!arrayV.isIntegral());
            BEAST_EXPECT(!arrayV.isDouble());
            BEAST_EXPECT(!arrayV.isNumeric());
            BEAST_EXPECT(!arrayV.isString());
            BEAST_EXPECT(arrayV.isArray());
            BEAST_EXPECT(arrayV.isArrayOrNull());
            BEAST_EXPECT(!arrayV.isObject());
            BEAST_EXPECT(!arrayV.isObjectOrNull());
        }
        {
            Json::Value const objectV{testCopy(Json::objectValue)};
            BEAST_EXPECT(!objectV.isNull());
            BEAST_EXPECT(!objectV.isBool());
            BEAST_EXPECT(!objectV.isInt());
            BEAST_EXPECT(!objectV.isUInt());
            BEAST_EXPECT(!objectV.isIntegral());
            BEAST_EXPECT(!objectV.isDouble());
            BEAST_EXPECT(!objectV.isNumeric());
            BEAST_EXPECT(!objectV.isString());
            BEAST_EXPECT(!objectV.isArray());
            BEAST_EXPECT(!objectV.isArrayOrNull());
            BEAST_EXPECT(objectV.isObject());
            BEAST_EXPECT(objectV.isObjectOrNull());
        }
    }

    void
    test_compare()
    {
        auto doCompare = [this](
                             Json::Value const& lhs,
                             Json::Value const& rhs,
                             bool lhsEqRhs,
                             bool lhsLtRhs,
                             int line) {
            auto fmt = [this](bool cond, char const* text, int line) {
                if (cond)
                    this->pass();
                else
                    this->fail(text, __FILE__, line);
            };
            fmt((lhs == rhs) == lhsEqRhs, "Value ==", line);
            fmt((lhs != rhs) != lhsEqRhs, "Value !=", line);
            fmt((lhs < rhs) == (!(lhsEqRhs | !lhsLtRhs)), "Value <", line);
            fmt((lhs <= rhs) == (lhsEqRhs | lhsLtRhs), "Value <=", line);
            fmt((lhs >= rhs) == (lhsEqRhs | !lhsLtRhs), "Value >=", line);
            fmt((lhs > rhs) == (!(lhsEqRhs | lhsLtRhs)), "Value >", line);
        };

        Json::Value const null0;
        Json::Value const intNeg1{-1};
        Json::Value const int0{Json::intValue};
        Json::Value const intPos1{1};
        Json::Value const uint0{Json::uintValue};
        Json::Value const uint1{1u};
        Json::Value const realNeg1{-1.0};
        Json::Value const real0{Json::realValue};
        Json::Value const realPos1{1.0};
        Json::Value const str0{Json::stringValue};
        Json::Value const str1{"1"};
        Json::Value const boolF{false};
        Json::Value const boolT{true};
        Json::Value const array0{Json::arrayValue};
        Json::Value const array1{[]() {
            Json::Value array1;
            array1[0u] = 1;
            return array1;
        }()};
        Json::Value const obj0{Json::objectValue};
        Json::Value const obj1{[]() {
            Json::Value obj1;
            obj1["one"] = 1;
            return obj1;
        }()};
        //                                 lhs == rhs lhs < rhs
        doCompare(null0, Json::Value{}, true, false, __LINE__);
        doCompare(null0, intNeg1, false, true, __LINE__);
        doCompare(null0, int0, false, true, __LINE__);
        doCompare(null0, intPos1, false, true, __LINE__);
        doCompare(null0, uint0, false, true, __LINE__);
        doCompare(null0, uint1, false, true, __LINE__);
        doCompare(null0, realNeg1, false, true, __LINE__);
        doCompare(null0, real0, false, true, __LINE__);
        doCompare(null0, realPos1, false, true, __LINE__);
        doCompare(null0, str0, false, true, __LINE__);
        doCompare(null0, str1, false, true, __LINE__);
        doCompare(null0, boolF, false, true, __LINE__);
        doCompare(null0, boolT, false, true, __LINE__);
        doCompare(null0, array0, false, true, __LINE__);
        doCompare(null0, array1, false, true, __LINE__);
        doCompare(null0, obj0, false, true, __LINE__);
        doCompare(null0, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(intNeg1, null0, false, false, __LINE__);
        doCompare(intNeg1, intNeg1, true, false, __LINE__);
        doCompare(intNeg1, int0, false, true, __LINE__);
        doCompare(intNeg1, intPos1, false, true, __LINE__);
        doCompare(intNeg1, uint0, false, true, __LINE__);
        doCompare(intNeg1, uint1, false, true, __LINE__);
        doCompare(intNeg1, realNeg1, false, true, __LINE__);
        doCompare(intNeg1, real0, false, true, __LINE__);
        doCompare(intNeg1, realPos1, false, true, __LINE__);
        doCompare(intNeg1, str0, false, true, __LINE__);
        doCompare(intNeg1, str1, false, true, __LINE__);
        doCompare(intNeg1, boolF, false, true, __LINE__);
        doCompare(intNeg1, boolT, false, true, __LINE__);
        doCompare(intNeg1, array0, false, true, __LINE__);
        doCompare(intNeg1, array1, false, true, __LINE__);
        doCompare(intNeg1, obj0, false, true, __LINE__);
        doCompare(intNeg1, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(int0, null0, false, false, __LINE__);
        doCompare(int0, intNeg1, false, false, __LINE__);
        doCompare(int0, int0, true, false, __LINE__);
        doCompare(int0, intPos1, false, true, __LINE__);
        doCompare(int0, uint0, true, false, __LINE__);
        doCompare(int0, uint1, false, true, __LINE__);
        doCompare(int0, realNeg1, false, true, __LINE__);
        doCompare(int0, real0, false, true, __LINE__);
        doCompare(int0, realPos1, false, true, __LINE__);
        doCompare(int0, str0, false, true, __LINE__);
        doCompare(int0, str1, false, true, __LINE__);
        doCompare(int0, boolF, false, true, __LINE__);
        doCompare(int0, boolT, false, true, __LINE__);
        doCompare(int0, array0, false, true, __LINE__);
        doCompare(int0, array1, false, true, __LINE__);
        doCompare(int0, obj0, false, true, __LINE__);
        doCompare(int0, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(intPos1, null0, false, false, __LINE__);
        doCompare(intPos1, intNeg1, false, false, __LINE__);
        doCompare(intPos1, int0, false, false, __LINE__);
        doCompare(intPos1, intPos1, true, false, __LINE__);
        doCompare(intPos1, uint0, false, false, __LINE__);
        doCompare(intPos1, uint1, true, false, __LINE__);
        doCompare(intPos1, realNeg1, false, true, __LINE__);
        doCompare(intPos1, real0, false, true, __LINE__);
        doCompare(intPos1, realPos1, false, true, __LINE__);
        doCompare(intPos1, str0, false, true, __LINE__);
        doCompare(intPos1, str1, false, true, __LINE__);
        doCompare(intPos1, boolF, false, true, __LINE__);
        doCompare(intPos1, boolT, false, true, __LINE__);
        doCompare(intPos1, array0, false, true, __LINE__);
        doCompare(intPos1, array1, false, true, __LINE__);
        doCompare(intPos1, obj0, false, true, __LINE__);
        doCompare(intPos1, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(uint0, null0, false, false, __LINE__);
        doCompare(uint0, intNeg1, false, false, __LINE__);
        doCompare(uint0, int0, true, false, __LINE__);
        doCompare(uint0, intPos1, false, true, __LINE__);
        doCompare(uint0, uint0, true, false, __LINE__);
        doCompare(uint0, uint1, false, true, __LINE__);
        doCompare(uint0, realNeg1, false, true, __LINE__);
        doCompare(uint0, real0, false, true, __LINE__);
        doCompare(uint0, realPos1, false, true, __LINE__);
        doCompare(uint0, str0, false, true, __LINE__);
        doCompare(uint0, str1, false, true, __LINE__);
        doCompare(uint0, boolF, false, true, __LINE__);
        doCompare(uint0, boolT, false, true, __LINE__);
        doCompare(uint0, array0, false, true, __LINE__);
        doCompare(uint0, array1, false, true, __LINE__);
        doCompare(uint0, obj0, false, true, __LINE__);
        doCompare(uint0, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(uint1, null0, false, false, __LINE__);
        doCompare(uint1, intNeg1, false, false, __LINE__);
        doCompare(uint1, int0, false, false, __LINE__);
        doCompare(uint1, intPos1, true, false, __LINE__);
        doCompare(uint1, uint0, false, false, __LINE__);
        doCompare(uint1, uint1, true, false, __LINE__);
        doCompare(uint1, realNeg1, false, true, __LINE__);
        doCompare(uint1, real0, false, true, __LINE__);
        doCompare(uint1, realPos1, false, true, __LINE__);
        doCompare(uint1, str0, false, true, __LINE__);
        doCompare(uint1, str1, false, true, __LINE__);
        doCompare(uint1, boolF, false, true, __LINE__);
        doCompare(uint1, boolT, false, true, __LINE__);
        doCompare(uint1, array0, false, true, __LINE__);
        doCompare(uint1, array1, false, true, __LINE__);
        doCompare(uint1, obj0, false, true, __LINE__);
        doCompare(uint1, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(realNeg1, null0, false, false, __LINE__);
        doCompare(realNeg1, intNeg1, false, false, __LINE__);
        doCompare(realNeg1, int0, false, false, __LINE__);
        doCompare(realNeg1, intPos1, false, false, __LINE__);
        doCompare(realNeg1, uint0, false, false, __LINE__);
        doCompare(realNeg1, uint1, false, false, __LINE__);
        doCompare(realNeg1, realNeg1, true, false, __LINE__);
        doCompare(realNeg1, real0, false, true, __LINE__);
        doCompare(realNeg1, realPos1, false, true, __LINE__);
        doCompare(realNeg1, str0, false, true, __LINE__);
        doCompare(realNeg1, str1, false, true, __LINE__);
        doCompare(realNeg1, boolF, false, true, __LINE__);
        doCompare(realNeg1, boolT, false, true, __LINE__);
        doCompare(realNeg1, array0, false, true, __LINE__);
        doCompare(realNeg1, array1, false, true, __LINE__);
        doCompare(realNeg1, obj0, false, true, __LINE__);
        doCompare(realNeg1, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(real0, null0, false, false, __LINE__);
        doCompare(real0, intNeg1, false, false, __LINE__);
        doCompare(real0, int0, false, false, __LINE__);
        doCompare(real0, intPos1, false, false, __LINE__);
        doCompare(real0, uint0, false, false, __LINE__);
        doCompare(real0, uint1, false, false, __LINE__);
        doCompare(real0, realNeg1, false, false, __LINE__);
        doCompare(real0, real0, true, false, __LINE__);
        doCompare(real0, realPos1, false, true, __LINE__);
        doCompare(real0, str0, false, true, __LINE__);
        doCompare(real0, str1, false, true, __LINE__);
        doCompare(real0, boolF, false, true, __LINE__);
        doCompare(real0, boolT, false, true, __LINE__);
        doCompare(real0, array0, false, true, __LINE__);
        doCompare(real0, array1, false, true, __LINE__);
        doCompare(real0, obj0, false, true, __LINE__);
        doCompare(real0, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(realPos1, null0, false, false, __LINE__);
        doCompare(realPos1, intNeg1, false, false, __LINE__);
        doCompare(realPos1, int0, false, false, __LINE__);
        doCompare(realPos1, intPos1, false, false, __LINE__);
        doCompare(realPos1, uint0, false, false, __LINE__);
        doCompare(realPos1, uint1, false, false, __LINE__);
        doCompare(realPos1, realNeg1, false, false, __LINE__);
        doCompare(realPos1, real0, false, false, __LINE__);
        doCompare(realPos1, realPos1, true, false, __LINE__);
        doCompare(realPos1, str0, false, true, __LINE__);
        doCompare(realPos1, str1, false, true, __LINE__);
        doCompare(realPos1, boolF, false, true, __LINE__);
        doCompare(realPos1, boolT, false, true, __LINE__);
        doCompare(realPos1, array0, false, true, __LINE__);
        doCompare(realPos1, array1, false, true, __LINE__);
        doCompare(realPos1, obj0, false, true, __LINE__);
        doCompare(realPos1, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(str0, null0, false, false, __LINE__);
        doCompare(str0, intNeg1, false, false, __LINE__);
        doCompare(str0, int0, false, false, __LINE__);
        doCompare(str0, intPos1, false, false, __LINE__);
        doCompare(str0, uint0, false, false, __LINE__);
        doCompare(str0, uint1, false, false, __LINE__);
        doCompare(str0, realNeg1, false, false, __LINE__);
        doCompare(str0, real0, false, false, __LINE__);
        doCompare(str0, realPos1, false, false, __LINE__);
        doCompare(str0, str0, true, false, __LINE__);
        doCompare(str0, str1, false, true, __LINE__);
        doCompare(str0, boolF, false, true, __LINE__);
        doCompare(str0, boolT, false, true, __LINE__);
        doCompare(str0, array0, false, true, __LINE__);
        doCompare(str0, array1, false, true, __LINE__);
        doCompare(str0, obj0, false, true, __LINE__);
        doCompare(str0, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(str1, null0, false, false, __LINE__);
        doCompare(str1, intNeg1, false, false, __LINE__);
        doCompare(str1, int0, false, false, __LINE__);
        doCompare(str1, intPos1, false, false, __LINE__);
        doCompare(str1, uint0, false, false, __LINE__);
        doCompare(str1, uint1, false, false, __LINE__);
        doCompare(str1, realNeg1, false, false, __LINE__);
        doCompare(str1, real0, false, false, __LINE__);
        doCompare(str1, realPos1, false, false, __LINE__);
        doCompare(str1, str0, false, false, __LINE__);
        doCompare(str1, str1, true, false, __LINE__);
        doCompare(str1, boolF, false, true, __LINE__);
        doCompare(str1, boolT, false, true, __LINE__);
        doCompare(str1, array0, false, true, __LINE__);
        doCompare(str1, array1, false, true, __LINE__);
        doCompare(str1, obj0, false, true, __LINE__);
        doCompare(str1, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(boolF, null0, false, false, __LINE__);
        doCompare(boolF, intNeg1, false, false, __LINE__);
        doCompare(boolF, int0, false, false, __LINE__);
        doCompare(boolF, intPos1, false, false, __LINE__);
        doCompare(boolF, uint0, false, false, __LINE__);
        doCompare(boolF, uint1, false, false, __LINE__);
        doCompare(boolF, realNeg1, false, false, __LINE__);
        doCompare(boolF, real0, false, false, __LINE__);
        doCompare(boolF, realPos1, false, false, __LINE__);
        doCompare(boolF, str0, false, false, __LINE__);
        doCompare(boolF, str1, false, false, __LINE__);
        doCompare(boolF, boolF, true, false, __LINE__);
        doCompare(boolF, boolT, false, true, __LINE__);
        doCompare(boolF, array0, false, true, __LINE__);
        doCompare(boolF, array1, false, true, __LINE__);
        doCompare(boolF, obj0, false, true, __LINE__);
        doCompare(boolF, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(boolT, null0, false, false, __LINE__);
        doCompare(boolT, intNeg1, false, false, __LINE__);
        doCompare(boolT, int0, false, false, __LINE__);
        doCompare(boolT, intPos1, false, false, __LINE__);
        doCompare(boolT, uint0, false, false, __LINE__);
        doCompare(boolT, uint1, false, false, __LINE__);
        doCompare(boolT, realNeg1, false, false, __LINE__);
        doCompare(boolT, real0, false, false, __LINE__);
        doCompare(boolT, realPos1, false, false, __LINE__);
        doCompare(boolT, str0, false, false, __LINE__);
        doCompare(boolT, str1, false, false, __LINE__);
        doCompare(boolT, boolF, false, false, __LINE__);
        doCompare(boolT, boolT, true, false, __LINE__);
        doCompare(boolT, array0, false, true, __LINE__);
        doCompare(boolT, array1, false, true, __LINE__);
        doCompare(boolT, obj0, false, true, __LINE__);
        doCompare(boolT, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(array0, null0, false, false, __LINE__);
        doCompare(array0, intNeg1, false, false, __LINE__);
        doCompare(array0, int0, false, false, __LINE__);
        doCompare(array0, intPos1, false, false, __LINE__);
        doCompare(array0, uint0, false, false, __LINE__);
        doCompare(array0, uint1, false, false, __LINE__);
        doCompare(array0, realNeg1, false, false, __LINE__);
        doCompare(array0, real0, false, false, __LINE__);
        doCompare(array0, realPos1, false, false, __LINE__);
        doCompare(array0, str0, false, false, __LINE__);
        doCompare(array0, str1, false, false, __LINE__);
        doCompare(array0, boolF, false, false, __LINE__);
        doCompare(array0, boolT, false, false, __LINE__);
        doCompare(array0, array0, true, false, __LINE__);
        doCompare(array0, array1, false, true, __LINE__);
        doCompare(array0, obj0, false, true, __LINE__);
        doCompare(array0, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(array1, null0, false, false, __LINE__);
        doCompare(array1, intNeg1, false, false, __LINE__);
        doCompare(array1, int0, false, false, __LINE__);
        doCompare(array1, intPos1, false, false, __LINE__);
        doCompare(array1, uint0, false, false, __LINE__);
        doCompare(array1, uint1, false, false, __LINE__);
        doCompare(array1, realNeg1, false, false, __LINE__);
        doCompare(array1, real0, false, false, __LINE__);
        doCompare(array1, realPos1, false, false, __LINE__);
        doCompare(array1, str0, false, false, __LINE__);
        doCompare(array1, str1, false, false, __LINE__);
        doCompare(array1, boolF, false, false, __LINE__);
        doCompare(array1, boolT, false, false, __LINE__);
        doCompare(array1, array0, false, false, __LINE__);
        doCompare(array1, array1, true, false, __LINE__);
        doCompare(array1, obj0, false, true, __LINE__);
        doCompare(array1, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(obj0, null0, false, false, __LINE__);
        doCompare(obj0, intNeg1, false, false, __LINE__);
        doCompare(obj0, int0, false, false, __LINE__);
        doCompare(obj0, intPos1, false, false, __LINE__);
        doCompare(obj0, uint0, false, false, __LINE__);
        doCompare(obj0, uint1, false, false, __LINE__);
        doCompare(obj0, realNeg1, false, false, __LINE__);
        doCompare(obj0, real0, false, false, __LINE__);
        doCompare(obj0, realPos1, false, false, __LINE__);
        doCompare(obj0, str0, false, false, __LINE__);
        doCompare(obj0, str1, false, false, __LINE__);
        doCompare(obj0, boolF, false, false, __LINE__);
        doCompare(obj0, boolT, false, false, __LINE__);
        doCompare(obj0, array0, false, false, __LINE__);
        doCompare(obj0, array1, false, false, __LINE__);
        doCompare(obj0, obj0, true, false, __LINE__);
        doCompare(obj0, obj1, false, true, __LINE__);
        //                                 lhs == rhs lhs < rhs
        doCompare(obj1, null0, false, false, __LINE__);
        doCompare(obj1, intNeg1, false, false, __LINE__);
        doCompare(obj1, int0, false, false, __LINE__);
        doCompare(obj1, intPos1, false, false, __LINE__);
        doCompare(obj1, uint0, false, false, __LINE__);
        doCompare(obj1, uint1, false, false, __LINE__);
        doCompare(obj1, realNeg1, false, false, __LINE__);
        doCompare(obj1, real0, false, false, __LINE__);
        doCompare(obj1, realPos1, false, false, __LINE__);
        doCompare(obj1, str0, false, false, __LINE__);
        doCompare(obj1, str1, false, false, __LINE__);
        doCompare(obj1, boolF, false, false, __LINE__);
        doCompare(obj1, boolT, false, false, __LINE__);
        doCompare(obj1, array0, false, false, __LINE__);
        doCompare(obj1, array1, false, false, __LINE__);
        doCompare(obj1, obj0, false, false, __LINE__);
        doCompare(obj1, obj1, true, false, __LINE__);
    }

    void
    test_bool()
    {
        BEAST_EXPECT(!Json::Value());

        BEAST_EXPECT(!Json::Value(""));

        BEAST_EXPECT(bool(Json::Value("empty")));
        BEAST_EXPECT(bool(Json::Value(false)));
        BEAST_EXPECT(bool(Json::Value(true)));
        BEAST_EXPECT(bool(Json::Value(0)));
        BEAST_EXPECT(bool(Json::Value(1)));

        Json::Value array(Json::arrayValue);
        BEAST_EXPECT(!array);
        array.append(0);
        BEAST_EXPECT(bool(array));

        Json::Value object(Json::objectValue);
        BEAST_EXPECT(!object);
        object[""] = false;
        BEAST_EXPECT(bool(object));
    }

    void
    test_bad_json()
    {
        char const* s(
            "{\"method\":\"ledger\",\"params\":[{\"ledger_index\":1e300}]}");

        Json::Value j;
        Json::Reader r;

        r.parse(s, j);
        pass();
    }

    void
    test_edge_cases()
    {
        std::string json;

        std::uint32_t max_uint = std::numeric_limits<std::uint32_t>::max();
        std::int32_t max_int = std::numeric_limits<std::int32_t>::max();
        std::int32_t min_int = std::numeric_limits<std::int32_t>::min();

        std::uint32_t a_uint = max_uint - 1978;
        std::int32_t a_large_int = max_int - 1978;
        std::int32_t a_small_int = min_int + 1978;

        json = "{\"max_uint\":" + std::to_string(max_uint);
        json += ",\"max_int\":" + std::to_string(max_int);
        json += ",\"min_int\":" + std::to_string(min_int);
        json += ",\"a_uint\":" + std::to_string(a_uint);
        json += ",\"a_large_int\":" + std::to_string(a_large_int);
        json += ",\"a_small_int\":" + std::to_string(a_small_int);
        json += "}";

        Json::Value j1;
        Json::Reader r1;

        BEAST_EXPECT(r1.parse(json, j1));
        BEAST_EXPECT(j1["max_uint"].asUInt() == max_uint);
        BEAST_EXPECT(j1["max_int"].asInt() == max_int);
        BEAST_EXPECT(j1["min_int"].asInt() == min_int);
        BEAST_EXPECT(j1["a_uint"].asUInt() == a_uint);
        BEAST_EXPECT(j1["a_uint"] > a_large_int);
        BEAST_EXPECT(j1["a_uint"] > a_small_int);
        BEAST_EXPECT(j1["a_large_int"].asInt() == a_large_int);
        BEAST_EXPECT(j1["a_large_int"].asUInt() == a_large_int);
        BEAST_EXPECT(j1["a_large_int"] < a_uint);
        BEAST_EXPECT(j1["a_small_int"].asInt() == a_small_int);
        BEAST_EXPECT(j1["a_small_int"] < a_uint);

        json = "{\"overflow\":";
        json += std::to_string(std::uint64_t(max_uint) + 1);
        json += "}";

        Json::Value j2;
        Json::Reader r2;

        BEAST_EXPECT(!r2.parse(json, j2));

        json = "{\"underflow\":";
        json += std::to_string(std::int64_t(min_int) - 1);
        json += "}";

        Json::Value j3;
        Json::Reader r3;

        BEAST_EXPECT(!r3.parse(json, j3));

        Json::Value intString{"4294967296"};
        try
        {
            [[maybe_unused]] std::uint32_t const uTooBig{intString.asUInt()};
            fail("4294967296", __FILE__, __LINE__);
        }
        catch (beast::BadLexicalCast const&)
        {
            pass();
        }

        intString = "4294967295";
        BEAST_EXPECT(intString.asUInt() == 4294967295u);

        intString = "0";
        BEAST_EXPECT(intString.asUInt() == 0);

        intString = "-1";
        try
        {
            [[maybe_unused]] std::uint32_t const uTooSmall{intString.asUInt()};
            fail("-1", __FILE__, __LINE__);
        }
        catch (beast::BadLexicalCast const&)
        {
            pass();
        }

        intString = "2147483648";
        try
        {
            [[maybe_unused]] std::int32_t tooPos{intString.asInt()};
            fail("2147483648", __FILE__, __LINE__);
        }
        catch (beast::BadLexicalCast const&)
        {
            pass();
        }

        intString = "2147483647";
        BEAST_EXPECT(intString.asInt() == 2147483647);

        intString = "-2147483648";
        BEAST_EXPECT(intString.asInt() == -2147483648LL);  // MSVC wants the LL

        intString = "-2147483649";
        try
        {
            [[maybe_unused]] std::int32_t tooNeg{intString.asInt()};
            fail("-2147483649", __FILE__, __LINE__);
        }
        catch (beast::BadLexicalCast const&)
        {
            pass();
        }
    }

    void
    test_copy()
    {
        Json::Value v1{2.5};
        BEAST_EXPECT(v1.isDouble());
        BEAST_EXPECT(v1.asDouble() == 2.5);

        Json::Value v2 = v1;
        BEAST_EXPECT(v1.isDouble());
        BEAST_EXPECT(v1.asDouble() == 2.5);
        BEAST_EXPECT(v2.isDouble());
        BEAST_EXPECT(v2.asDouble() == 2.5);
        BEAST_EXPECT(v1 == v2);

        v1 = v2;
        BEAST_EXPECT(v1.isDouble());
        BEAST_EXPECT(v1.asDouble() == 2.5);
        BEAST_EXPECT(v2.isDouble());
        BEAST_EXPECT(v2.asDouble() == 2.5);
        BEAST_EXPECT(v1 == v2);

        pass();
    }

    void
    test_move()
    {
        Json::Value v1{2.5};
        BEAST_EXPECT(v1.isDouble());
        BEAST_EXPECT(v1.asDouble() == 2.5);

        Json::Value v2 = std::move(v1);
        BEAST_EXPECT(!v1);
        BEAST_EXPECT(v2.isDouble());
        BEAST_EXPECT(v2.asDouble() == 2.5);
        BEAST_EXPECT(v1 != v2);

        v1 = std::move(v2);
        BEAST_EXPECT(v1.isDouble());
        BEAST_EXPECT(v1.asDouble() == 2.5);
        BEAST_EXPECT(!v2);
        BEAST_EXPECT(v1 != v2);

        pass();
    }

    void
    test_comparisons()
    {
        Json::Value a, b;
        auto testEquals = [&](std::string const& name) {
            BEAST_EXPECT(a == b);
            BEAST_EXPECT(a <= b);
            BEAST_EXPECT(a >= b);

            BEAST_EXPECT(!(a != b));
            BEAST_EXPECT(!(a < b));
            BEAST_EXPECT(!(a > b));

            BEAST_EXPECT(b == a);
            BEAST_EXPECT(b <= a);
            BEAST_EXPECT(b >= a);

            BEAST_EXPECT(!(b != a));
            BEAST_EXPECT(!(b < a));
            BEAST_EXPECT(!(b > a));
        };

        auto testGreaterThan = [&](std::string const& name) {
            BEAST_EXPECT(!(a == b));
            BEAST_EXPECT(!(a <= b));
            BEAST_EXPECT(a >= b);

            BEAST_EXPECT(a != b);
            BEAST_EXPECT(!(a < b));
            BEAST_EXPECT(a > b);

            BEAST_EXPECT(!(b == a));
            BEAST_EXPECT(b <= a);
            BEAST_EXPECT(!(b >= a));

            BEAST_EXPECT(b != a);
            BEAST_EXPECT(b < a);
            BEAST_EXPECT(!(b > a));
        };

        a["a"] = Json::UInt(0);
        b["a"] = Json::Int(0);
        testEquals("zero");

        b["a"] = Json::Int(-1);
        testGreaterThan("negative");

        Json::Int big = std::numeric_limits<int>::max();
        Json::UInt bigger = big;
        bigger++;

        a["a"] = bigger;
        b["a"] = big;
        testGreaterThan("big");
    }

    void
    test_compact()
    {
        Json::Value j;
        Json::Reader r;
        char const* s("{\"array\":[{\"12\":23},{},null,false,0.5]}");

        auto countLines = [](std::string const& str) {
            return 1 + std::count_if(str.begin(), str.end(), [](char c) {
                       return c == '\n';
                   });
        };

        BEAST_EXPECT(r.parse(s, j));
        {
            std::stringstream ss;
            ss << j;
            BEAST_EXPECT(countLines(ss.str()) > 1);
        }
        {
            std::stringstream ss;
            ss << Json::Compact(std::move(j));
            BEAST_EXPECT(countLines(ss.str()) == 1);
        }
    }

    void
    test_conversions()
    {
        // We have Json::Int, but not Json::Double or Json::Real.
        // We have Json::Int, Json::Value::Int, and Json::ValueType::intValue.
        // We have Json::ValueType::realValue but Json::Value::asDouble.
        // TODO: What's the thinking here?
        {
            // null
            Json::Value val;
            BEAST_EXPECT(val.isNull());
            //          BEAST_EXPECT(strcmp (val.asCString(), ?) == 0); //
            //          asserts
            BEAST_EXPECT(val.asString() == "");
            BEAST_EXPECT(val.asInt() == 0);
            BEAST_EXPECT(val.asUInt() == 0);
            BEAST_EXPECT(val.asDouble() == 0.0);
            BEAST_EXPECT(val.asBool() == false);

            BEAST_EXPECT(val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::objectValue));
        }
        {
            // int
            Json::Value val = -1234;
            BEAST_EXPECT(val.isInt());
            //          BEAST_EXPECT(strcmp (val.asCString(), ?) == 0); //
            //          asserts
            BEAST_EXPECT(val.asString() == "-1234");
            BEAST_EXPECT(val.asInt() == -1234);
            //          BEAST_EXPECT(val.asUInt() == ?);                //
            //          asserts or throws
            BEAST_EXPECT(val.asDouble() == -1234.0);
            BEAST_EXPECT(val.asBool() == true);

            BEAST_EXPECT(!val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::objectValue));
        }
        {
            // uint
            Json::Value val = 1234U;
            BEAST_EXPECT(val.isUInt());
            //          BEAST_EXPECT(strcmp (val.asCString(), ?) == 0); //
            //          asserts
            BEAST_EXPECT(val.asString() == "1234");
            BEAST_EXPECT(val.asInt() == 1234);
            BEAST_EXPECT(val.asUInt() == 1234u);
            BEAST_EXPECT(val.asDouble() == 1234.0);
            BEAST_EXPECT(val.asBool() == true);

            BEAST_EXPECT(!val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::objectValue));
        }
        {
            // real
            Json::Value val = 2.0;
            BEAST_EXPECT(val.isDouble());
            //          BEAST_EXPECT(strcmp (val.asCString(), ?) == 0); //
            //          asserts
            BEAST_EXPECT(
                std::regex_match(val.asString(), std::regex("^2\\.0*$")));
            BEAST_EXPECT(val.asInt() == 2);
            BEAST_EXPECT(val.asUInt() == 2u);
            BEAST_EXPECT(val.asDouble() == 2.0);
            BEAST_EXPECT(val.asBool() == true);

            BEAST_EXPECT(!val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::objectValue));
        }
        {
            // numeric string
            Json::Value val = "54321";
            BEAST_EXPECT(val.isString());
            BEAST_EXPECT(strcmp(val.asCString(), "54321") == 0);
            BEAST_EXPECT(val.asString() == "54321");
            BEAST_EXPECT(val.asInt() == 54321);
            BEAST_EXPECT(val.asUInt() == 54321u);
            //          BEAST_EXPECT(val.asDouble() == 54321.0);        //
            //          asserts or throws
            BEAST_EXPECT(val.asBool() == true);

            BEAST_EXPECT(!val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::objectValue));
        }
        {
            // non-numeric string
            Json::Value val(Json::stringValue);
            BEAST_EXPECT(val.isString());
            BEAST_EXPECT(val.asCString() == nullptr);
            BEAST_EXPECT(val.asString() == "");
            try
            {
                BEAST_EXPECT(val.asInt() == 0);
                fail("expected exception", __FILE__, __LINE__);
            }
            catch (std::exception const&)
            {
                pass();
            }
            try
            {
                BEAST_EXPECT(val.asUInt() == 0);
                fail("expected exception", __FILE__, __LINE__);
            }
            catch (std::exception const&)
            {
                pass();
            }
            //          BEAST_EXPECT(val.asDouble() == ?);              //
            //          asserts or throws
            BEAST_EXPECT(val.asBool() == false);

            BEAST_EXPECT(val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::objectValue));
        }
        {
            // bool false
            Json::Value val = false;
            BEAST_EXPECT(val.isBool());
            //          BEAST_EXPECT(strcmp (val.asCString(), ?) == 0); //
            //          asserts
            BEAST_EXPECT(val.asString() == "false");
            BEAST_EXPECT(val.asInt() == 0);
            BEAST_EXPECT(val.asUInt() == 0);
            BEAST_EXPECT(val.asDouble() == 0.0);
            BEAST_EXPECT(val.asBool() == false);

            BEAST_EXPECT(val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::objectValue));
        }
        {
            // bool true
            Json::Value val = true;
            BEAST_EXPECT(val.isBool());
            //          BEAST_EXPECT(strcmp (val.asCString(), ?) == 0); //
            //          asserts
            BEAST_EXPECT(val.asString() == "true");
            BEAST_EXPECT(val.asInt() == 1);
            BEAST_EXPECT(val.asUInt() == 1);
            BEAST_EXPECT(val.asDouble() == 1.0);
            BEAST_EXPECT(val.asBool() == true);

            BEAST_EXPECT(!val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::objectValue));
        }
        {
            // array type
            Json::Value val(Json::arrayValue);
            BEAST_EXPECT(val.isArray());
            //          BEAST_EXPECT(strcmp (val.asCString(), ?) == 0); //
            //          asserts BEAST_EXPECT(val.asString() == ?); // asserts or
            //          throws BEAST_EXPECT(val.asInt() == ?); // asserts or
            //          throws BEAST_EXPECT(val.asUInt() == ?); // asserts or
            //          throws BEAST_EXPECT(val.asDouble() == ?); // asserts or
            //          throws
            BEAST_EXPECT(val.asBool() == false);  // empty or not

            BEAST_EXPECT(val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::objectValue));
        }
        {
            // object type
            Json::Value val(Json::objectValue);
            BEAST_EXPECT(val.isObject());
            //          BEAST_EXPECT(strcmp (val.asCString(), ?) == 0); //
            //          asserts BEAST_EXPECT(strcmp (val.asCString(), ?) == 0);
            //          // asserts BEAST_EXPECT(val.asString() == ?); // asserts
            //          or throws BEAST_EXPECT(val.asInt() == ?); // asserts or
            //          throws BEAST_EXPECT(val.asUInt() == ?); // asserts or
            //          throws
            BEAST_EXPECT(val.asBool() == false);  // empty or not

            BEAST_EXPECT(val.isConvertibleTo(Json::nullValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::intValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::uintValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::realValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::stringValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::booleanValue));
            BEAST_EXPECT(!val.isConvertibleTo(Json::arrayValue));
            BEAST_EXPECT(val.isConvertibleTo(Json::objectValue));
        }
    }

    void
    test_access()
    {
        Json::Value val;
        BEAST_EXPECT(val.type() == Json::nullValue);
        BEAST_EXPECT(val.size() == 0);
        BEAST_EXPECT(!val.isValidIndex(0));
        BEAST_EXPECT(!val.isMember("key"));
        {
            Json::Value const constVal = val;
            BEAST_EXPECT(constVal[7u].type() == Json::nullValue);
            BEAST_EXPECT(!constVal.isMember("key"));
            BEAST_EXPECT(constVal["key"].type() == Json::nullValue);
            BEAST_EXPECT(constVal.getMemberNames().empty());
            BEAST_EXPECT(constVal.get(1u, "default0") == "default0");
            BEAST_EXPECT(constVal.get(std::string("not"), "oh") == "oh");
            BEAST_EXPECT(constVal.get("missing", "default2") == "default2");
        }

        val = -7;
        BEAST_EXPECT(val.type() == Json::intValue);
        BEAST_EXPECT(val.size() == 0);
        BEAST_EXPECT(!val.isValidIndex(0));
        BEAST_EXPECT(!val.isMember("key"));

        val = 42u;
        BEAST_EXPECT(val.type() == Json::uintValue);
        BEAST_EXPECT(val.size() == 0);
        BEAST_EXPECT(!val.isValidIndex(0));
        BEAST_EXPECT(!val.isMember("key"));

        val = 3.14159;
        BEAST_EXPECT(val.type() == Json::realValue);
        BEAST_EXPECT(val.size() == 0);
        BEAST_EXPECT(!val.isValidIndex(0));
        BEAST_EXPECT(!val.isMember("key"));

        val = true;
        BEAST_EXPECT(val.type() == Json::booleanValue);
        BEAST_EXPECT(val.size() == 0);
        BEAST_EXPECT(!val.isValidIndex(0));
        BEAST_EXPECT(!val.isMember("key"));

        val = "string";
        BEAST_EXPECT(val.type() == Json::stringValue);
        BEAST_EXPECT(val.size() == 0);
        BEAST_EXPECT(!val.isValidIndex(0));
        BEAST_EXPECT(!val.isMember("key"));

        val = Json::Value(Json::objectValue);
        BEAST_EXPECT(val.type() == Json::objectValue);
        BEAST_EXPECT(val.size() == 0);
        static Json::StaticString const staticThree("three");
        val[staticThree] = 3;
        val["two"] = 2;
        BEAST_EXPECT(val.size() == 2);
        BEAST_EXPECT(val.isValidIndex(1));
        BEAST_EXPECT(!val.isValidIndex(2));
        BEAST_EXPECT(val[staticThree] == 3);
        BEAST_EXPECT(val.isMember("two"));
        BEAST_EXPECT(val.isMember(staticThree));
        BEAST_EXPECT(!val.isMember("key"));
        {
            Json::Value const constVal = val;
            BEAST_EXPECT(constVal["two"] == 2);
            BEAST_EXPECT(constVal["four"].type() == Json::nullValue);
            BEAST_EXPECT(constVal[staticThree] == 3);
            BEAST_EXPECT(constVal.isMember("two"));
            BEAST_EXPECT(constVal.isMember(staticThree));
            BEAST_EXPECT(!constVal.isMember("key"));
            BEAST_EXPECT(val.get(std::string("two"), "backup") == 2);
            BEAST_EXPECT(val.get("missing", "default2") == "default2");
        }

        val = Json::Value(Json::arrayValue);
        BEAST_EXPECT(val.type() == Json::arrayValue);
        BEAST_EXPECT(val.size() == 0);
        val[0u] = "zero";
        val[1u] = "one";
        BEAST_EXPECT(val.size() == 2);
        BEAST_EXPECT(val.isValidIndex(1));
        BEAST_EXPECT(!val.isValidIndex(2));
        BEAST_EXPECT(val[20u].type() == Json::nullValue);
        BEAST_EXPECT(!val.isMember("key"));
        {
            Json::Value const constVal = val;
            BEAST_EXPECT(constVal[0u] == "zero");
            BEAST_EXPECT(constVal[2u].type() == Json::nullValue);
            BEAST_EXPECT(!constVal.isMember("key"));
            BEAST_EXPECT(val.get(1u, "default0") == "one");
            BEAST_EXPECT(val.get(3u, "default1") == "default1");
        }
    }

    void
    test_removeMember()
    {
        Json::Value val;
        BEAST_EXPECT(
            val.removeMember(std::string("member")).type() == Json::nullValue);

        val = Json::Value(Json::objectValue);
        static Json::StaticString const staticThree("three");
        val[staticThree] = 3;
        val["two"] = 2;
        BEAST_EXPECT(val.size() == 2);

        BEAST_EXPECT(
            val.removeMember(std::string("six")).type() == Json::nullValue);
        BEAST_EXPECT(val.size() == 2);

        BEAST_EXPECT(val.removeMember(staticThree) == 3);
        BEAST_EXPECT(val.size() == 1);

        BEAST_EXPECT(val.removeMember(staticThree).type() == Json::nullValue);
        BEAST_EXPECT(val.size() == 1);

        BEAST_EXPECT(val.removeMember(std::string("two")) == 2);
        BEAST_EXPECT(val.size() == 0);

        BEAST_EXPECT(
            val.removeMember(std::string("two")).type() == Json::nullValue);
        BEAST_EXPECT(val.size() == 0);
    }

    void
    test_iterator()
    {
        {
            // Iterating an array.
            Json::Value arr{Json::arrayValue};
            arr[0u] = "zero";
            arr[1u] = "one";
            arr[2u] = "two";
            arr[3u] = "three";

            Json::ValueIterator const b{arr.begin()};
            Json::ValueIterator const e{arr.end()};

            Json::ValueIterator i1 = b;
            Json::ValueIterator i2 = e;
            --i2;

            // key(), index(), and memberName() on an object iterator.
            BEAST_EXPECT(b != e);
            BEAST_EXPECT(!(b == e));
            BEAST_EXPECT(i1.key() == 0);
            BEAST_EXPECT(i2.key() == 3);
            BEAST_EXPECT(i1.index() == 0);
            BEAST_EXPECT(i2.index() == 3);
            BEAST_EXPECT(std::strcmp(i1.memberName(), "") == 0);
            BEAST_EXPECT(std::strcmp(i2.memberName(), "") == 0);

            // Pre and post increment and decrement.
            *i1++ = "0";
            BEAST_EXPECT(*i1 == "one");
            *i1 = "1";
            ++i1;

            *i2-- = "3";
            BEAST_EXPECT(*i2 == "two");
            BEAST_EXPECT(i1 == i2);
            *i2 = "2";
            BEAST_EXPECT(*i1 == "2");
        }
        {
            // Iterating a const object.
            Json::Value const obj{[]() {
                Json::Value obj{Json::objectValue};
                obj["0"] = 0;
                obj["1"] = 1;
                obj["2"] = 2;
                obj["3"] = 3;
                return obj;
            }()};

            Json::ValueConstIterator i1{obj.begin()};
            Json::ValueConstIterator i2{obj.end()};
            --i2;

            // key(), index(), and memberName() on an object iterator.
            BEAST_EXPECT(i1 != i2);
            BEAST_EXPECT(!(i1 == i2));
            BEAST_EXPECT(i1.key() == "0");
            BEAST_EXPECT(i2.key() == "3");
            BEAST_EXPECT(i1.index() == -1);
            BEAST_EXPECT(i2.index() == -1);
            BEAST_EXPECT(std::strcmp(i1.memberName(), "0") == 0);
            BEAST_EXPECT(std::strcmp(i2.memberName(), "3") == 0);

            // Pre and post increment and decrement.
            BEAST_EXPECT(*i1++ == 0);
            BEAST_EXPECT(*i1 == 1);
            ++i1;

            BEAST_EXPECT(*i2-- == 3);
            BEAST_EXPECT(*i2 == 2);
            BEAST_EXPECT(i1 == i2);
            BEAST_EXPECT(*i1 == 2);
        }
        {
            // Iterating a non-const null object.
            Json::Value nul{};
            BEAST_EXPECT(nul.begin() == nul.end());
        }
        {
            // Iterating a const Int.
            Json::Value const i{-3};
            BEAST_EXPECT(i.begin() == i.end());
        }
    }

    void
    test_nest_limits()
    {
        Json::Reader r;
        {
            auto nest = [](std::uint32_t depth) -> std::string {
                std::string s = "{";
                for (std::uint32_t i{1}; i <= depth; ++i)
                    s += "\"obj\":{";
                for (std::uint32_t i{1}; i <= depth; ++i)
                    s += "}";
                s += "}";
                return s;
            };

            {
                // Within object nest limit
                auto json{nest(std::min(10u, Json::Reader::nest_limit))};
                Json::Value j;
                BEAST_EXPECT(r.parse(json, j));
            }

            {
                // Exceed object nest limit
                auto json{nest(Json::Reader::nest_limit + 1)};
                Json::Value j;
                BEAST_EXPECT(!r.parse(json, j));
            }
        }

        auto nest = [](std::uint32_t depth) -> std::string {
            std::string s = "{";
            for (std::uint32_t i{1}; i <= depth; ++i)
                s += "\"array\":[{";
            for (std::uint32_t i{1}; i <= depth; ++i)
                s += "]}";
            s += "}";
            return s;
        };
        {
            // Exceed array nest limit
            auto json{nest(Json::Reader::nest_limit + 1)};
            Json::Value j;
            BEAST_EXPECT(!r.parse(json, j));
        }
    }

    void
    test_leak()
    {
        // When run with the address sanitizer, this test confirms there is no
        // memory leak with the scenarios below.
        {
            Json::Value a;
            a[0u] = 1;
            BEAST_EXPECT(a.type() == Json::arrayValue);
            BEAST_EXPECT(a[0u].type() == Json::intValue);
            a = std::move(a[0u]);
            BEAST_EXPECT(a.type() == Json::intValue);
        }
        {
            Json::Value b;
            Json::Value temp;
            temp["a"] = "Probably avoids the small string optimization";
            temp["b"] = "Also probably avoids the small string optimization";
            BEAST_EXPECT(temp.type() == Json::objectValue);
            b.append(temp);
            BEAST_EXPECT(temp.type() == Json::objectValue);
            BEAST_EXPECT(b.size() == 1);

            b.append(std::move(temp));
            BEAST_EXPECT(b.size() == 2);

            // Note that the type() == nullValue check is implementation
            // specific and not guaranteed to be valid in the future.
            BEAST_EXPECT(temp.type() == Json::nullValue);
        }
    }

    void
    run() override
    {
        test_StaticString();
        test_types();
        test_compare();
        test_bool();
        test_bad_json();
        test_edge_cases();
        test_copy();
        test_move();
        test_comparisons();
        test_compact();
        test_conversions();
        test_access();
        test_removeMember();
        test_iterator();
        test_nest_limits();
        test_leak();
    }
};

BEAST_DEFINE_TESTSUITE(json_value, json, ripple);

}  // namespace ripple
