diff --git a/.github/actions/dependencies/action.yml b/.github/actions/dependencies/action.yml index 50e2999018a..9a6bd444303 100644 --- a/.github/actions/dependencies/action.yml +++ b/.github/actions/dependencies/action.yml @@ -24,6 +24,11 @@ runs: # Do not quote the URL. An empty string will be accepted (with # a non-fatal warning), but a missing argument will not. conan remote add ripple ${{ env.CONAN_URL }} --insert 0 + if [[ "${RUNNER_OS}" = "macOS" ]] + then + conan profile update 'env.CXXFLAGS="-DBOOST_ASIO_DISABLE_CONCEPTS"' default + conan profile update 'conf.tools.build:cxxflags+=["-DBOOST_ASIO_DISABLE_CONCEPTS"]' default + fi - name: try to authenticate to Ripple Conan remote id: remote shell: bash @@ -47,6 +52,19 @@ runs: run: | mkdir ${build_dir} cd ${build_dir} + # This fixes some dependency build issues, especially on MacOS + if [[ "${RUNNER_OS}" = "macOS" && \ + "${{ steps.binaries.outputs.missing }}" =~ "boost" ]] + then + conan install \ + --output-folder . \ + --build boost \ + --options tests=True \ + --options xrpld=True \ + --settings build_type=${{ inputs.configuration }} \ + .. || \ + rm -rfv ~/.conan + fi conan install \ --output-folder . \ --build missing \ diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index c214f417e79..ab9b93d0685 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -19,6 +19,7 @@ jobs: test: strategy: + fail-fast: false matrix: platform: - macos @@ -26,6 +27,10 @@ jobs: - Ninja configuration: - Release + - Debug + cmake-args: + - + - "-Dunity=ON" runs-on: [self-hosted, macOS] env: # The `build` action requires these variables. @@ -56,6 +61,9 @@ jobs: else brew install cmake fi + - name: install nproc + run: | + brew install coreutils - name: check environment run: | env | sort @@ -63,6 +71,9 @@ jobs: python --version conan --version cmake --version + nproc --version + echo -n "nproc returns: " + nproc - name: configure Conan run : | conan profile new default --detect || true @@ -81,6 +92,13 @@ jobs: with: generator: ${{ matrix.generator }} configuration: ${{ matrix.configuration }} + cmake-args: ${{ matrix.cmake-args }} - name: test run: | - ${build_dir}/rippled --unittest + n=$(nproc) + if [[ $n -gt 2 ]] + then + : $[ n/=2 ] + fi + echo "Using $n test jobs" + ${build_dir}/rippled --unittest --unittest-jobs $n diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 8d6ff09b763..33245b4bfc6 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -135,7 +135,7 @@ constexpr std::uint32_t const tfTransferable = 0x00000008; constexpr std::uint32_t const tfMutable = 0x00000010; // MPTokenIssuanceCreate flags: -// NOTE - there is intentionally no flag here for lsfMPTLocked, which this transaction cannot mutate. +// NOTE - there is intentionally no flag here for lsfMPTLocked, which this transaction cannot mutate. constexpr std::uint32_t const tfMPTCanLock = lsfMPTCanLock; constexpr std::uint32_t const tfMPTRequireAuth = lsfMPTRequireAuth; constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow; diff --git a/include/xrpl/server/detail/Door.h b/include/xrpl/server/detail/Door.h index 20bdf421e38..7a42c8b5bf0 100644 --- a/include/xrpl/server/detail/Door.h +++ b/include/xrpl/server/detail/Door.h @@ -264,8 +264,9 @@ Door::reOpen() acceptor_.bind(local_address, ec); if (ec) { - JLOG(j_.error()) << "Bind port '" << port_.name - << "' failed:" << ec.message(); + JLOG(j_.error()) << "Bind port '" << port_.name << "', (" + << local_address << ")" + << "failed:" << ec.message(); Throw(); } diff --git a/src/test/app/MultiSign_test.cpp b/src/test/app/MultiSign_test.cpp index 77d85d9011b..b50c6e41d4b 100644 --- a/src/test/app/MultiSign_test.cpp +++ b/src/test/app/MultiSign_test.cpp @@ -644,6 +644,7 @@ class MultiSign_test : public beast::unit_test::suite Json::Value jv_submit; jv_submit[jss::tx_json] = jrr[jss::tx_json]; jrr = env.rpc( + env.rejectNever, "json", "submit_multisigned", to_string(jv_submit))[jss::result]; diff --git a/src/test/jtx/Env.h b/src/test/jtx/Env.h index b3f49ef838b..cd6214a5547 100644 --- a/src/test/jtx/Env.h +++ b/src/test/jtx/Env.h @@ -287,6 +287,26 @@ class Env return *bundle_.client; } + /// Given the return value of an RPC call, returns true if it is acceptable. + using RpcCallback = std::function; + /// Used as the default callback - any result is acceptable, except an + /// internal error, which usually indicates a timeout. + const RpcCallback rejectInternalError = [](Json::Value const& jr) { + auto const parsedResult = parseResult(jr); + return parsedResult.rpcCode != rpcINTERNAL; + }; + /// Used as the callback when any result is acceptable, particularly when an + /// internal error is expected. + const RpcCallback rejectNever = [](Json::Value const&) { return true; }; + /// Returns the appropriate callback when the decision of what is acceptible + /// is made at run time. For example, when conditional on API version + /// number. + RpcCallback + rejectInternalErrorIf(bool shouldFail) + { + return shouldFail ? rejectNever : rejectInternalError; + } + /** Execute an RPC command. The command is examined and used to build @@ -303,16 +323,43 @@ class Env Json::Value rpc(unsigned apiVersion, std::string const& cmd, Args&&... args); + template + Json::Value + rpc(RpcCallback cb, + std::unordered_map const& headers, + std::string const& cmd, + Args&&... args); + template Json::Value rpc(std::unordered_map const& headers, std::string const& cmd, Args&&... args); + template + Json::Value + rpc(RpcCallback cb, std::string const& cmd, Args&&... args); + template Json::Value rpc(std::string const& cmd, Args&&... args); + void + retry(std::function cb, std::string const& context); + + static void + retry( + std::function cb, + std::string const& context, + std::chrono::milliseconds delay); + + static void + retry( + std::function cb, + std::string const& context, + beast::unit_test::suite* test, + std::chrono::milliseconds delay); + /** Returns the current ledger. This is a non-modifiable snapshot of the @@ -517,7 +564,10 @@ class Env jtx::required(args...)(*this); } - /** Gets the TER result and `didApply` flag from a RPC Json result object. + /** Parses the Json result of an RPC request. + * + * Includes RPC result status, transaction engine result (TER) if + * appropriate, and error information, if any. */ static ParsedResult parseResult(Json::Value const& jr); @@ -703,6 +753,7 @@ class Env Json::Value do_rpc( + RpcCallback cb, unsigned apiVersion, std::vector const& args, std::unordered_map const& headers = {}); @@ -753,6 +804,7 @@ Env::rpc( Args&&... args) { return do_rpc( + rejectInternalError, apiVersion, std::vector{cmd, std::forward(args)...}, headers); @@ -772,16 +824,39 @@ Env::rpc(unsigned apiVersion, std::string const& cmd, Args&&... args) template Json::Value Env::rpc( + RpcCallback cb, std::unordered_map const& headers, std::string const& cmd, Args&&... args) { return do_rpc( + cb, RPC::apiCommandLineVersion, std::vector{cmd, std::forward(args)...}, headers); } +template +Json::Value +Env::rpc( + std::unordered_map const& headers, + std::string const& cmd, + Args&&... args) +{ + return rpc(rejectInternalError, headers, cmd, std::forward(args)...); +} + +template +Json::Value +Env::rpc(RpcCallback cb, std::string const& cmd, Args&&... args) +{ + return rpc( + cb, + std::unordered_map(), + cmd, + std::forward(args)...); +} + template Json::Value Env::rpc(std::string const& cmd, Args&&... args) diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 2ae57588589..b13c9255d8a 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -310,9 +310,18 @@ Env::parseResult(Json::Value const& jr) parsed.ter = TER::fromInt(result[jss::engine_result_code].asInt()); parsed.rpcCode.emplace(rpcSUCCESS); } + else if ( + !result.isMember(jss::error) && !result.isMember(jss::error_code) && + !result.isMember(jss::error_message) && + !result.isMember(jss::error_exception)) + // parsed.ter remains unseated + parsed.rpcCode.emplace(rpcSUCCESS); else error(parsed, result); } + else if ( + jr.isObject() && jr.isMember(jss::error) && jr[jss::error].isObject()) + error(parsed, jr[jss::error]); else error(parsed, jr); @@ -326,24 +335,20 @@ Env::submit(JTx const& jt) auto const jr = [&]() { if (jt.stx) { - // We shouldn't need to retry, but it fixes the test on macOS for - // the moment. - int retries = 3; - do - { - txid_ = jt.stx->getTransactionID(); - Serializer s; - jt.stx->add(s); - auto const jr = rpc("submit", strHex(s.slice())); - + txid_ = jt.stx->getTransactionID(); + Serializer s; + jt.stx->add(s); + auto const cb = [&](Json::Value const& jr) { parsedResult = parseResult(jr); test.expect(parsedResult.ter, "ter uninitialized!"); ter_ = parsedResult.ter.value_or(telENV_RPC_FAILED); - if (ter_ != telENV_RPC_FAILED || + return ( + ter_ != telENV_RPC_FAILED || parsedResult.rpcCode != rpcINTERNAL || - jt.ter == telENV_RPC_FAILED || --retries <= 0) - return jr; - } while (true); + jt.ter == telENV_RPC_FAILED); + }; + // rpc() will call cb(), which does all the parsing + return rpc(cb, "submit", strHex(s.slice())); } else { @@ -572,12 +577,94 @@ Env::ust(JTx const& jt) Json::Value Env::do_rpc( + RpcCallback cb, unsigned apiVersion, std::vector const& args, std::unordered_map const& headers) { - return rpcClient(args, app().config(), app().logs(), apiVersion, headers) - .second; + // We shouldn't need to retry, but it fixes the test on macOS for + // the moment. + if (!test.BEAST_EXPECT(cb)) + cb = rejectInternalError; + int retries = 3; + do + { + std::string retString; + try + { + auto const ret = + rpcClient( + args, app().config(), app().logs(), apiVersion, headers) + .second; + if (cb(ret) || --retries <= 0) + return ret; + test.log << "RPC failure: "; + retString = to_string(ret); + } + catch (std::exception const& e) + { + using namespace std::string_literals; + // TODO: Narrow down the exceptions that can be retried + if (--retries <= 0) + throw; + test.log << "RPC exception: "; + retString = e.what(); + } + std::stringstream ss; + for (auto const& arg : args) + { + ss << arg << ", "; + } + test.log << ss.str() << " -> " << retString << std::endl; + using namespace std::chrono_literals; + std::this_thread::sleep_for(100ms); + } while (true); +} + +void +Env::retry(std::function cb, std::string const& context) +{ + using namespace std::chrono_literals; + retry(cb, context, &test, 100ms); +} + +void +Env::retry( + std::function cb, + std::string const& context, + std::chrono::milliseconds delay) +{ + retry(cb, context, nullptr, delay); +} + +void +Env::retry( + std::function cb, + std::string const& context, + beast::unit_test::suite* test, + std::chrono::milliseconds delay) +{ + int retries = 3; + do + { + try + { + return cb(); + } + catch (std::exception const& e) + { + if (--retries <= 0) + throw; + if (test) + test->log << "Retry exception(" << context << "): " << e.what() + << std::endl; + // TODO remove + else + std::cout << "Retry exception(" << context << "): " << e.what() + << std::endl; + std::this_thread::sleep_for(delay); + } + } while (true); } void diff --git a/src/test/jtx/impl/JSONRPCClient.cpp b/src/test/jtx/impl/JSONRPCClient.cpp index 20f2149e4a0..a8119ff9749 100644 --- a/src/test/jtx/impl/JSONRPCClient.cpp +++ b/src/test/jtx/impl/JSONRPCClient.cpp @@ -17,6 +17,8 @@ */ //============================================================================== #include + +#include #include #include #include @@ -34,30 +36,6 @@ namespace test { class JSONRPCClient : public AbstractClient { - static boost::asio::ip::tcp::endpoint - getEndpoint(BasicConfig const& cfg) - { - auto& log = std::cerr; - ParsedPort common; - parse_Port(common, cfg["server"], log); - for (auto const& name : cfg.section("server").values()) - { - if (!cfg.exists(name)) - continue; - ParsedPort pp; - parse_Port(pp, cfg[name], log); - if (pp.protocol.count("http") == 0) - continue; - using namespace boost::asio::ip; - if (pp.ip && pp.ip->is_unspecified()) - *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} - : address{address_v4::loopback()}; - return {*pp.ip, *pp.port}; - } - Throw("Missing HTTP port"); - return {}; // Silence compiler control paths return value warning - } - template static std::string buffer_string(ConstBufferSequence const& b) @@ -70,6 +48,7 @@ class JSONRPCClient : public AbstractClient } boost::asio::ip::tcp::endpoint ep_; + std::string endpointLabel_; boost::asio::io_service ios_; boost::asio::ip::tcp::socket stream_; boost::beast::multi_buffer bin_; @@ -81,6 +60,9 @@ class JSONRPCClient : public AbstractClient : ep_(getEndpoint(cfg)), stream_(ios_), rpc_version_(rpc_version) { stream_.connect(ep_); + std::stringstream ss; + ss << ep_; + endpointLabel_ = ss.str(); } ~JSONRPCClient() override @@ -89,6 +71,30 @@ class JSONRPCClient : public AbstractClient // stream_.close(); } + static boost::asio::ip::tcp::endpoint + getEndpoint(BasicConfig const& cfg) + { + auto& log = std::cerr; + ParsedPort common; + parse_Port(common, cfg["server"], log); + for (auto const& name : cfg.section("server").values()) + { + if (!cfg.exists(name)) + continue; + ParsedPort pp; + parse_Port(pp, cfg[name], log); + if (pp.protocol.count("http") == 0) + continue; + using namespace boost::asio::ip; + if (pp.ip && pp.ip->is_unspecified()) + *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} + : address{address_v4::loopback()}; + return {*pp.ip, *pp.port}; + } + Throw("Missing HTTP port"); + return {}; // Silence compiler control paths return value warning + } + /* Return value is an Object type with up to three keys: status @@ -129,10 +135,20 @@ class JSONRPCClient : public AbstractClient req.body() = to_string(jr); } req.prepare_payload(); - write(stream_, req); + + using namespace std::chrono_literals; + using namespace ripple::test::jtx; + + Env::retry( + [&]() { write(stream_, req); }, + "JSONRPCClient::invoke write " + endpointLabel_, + 10ms); response res; - read(stream_, bin_, res); + Env::retry( + [&]() { read(stream_, bin_, res); }, + "JSONRPCClient::invoke read " + endpointLabel_, + 10ms); Json::Reader jr; Json::Value jv; @@ -154,7 +170,17 @@ class JSONRPCClient : public AbstractClient std::unique_ptr makeJSONRPCClient(Config const& cfg, unsigned rpc_version) { - return std::make_unique(cfg, rpc_version); + using namespace std::chrono_literals; + using namespace ripple::test::jtx; + + std::unique_ptr ret; + std::stringstream endpoint; + endpoint << JSONRPCClient::getEndpoint(cfg); + Env::retry( + [&]() { ret = std::make_unique(cfg, rpc_version); }, + "makeJSONRPCClient " + endpoint.str(), + 250ms); + return ret; } } // namespace test diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index 185d0ea5dba..6ff5d3aba61 100644 --- a/src/test/jtx/impl/WSClient.cpp +++ b/src/test/jtx/impl/WSClient.cpp @@ -50,31 +50,6 @@ class WSClientImpl : public WSClient } }; - static boost::asio::ip::tcp::endpoint - getEndpoint(BasicConfig const& cfg, bool v2) - { - auto& log = std::cerr; - ParsedPort common; - parse_Port(common, cfg["server"], log); - auto const ps = v2 ? "ws2" : "ws"; - for (auto const& name : cfg.section("server").values()) - { - if (!cfg.exists(name)) - continue; - ParsedPort pp; - parse_Port(pp, cfg[name], log); - if (pp.protocol.count(ps) == 0) - continue; - using namespace boost::asio::ip; - if (pp.ip && pp.ip->is_unspecified()) - *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} - : address{address_v4::loopback()}; - return {*pp.ip, *pp.port}; - } - Throw("Missing WebSocket port"); - return {}; // Silence compiler control paths return value warning - } - template static std::string buffer_string(ConstBuffers const& b) @@ -94,6 +69,7 @@ class WSClientImpl : public WSClient boost::asio::ip::tcp::socket stream_; boost::beast::websocket::stream ws_; boost::beast::multi_buffer rb_; + std::string endpointLabel_; bool peerClosed_ = false; @@ -153,6 +129,9 @@ class WSClientImpl : public WSClient rb_, strand_.wrap(std::bind( &WSClientImpl::on_read_msg, this, std::placeholders::_1))); + std::stringstream ss; + ss << ep; + endpointLabel_ = ss.str(); } catch (std::exception&) { @@ -166,6 +145,31 @@ class WSClientImpl : public WSClient cleanup(); } + static boost::asio::ip::tcp::endpoint + getEndpoint(BasicConfig const& cfg, bool v2) + { + auto& log = std::cerr; + ParsedPort common; + parse_Port(common, cfg["server"], log); + auto const ps = v2 ? "ws2" : "ws"; + for (auto const& name : cfg.section("server").values()) + { + if (!cfg.exists(name)) + continue; + ParsedPort pp; + parse_Port(pp, cfg[name], log); + if (pp.protocol.count(ps) == 0) + continue; + using namespace boost::asio::ip; + if (pp.ip && pp.ip->is_unspecified()) + *pp.ip = pp.ip->is_v6() ? address{address_v6::loopback()} + : address{address_v4::loopback()}; + return {*pp.ip, *pp.port}; + } + Throw("Missing WebSocket port"); + return {}; // Silence compiler control paths return value warning + } + Json::Value invoke(std::string const& cmd, Json::Value const& params) override { @@ -173,6 +177,8 @@ class WSClientImpl : public WSClient using namespace std::chrono_literals; { + using namespace ripple::test::jtx; + Json::Value jp; if (params) jp = params; @@ -186,7 +192,10 @@ class WSClientImpl : public WSClient else jp[jss::command] = cmd; auto const s = to_string(jp); - ws_.write_some(true, buffer(s)); + Env::retry( + [&]() { ws_.write_some(true, buffer(s)); }, + "WSClient::invoke write_some " + endpointLabel_, + 100ms); } auto jv = findMsg(5s, [&](Json::Value const& jval) { @@ -303,7 +312,20 @@ makeWSClient( unsigned rpc_version, std::unordered_map const& headers) { - return std::make_unique(cfg, v2, rpc_version, headers); + using namespace std::chrono_literals; + using namespace ripple::test::jtx; + + std::unique_ptr ret; + std::stringstream endpoint; + endpoint << WSClientImpl::getEndpoint(cfg, v2); + + Env::retry( + [&]() { + ret = std::make_unique(cfg, v2, rpc_version, headers); + }, + "makeWSClient " + endpoint.str(), + 250ms); + return ret; } } // namespace test diff --git a/src/test/jtx/impl/envconfig.cpp b/src/test/jtx/impl/envconfig.cpp index c9788a6d70f..64a38323821 100644 --- a/src/test/jtx/impl/envconfig.cpp +++ b/src/test/jtx/impl/envconfig.cpp @@ -26,10 +26,29 @@ namespace ripple { namespace test { int port_base = 8000; +int port_start = port_base; +int port_increment = 4; +int port_reserve = 24; +int port_max = port_base + port_reserve; void -incPorts(int times) +incPorts(int sets, int times) { - port_base += (4 * times); + port_increment *= sets; + port_base += (port_reserve * times); + port_reserve = std::max(port_reserve, port_increment * 2); + if (port_base + port_reserve > 65535) + { + // This should only happen if the runner uses + // (65535 - 8100) / 25 ~= 2297 threads. + // If the runner uses so many jobs that it overflows, wrap + // around and hope for the best. Even then, it won't collide + // unless the runner uses (65535 - 1024) / 25 ~= 2580 threads. + // If this ever becomes an issue, just decrease the + // port_reserve or something. + port_base = (port_base % 65535) + 1024; + } + port_start = port_base; + port_max = port_base + port_reserve; } std::atomic envUseIPv4{false}; @@ -37,6 +56,9 @@ std::atomic envUseIPv4{false}; void setupConfigForUnitTests(Config& cfg) { + port_base += port_increment; + if (port_base + port_increment >= port_max) + port_base = port_start; std::string const port_peer = std::to_string(port_base); std::string port_rpc = std::to_string(port_base + 1); std::string port_ws = std::to_string(port_base + 2); diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index 9352518dbac..bfb9f1e9aa4 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -2275,7 +2275,10 @@ class LedgerRPC_test : public beast::unit_test::suite }); Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; + env.rejectInternalErrorIf(apiVersion < 2u), + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; if (apiVersion < 2u) checkErrorValue(jrr, "internal", "Internal error."); @@ -2312,7 +2315,10 @@ class LedgerRPC_test : public beast::unit_test::suite }); Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; + env.rejectInternalErrorIf(apiVersion < 2u), + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; if (apiVersion < 2u) checkErrorValue(jrr, "internal", "Internal error."); @@ -2328,7 +2334,10 @@ class LedgerRPC_test : public beast::unit_test::suite }); Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; + env.rejectInternalErrorIf(apiVersion < 2u), + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; if (apiVersion < 2u) checkErrorValue(jrr, "internal", "Internal error."); @@ -2344,7 +2353,10 @@ class LedgerRPC_test : public beast::unit_test::suite }); Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; + env.rejectInternalErrorIf(apiVersion < 2u), + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; if (apiVersion < 2u) checkErrorValue(jrr, "internal", "Internal error."); @@ -2360,7 +2372,10 @@ class LedgerRPC_test : public beast::unit_test::suite }); Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; + env.rejectInternalErrorIf(apiVersion < 2u), + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; if (apiVersion < 2u) checkErrorValue(jrr, "internal", "Internal error."); @@ -2383,7 +2398,10 @@ class LedgerRPC_test : public beast::unit_test::suite }); Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; + env.rejectInternalErrorIf(apiVersion < 2u), + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; if (apiVersion < 2u) checkErrorValue(jrr, "internal", "Internal error."); @@ -2399,7 +2417,10 @@ class LedgerRPC_test : public beast::unit_test::suite }); Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; + env.rejectInternalErrorIf(apiVersion < 2u), + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; if (apiVersion < 2u) checkErrorValue(jrr, "internal", "Internal error."); diff --git a/src/test/rpc/LedgerRequestRPC_test.cpp b/src/test/rpc/LedgerRequestRPC_test.cpp index 8922cd38386..c9f375e435a 100644 --- a/src/test/rpc/LedgerRequestRPC_test.cpp +++ b/src/test/rpc/LedgerRequestRPC_test.cpp @@ -347,7 +347,8 @@ class LedgerRequestRPC_test : public beast::unit_test::suite auto const USD = gw["USD"]; env.fund(XRP(100000), gw); - auto const result = env.rpc("ledger_request", "1")[jss::result]; + auto const result = + env.rpc(env.rejectNever, "ledger_request", "1")[jss::result]; // The current HTTP/S ServerHandler returns an HTTP 403 error code here // rather than a noPermission JSON error. The JSONRPCClient just eats // that error and returns an null result. diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index f1cb2f9a135..4b83b4e47f2 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -1284,6 +1284,8 @@ class Subscribe_test : public beast::unit_test::suite auto wscShort = makeWSClient(env.app().config()); auto jv = wscShort->invoke("subscribe", request); IdxHashVec vec2; + if (!BEAST_EXPECT(jv)) + return; if (!BEAST_EXPECT(getTxHash(*wscShort, vec2, count).first)) return; if (!BEAST_EXPECT(hashCompare(vec1, vec2, true))) diff --git a/src/test/rpc/ValidatorInfo_test.cpp b/src/test/rpc/ValidatorInfo_test.cpp index 603a0ad9d23..7fa8b23f9a7 100644 --- a/src/test/rpc/ValidatorInfo_test.cpp +++ b/src/test/rpc/ValidatorInfo_test.cpp @@ -50,7 +50,8 @@ class ValidatorInfo_test : public beast::unit_test::suite { using namespace test::jtx; Env env{*this, envconfig(no_admin)}; - auto const info = env.rpc("validator_info")[jss::result]; + auto const info = + env.rpc(env.rejectNever, "validator_info")[jss::result]; BEAST_EXPECT(info.isNull()); } diff --git a/src/test/rpc/ValidatorRPC_test.cpp b/src/test/rpc/ValidatorRPC_test.cpp index 2bd4b69c37b..97081888dfc 100644 --- a/src/test/rpc/ValidatorRPC_test.cpp +++ b/src/test/rpc/ValidatorRPC_test.cpp @@ -49,7 +49,8 @@ class ValidatorRPC_test : public beast::unit_test::suite for (std::string cmd : {"validators", "validator_list_sites"}) { Env env{*this, isAdmin ? envconfig() : envconfig(no_admin)}; - auto const jrr = env.rpc(cmd)[jss::result]; + auto const jrr = env.rpc( + env.rejectInternalErrorIf(!isAdmin), cmd)[jss::result]; if (isAdmin) { BEAST_EXPECT(!jrr.isMember(jss::error)); diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index 8aa7bf19f30..46f828fa8ff 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -485,12 +485,18 @@ class ServerStatus_test : public beast::unit_test::suite, async_connect(sock, it, yield[ec]); if (!BEAST_EXPECTS(!ec, ec.message())) return; - async_write(sock, boost::asio::buffer(req_string), yield[ec]); + env.retry( + [&]() { + async_write(sock, boost::asio::buffer(req_string), yield[ec]); + }, + "ServerStatus testTruncatedWSUpgrade async_write"); if (!BEAST_EXPECTS(!ec, ec.message())) return; // since we've sent an incomplete request, the server will // keep trying to read until it gives up (by timeout) - async_read(sock, sb, resp, yield[ec]); + env.retry( + [&]() { async_read(sock, sb, resp, yield[ec]); }, + "ServerStatus testTruncatedWSUpgrade async_read"); BEAST_EXPECT(ec); } diff --git a/src/test/unit_test/multi_runner.cpp b/src/test/unit_test/multi_runner.cpp index 60487cadfb8..57c4e8279bc 100644 --- a/src/test/unit_test/multi_runner.cpp +++ b/src/test/unit_test/multi_runner.cpp @@ -33,7 +33,7 @@ namespace ripple { namespace test { extern void -incPorts(int times); +incPorts(int sets, int times); namespace detail { @@ -511,7 +511,7 @@ multi_runner_child::multi_runner_child( , print_log_{!quiet || print_log} { // incPort twice (2*jobIndex_) because some tests need two envs - test::incPorts(2 * job_index_); + test::incPorts(2, job_index_); if (num_jobs_ > 1) {