Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 46 additions & 9 deletions src/test/jtx/Env.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,46 @@
#include <xrpl/protocol/STTx.h>

#include <functional>
#include <source_location>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <variant>
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <variant> header is included but the standard library already provides all necessary functionality. Consider documenting why std::variant is required here, as it adds complexity to the WithSourceLoc implementation. A simpler approach might be to use two separate wrapper types or template the wrapper class.

Copilot uses AI. Check for mistakes.
#include <vector>

namespace xrpl {
namespace test {
namespace jtx {

class Env; // Forward declaration

/** Wrapper that captures source_location when implicitly constructed.
This solves the problem of combining std::source_location with variadic
templates. The source_location default argument is evaluated at the
call site when the wrapper is constructed via implicit conversion.

This is a non-template struct that uses std::variant to hold either
Json::Value or JTx, allowing implicit conversion without template
argument deduction issues.
*/
struct WithSourceLoc
{
std::variant<Json::Value, JTx> value;
std::source_location loc;

// Non-explicit constructors allow implicit conversion.
// The default argument for loc is evaluated at the call site.
WithSourceLoc(Json::Value v, std::source_location l = std::source_location::current()) : value(std::move(v)), loc(l)
{
}

WithSourceLoc(JTx v, std::source_location l = std::source_location::current()) : value(std::move(v)), loc(l)
{
}
Comment on lines +66 to +72
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both constructors can be implemented more concisely on a single line since the bodies are empty. Consider: WithSourceLoc(Json::Value v, std::source_location l = std::source_location::current()) : value(std::move(v)), loc(l) {}

Suggested change
WithSourceLoc(Json::Value v, std::source_location l = std::source_location::current()) : value(std::move(v)), loc(l)
{
}
WithSourceLoc(JTx v, std::source_location l = std::source_location::current()) : value(std::move(v)), loc(l)
{
}
WithSourceLoc(Json::Value v, std::source_location l = std::source_location::current()) : value(std::move(v)), loc(l) {}
WithSourceLoc(JTx v, std::source_location l = std::source_location::current()) : value(std::move(v)), loc(l) {}

Copilot uses AI. Check for mistakes.
};

/** Designate accounts as no-ripple in Env::fund */
template <class... Args>
std::array<Account, 1 + sizeof...(Args)>
Expand Down Expand Up @@ -523,35 +552,43 @@ class Env
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(WithSourceLoc jv, FN const&... fN)
{
submit(jt(std::forward<JsonValue>(jv), fN...));
std::visit([&](auto&& v) { submit(jt(std::move(v), fN...), jv.loc); }, std::move(jv.value));
return *this;
}

template <class JsonValue, class... FN>
template <class... FN>
Env&
operator()(JsonValue&& jv, FN const&... fN)
operator()(WithSourceLoc jv, FN const&... fN)
{
return apply(std::forward<JsonValue>(jv), fN...);
std::visit([&](auto&& v) { submit(jt(std::move(v), fN...), jv.loc); }, std::move(jv.value));
return *this;
}
/** @} */

Expand Down
18 changes: 10 additions & 8 deletions src/test/jtx/impl/Env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <xrpl/protocol/jss.h>

#include <memory>
#include <source_location>

namespace xrpl {
namespace test {
Expand Down Expand Up @@ -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 = [&]() {
Expand All @@ -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();
Expand Down Expand Up @@ -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) + ")" : "";
auto const file = std::string("(") + loc.file_name() + ":" + to_string(loc.line()) + ")";
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file variable name is misleading since it contains both filename and line number in a formatted string. Consider renaming to location or source_info to better reflect its content.

Copilot uses AI. Check for mistakes.
bool bad = !test.expect(parsed.ter, "apply: 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 " + file + ": 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 " + file + ": 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)) ||
Expand All @@ -424,7 +426,7 @@ Env::postconditions(JTx const& jt, ParsedResult const& parsed, Json::Value const
(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 " +
"apply " + file + ": Got RPC result "s + parsed.rpcError + " (" + parsed.rpcException + "); Expected " +
jt.rpcException->first + " (" + jt.rpcException->second.value_or("n/a") + ")" + line)) ||
bad;
if (bad)
Expand Down
Loading