mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
test: Add file and line location to Env (#6276)
This change uses `std::source_location` to output the file and line location of the call that triggered a failed transaction.
This commit is contained in:
@@ -491,8 +491,7 @@ struct LedgerServer
|
||||
for (int i = 0; i < newTxes; ++i)
|
||||
{
|
||||
updateIdx();
|
||||
env.apply(
|
||||
pay(accounts[fromIdx],
|
||||
env(pay(accounts[fromIdx],
|
||||
accounts[toIdx],
|
||||
jtx::drops(ledgerMaster.getClosedLedger()->fees().base) + jtx::XRP(param.txAmount)),
|
||||
jtx::seq(jtx::autofill),
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
|
||||
#include <functional>
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -42,6 +43,27 @@ namespace xrpl {
|
||||
namespace test {
|
||||
namespace jtx {
|
||||
|
||||
/** Wrapper that captures std::source_location when implicitly constructed.
|
||||
This solves the problem of combining std::source_location with variadic
|
||||
templates. The std::source_location default argument is evaluated at the
|
||||
call site when the wrapper is constructed via implicit conversion.
|
||||
|
||||
This is a template struct that holds the value directly, allowing implicit
|
||||
conversion without template argument deduction issues via CTAD.
|
||||
*/
|
||||
template <class T>
|
||||
struct WithSourceLocation
|
||||
{
|
||||
T value;
|
||||
std::source_location loc;
|
||||
|
||||
// Non-explicit constructor allows implicit conversion.
|
||||
// The default argument for loc is evaluated at the call site.
|
||||
WithSourceLocation(T v, std::source_location l = std::source_location::current()) : value(std::move(v)), loc(l)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** Designate accounts as no-ripple in Env::fund */
|
||||
template <class... Args>
|
||||
std::array<Account, 1 + sizeof...(Args)>
|
||||
@@ -522,35 +544,57 @@ public:
|
||||
This calls postconditions.
|
||||
*/
|
||||
virtual void
|
||||
submit(JTx const& jt);
|
||||
submit(JTx const& jt, std::source_location const& loc = std::source_location::current());
|
||||
|
||||
/** Use the submit RPC command with a provided JTx object.
|
||||
This calls postconditions.
|
||||
*/
|
||||
void
|
||||
sign_and_submit(JTx const& jt, Json::Value params = Json::nullValue);
|
||||
sign_and_submit(
|
||||
JTx const& jt,
|
||||
Json::Value params = Json::nullValue,
|
||||
std::source_location const& loc = std::source_location::current());
|
||||
|
||||
/** Check expected postconditions
|
||||
of JTx submission.
|
||||
*/
|
||||
void
|
||||
postconditions(JTx const& jt, ParsedResult const& parsed, Json::Value const& jr = Json::Value());
|
||||
postconditions(
|
||||
JTx const& jt,
|
||||
ParsedResult const& parsed,
|
||||
Json::Value const& jr = Json::Value(),
|
||||
std::source_location const& loc = std::source_location::current());
|
||||
|
||||
/** Apply funclets and submit. */
|
||||
/** @{ */
|
||||
template <class JsonValue, class... FN>
|
||||
template <class... FN>
|
||||
Env&
|
||||
apply(JsonValue&& jv, FN const&... fN)
|
||||
apply(WithSourceLocation<Json::Value> jv, FN const&... fN)
|
||||
{
|
||||
submit(jt(std::forward<JsonValue>(jv), fN...));
|
||||
submit(jt(std::move(jv.value), fN...), jv.loc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class JsonValue, class... FN>
|
||||
template <class... FN>
|
||||
Env&
|
||||
operator()(JsonValue&& jv, FN const&... fN)
|
||||
apply(WithSourceLocation<JTx> jv, FN const&... fN)
|
||||
{
|
||||
return apply(std::forward<JsonValue>(jv), fN...);
|
||||
submit(jt(std::move(jv.value), fN...), jv.loc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class... FN>
|
||||
Env&
|
||||
operator()(WithSourceLocation<Json::Value> jv, FN const&... fN)
|
||||
{
|
||||
return apply(std::move(jv), fN...);
|
||||
}
|
||||
|
||||
template <class... FN>
|
||||
Env&
|
||||
operator()(WithSourceLocation<JTx> jv, FN const&... fN)
|
||||
{
|
||||
return apply(std::move(jv), fN...);
|
||||
}
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -23,19 +23,20 @@ private:
|
||||
SignSubmitRunner&
|
||||
operator=(SignSubmitRunner&&) = delete;
|
||||
|
||||
SignSubmitRunner(Env& env, JTx&& jt) : env_(env), jt_(jt)
|
||||
SignSubmitRunner(Env& env, JTx&& jt, std::source_location loc) : env_(env), jt_(jt), loc_(loc)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Json::Value const& params = Json::nullValue)
|
||||
{
|
||||
env_.sign_and_submit(jt_, params);
|
||||
env_.sign_and_submit(jt_, params, loc_);
|
||||
}
|
||||
|
||||
private:
|
||||
Env& env_;
|
||||
JTx const jt_;
|
||||
std::source_location const loc_;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -47,12 +48,20 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
template <class JsonValue, class... FN>
|
||||
template <class... FN>
|
||||
SignSubmitRunner
|
||||
operator()(JsonValue&& jv, FN const&... fN)
|
||||
operator()(WithSourceLocation<Json::Value> jv, FN const&... fN)
|
||||
{
|
||||
auto jtx = env_.jt(std::forward<JsonValue>(jv), fN...);
|
||||
return SignSubmitRunner(env_, std::move(jtx));
|
||||
auto jtx = env_.jt(std::move(jv.value), fN...);
|
||||
return SignSubmitRunner(env_, std::move(jtx), jv.loc);
|
||||
}
|
||||
|
||||
template <class... FN>
|
||||
SignSubmitRunner
|
||||
operator()(WithSourceLocation<JTx> jv, FN const&... fN)
|
||||
{
|
||||
auto jtx = env_.jt(std::move(jv.value), fN...);
|
||||
return SignSubmitRunner(env_, std::move(jtx), jv.loc);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <memory>
|
||||
#include <source_location>
|
||||
|
||||
namespace xrpl {
|
||||
namespace test {
|
||||
@@ -330,7 +331,7 @@ Env::parseResult(Json::Value const& jr)
|
||||
}
|
||||
|
||||
void
|
||||
Env::submit(JTx const& jt)
|
||||
Env::submit(JTx const& jt, std::source_location const& loc)
|
||||
{
|
||||
ParsedResult parsedResult;
|
||||
auto const jr = [&]() {
|
||||
@@ -356,11 +357,11 @@ Env::submit(JTx const& jt)
|
||||
return Json::Value();
|
||||
}
|
||||
}();
|
||||
return postconditions(jt, parsedResult, jr);
|
||||
return postconditions(jt, parsedResult, jr, loc);
|
||||
}
|
||||
|
||||
void
|
||||
Env::sign_and_submit(JTx const& jt, Json::Value params)
|
||||
Env::sign_and_submit(JTx const& jt, Json::Value params, std::source_location const& loc)
|
||||
{
|
||||
auto const account = lookup(jt.jv[jss::Account].asString());
|
||||
auto const& passphrase = account.name();
|
||||
@@ -393,25 +394,26 @@ Env::sign_and_submit(JTx const& jt, Json::Value params)
|
||||
test.expect(parsedResult.ter, "ter uninitialized!");
|
||||
ter_ = parsedResult.ter.value_or(telENV_RPC_FAILED);
|
||||
|
||||
return postconditions(jt, parsedResult, jr);
|
||||
return postconditions(jt, parsedResult, jr, loc);
|
||||
}
|
||||
|
||||
void
|
||||
Env::postconditions(JTx const& jt, ParsedResult const& parsed, Json::Value const& jr)
|
||||
Env::postconditions(JTx const& jt, ParsedResult const& parsed, Json::Value const& jr, std::source_location const& loc)
|
||||
{
|
||||
auto const line = jt.testLine ? " (" + to_string(*jt.testLine) + ")" : "";
|
||||
bool bad = !test.expect(parsed.ter, "apply: No ter result!" + line);
|
||||
auto const locStr = std::string("(") + loc.file_name() + ":" + to_string(loc.line()) + ")";
|
||||
bool bad = !test.expect(parsed.ter, "apply " + locStr + ": No ter result!" + line);
|
||||
bad =
|
||||
(jt.ter && parsed.ter &&
|
||||
!test.expect(
|
||||
*parsed.ter == *jt.ter,
|
||||
"apply: Got " + transToken(*parsed.ter) + " (" + transHuman(*parsed.ter) + "); Expected " +
|
||||
"apply " + locStr + ": Got " + transToken(*parsed.ter) + " (" + transHuman(*parsed.ter) + "); Expected " +
|
||||
transToken(*jt.ter) + " (" + transHuman(*jt.ter) + ")" + line));
|
||||
using namespace std::string_literals;
|
||||
bad = (jt.rpcCode &&
|
||||
!test.expect(
|
||||
parsed.rpcCode == jt.rpcCode->first && parsed.rpcMessage == jt.rpcCode->second,
|
||||
"apply: Got RPC result "s +
|
||||
"apply " + locStr + ": Got RPC result "s +
|
||||
(parsed.rpcCode ? RPC::get_error_info(*parsed.rpcCode).token.c_str() : "NO RESULT") + " (" +
|
||||
parsed.rpcMessage + "); Expected " + RPC::get_error_info(jt.rpcCode->first).token.c_str() + " (" +
|
||||
jt.rpcCode->second + ")" + line)) ||
|
||||
@@ -419,13 +421,14 @@ Env::postconditions(JTx const& jt, ParsedResult const& parsed, Json::Value const
|
||||
// If we have an rpcCode (just checked), then the rpcException check is
|
||||
// optional - the 'error' field may not be defined, but if it is, it must
|
||||
// match rpcError.
|
||||
bad = (jt.rpcException &&
|
||||
!test.expect(
|
||||
(jt.rpcCode && parsed.rpcError.empty()) ||
|
||||
(parsed.rpcError == jt.rpcException->first &&
|
||||
(!jt.rpcException->second || parsed.rpcException == *jt.rpcException->second)),
|
||||
"apply: Got RPC result "s + parsed.rpcError + " (" + parsed.rpcException + "); Expected " +
|
||||
jt.rpcException->first + " (" + jt.rpcException->second.value_or("n/a") + ")" + line)) ||
|
||||
bad =
|
||||
(jt.rpcException &&
|
||||
!test.expect(
|
||||
(jt.rpcCode && parsed.rpcError.empty()) ||
|
||||
(parsed.rpcError == jt.rpcException->first &&
|
||||
(!jt.rpcException->second || parsed.rpcException == *jt.rpcException->second)),
|
||||
"apply " + locStr + ": Got RPC result "s + parsed.rpcError + " (" + parsed.rpcException + "); Expected " +
|
||||
jt.rpcException->first + " (" + jt.rpcException->second.value_or("n/a") + ")" + line)) ||
|
||||
bad;
|
||||
if (bad)
|
||||
{
|
||||
|
||||
@@ -835,8 +835,7 @@ public:
|
||||
{
|
||||
auto& from = (i % 2 == 0) ? a : b;
|
||||
auto& to = (i % 2 == 0) ? b : a;
|
||||
env.apply(
|
||||
pay(from, to, jtx::XRP(numXRP)),
|
||||
env(pay(from, to, jtx::XRP(numXRP)),
|
||||
jtx::seq(jtx::autofill),
|
||||
jtx::fee(jtx::autofill),
|
||||
jtx::sig(jtx::autofill));
|
||||
|
||||
Reference in New Issue
Block a user