This commit is contained in:
JCW
2026-07-19 10:21:04 +01:00
parent 5ce0b1c2c7
commit 5671187384
126 changed files with 1754 additions and 1854 deletions

View File

@@ -52,13 +52,13 @@ public:
% if field.get('mpt_support'):
* MPT Support: ${field['mpt_support']}
% endif
% if field['requirement'] == 'SoeRequired':
% if field['requirement'] == 'SoeRequired' or field['requirement'] == 'SoeDefault':
* @return The field value.
% else:
* @return The field value, or std::nullopt if not present.
% endif
*/
% if field['requirement'] == 'SoeRequired':
% if field['requirement'] == 'SoeRequired' or field['requirement'] == 'SoeDefault':
[[nodiscard]]
${field['typeData']['return_type']}
get${field['name'][2:]}() const
@@ -94,13 +94,13 @@ public:
* MPT Support: ${field['mpt_support']}
% endif
* @note This is an untyped field (${field.get('cppType', 'unknown')}).
% if field['requirement'] == 'SoeRequired':
% if field['requirement'] == 'SoeRequired' or field['requirement'] == 'SoeDefault':
* @return The field value.
% else:
* @return The field value, or std::nullopt if not present.
% endif
*/
% if field['requirement'] == 'SoeRequired':
% if field['requirement'] == 'SoeRequired' or field['requirement'] == 'SoeDefault':
[[nodiscard]]
${field['typeData']['return_type']}
get${field['name'][2:]}() const

View File

@@ -1,19 +1,31 @@
// Auto-generated unit tests for ledger entry ${name}
<%! from itertools import chain %>\
<%
default_fields = [f for f in fields if f["requirement"] == "SoeDefault"]
required_fields = [f for f in fields if f["requirement"] == "SoeRequired"]
optional_fields = [f for f in fields if f["requirement"] != "SoeRequired"]
optional_fields = [f for f in fields if f["requirement"] == "SoeOptional"]
def canonical_expr(field):
return f"canonical_{field['stiSuffix']}()"
def default_expr(field):
# The value a SoeDefault field's getter returns when the field is unset.
if field["typed"]:
return f"{field['typeData']['return_type']}{{}}"
sti = field.get("stiSuffix")
if sti == "ARRAY":
return "STArray{}"
if sti == "PATHSET":
return "STPathSet{}"
return "STObject{sfGeneric}"
# Pick a wrong ledger entry to test type mismatch
# Use Ticket as it has minimal required fields (just Account)
if name != "Ticket":
wrong_le_include = "Ticket"
else:
wrong_le_include = "Check"
%>
%>\
#include <gtest/gtest.h>
#include <protocol_autogen/TestHelpers.h>
@@ -46,6 +58,9 @@ TEST(${name}Tests, BuilderSettersRoundTrip)
% for field in optional_fields:
builder.set${field["name"][2:]}(${field["paramName"]}Value);
% endfor
% for field in default_fields:
builder.set${field["name"][2:]}(${field["paramName"]}Value);
% endfor
builder.setLedgerIndex(index);
builder.setFlags(0x1u);
@@ -56,7 +71,7 @@ TEST(${name}Tests, BuilderSettersRoundTrip)
EXPECT_TRUE(entry.validate());
% for field in required_fields:
% for field in chain(required_fields, default_fields):
{
auto const& expected = ${field["paramName"]}Value;
auto const actual = entry.get${field["name"][2:]}();
@@ -112,7 +127,7 @@ TEST(${name}Tests, BuilderFromSleRoundTrip)
EXPECT_TRUE(entryFromBuilder.validate());
EXPECT_TRUE(entryFromSle.validate());
% for field in required_fields:
% for field in chain(required_fields, default_fields):
{
auto const& expected = ${field["paramName"]}Value;
@@ -228,4 +243,48 @@ TEST(${name}Tests, OptionalFieldsReturnNullopt)
% endfor
}
% endif
% if default_fields:
// 6) Default fields return the type default when unset, and the assigned value
// after being set.
TEST(${name}Tests, DefaultFieldsRoundTrip)
{
uint256 const index{4u};
% for field in required_fields:
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
% endfor
// Unset: default fields return the type default.
${name}Builder defaultBuilder{
% for i, field in enumerate(required_fields):
${field["paramName"]}Value${"," if i < len(required_fields) - 1 else ""}
% endfor
};
auto const defaultEntry = defaultBuilder.build(index);
% for field in default_fields:
{
auto const expected = ${default_expr(field)};
expectEqualField(expected, defaultEntry.get${field["name"][2:]}(), "${field["name"]}");
}
% endfor
// Set: default fields return the assigned value.
${name}Builder setBuilder{
% for i, field in enumerate(required_fields):
${field["paramName"]}Value${"," if i < len(required_fields) - 1 else ""}
% endfor
};
% for field in default_fields:
setBuilder.set${field["name"][2:]}(${canonical_expr(field)});
% endfor
auto const setEntry = setBuilder.build(index);
% for field in default_fields:
{
auto const expected = ${canonical_expr(field)};
expectEqualField(expected, setEntry.get${field["name"][2:]}(), "${field["name"]}");
}
% endfor
}
% endif
}

View File

@@ -54,7 +54,7 @@ public:
% if field.get('supports_mpt'):
* @note This field supports MPT (Multi-Purpose Token) amounts.
% endif
% if field['requirement'] == 'SoeRequired':
% if field['requirement'] == 'SoeRequired' or field['requirement'] == 'SoeDefault':
* @return The field value.
% else:
* @return The field value, or std::nullopt if not present.
@@ -97,13 +97,13 @@ public:
* @note This field supports MPT (Multi-Purpose Token) amounts.
% endif
* @note This is an untyped field.
% if field['requirement'] == 'SoeRequired':
% if field['requirement'] == 'SoeRequired' or field['requirement'] == 'SoeDefault':
* @return The field value.
% else:
* @return The field value, or std::nullopt if not present.
% endif
*/
% if field['requirement'] == 'SoeRequired':
% if field['requirement'] == 'SoeRequired' or field['requirement'] == 'SoeDefault':
[[nodiscard]]
${field['typeData']['return_type']}
get${field['name'][2:]}() const

View File

@@ -1,18 +1,30 @@
// Auto-generated unit tests for transaction ${name}
<%! from itertools import chain %>\
<%
default_fields = [f for f in fields if f["requirement"] == "SoeDefault"]
required_fields = [f for f in fields if f["requirement"] == "SoeRequired"]
optional_fields = [f for f in fields if f["requirement"] != "SoeRequired"]
optional_fields = [f for f in fields if f["requirement"] == "SoeOptional"]
def canonical_expr(field):
return f"canonical_{field['stiSuffix']}()"
def default_expr(field):
# The value a SoeDefault field's getter returns when the field is unset.
if field["typed"]:
return f"{field['typeData']['return_type']}{{}}"
sti = field.get("stiSuffix")
if sti == "ARRAY":
return "STArray{}"
if sti == "PATHSET":
return "STPathSet{}"
return "STObject{sfGeneric}"
# Pick a wrong transaction to test type mismatch
if name != "AccountSet":
wrong_tx_include = "AccountSet"
else:
wrong_tx_include = "OfferCancel"
%>
%>\
#include <gtest/gtest.h>
#include <protocol_autogen/TestHelpers.h>
@@ -58,6 +70,10 @@ TEST(Transactions${name}Tests, BuilderSettersRoundTrip)
% for field in optional_fields:
builder.set${field["name"][2:]}(${field["paramName"]}Value);
% endfor
// Set default fields
% for field in default_fields:
builder.set${field["name"][2:]}(${field["paramName"]}Value);
% endfor
auto tx = builder.build(publicKey, secretKey);
@@ -73,8 +89,8 @@ TEST(Transactions${name}Tests, BuilderSettersRoundTrip)
EXPECT_EQ(tx.getSequence(), sequenceValue);
EXPECT_EQ(tx.getFee(), feeValue);
// Verify required fields
% for field in required_fields:
// Verify required and default fields
% for field in chain(required_fields, default_fields):
{
auto const& expected = ${field["paramName"]}Value;
auto const actual = tx.get${field["name"][2:]}();
@@ -126,6 +142,9 @@ TEST(Transactions${name}Tests, BuilderFromStTxRoundTrip)
% for field in optional_fields:
initialBuilder.set${field["name"][2:]}(${field["paramName"]}Value);
% endfor
% for field in default_fields:
initialBuilder.set${field["name"][2:]}(${field["paramName"]}Value);
% endfor
auto initialTx = initialBuilder.build(publicKey, secretKey);
@@ -142,8 +161,8 @@ TEST(Transactions${name}Tests, BuilderFromStTxRoundTrip)
EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue);
EXPECT_EQ(rebuiltTx.getFee(), feeValue);
// Verify required fields
% for field in required_fields:
// Verify required and default fields
% for field in chain(required_fields, default_fields):
{
auto const& expected = ${field["paramName"]}Value;
auto const actual = rebuiltTx.get${field["name"][2:]}();
@@ -238,4 +257,62 @@ TEST(Transactions${name}Tests, OptionalFieldsReturnNullopt)
}
% endif
% if default_fields:
// 6) Default fields return the type default when unset, and the assigned value
// after being set.
TEST(Transactions${name}Tests, DefaultFieldsRoundTrip)
{
// Generate a deterministic keypair for signing
auto const [publicKey, secretKey] =
generateKeyPair(KeyType::Secp256k1, generateSeed("test${name}Default"));
// Common transaction fields
auto const accountValue = calcAccountID(publicKey);
std::uint32_t const sequenceValue = 4;
auto const feeValue = canonical_AMOUNT();
// Transaction-specific required field values
% for field in required_fields:
auto const ${field["paramName"]}Value = ${canonical_expr(field)};
% endfor
// Unset: default fields return the type default.
${name}Builder defaultBuilder{
accountValue,
% for field in required_fields:
${field["paramName"]}Value,
% endfor
sequenceValue,
feeValue
};
auto const defaultTx = defaultBuilder.build(publicKey, secretKey);
% for field in default_fields:
{
auto const expected = ${default_expr(field)};
expectEqualField(expected, defaultTx.get${field["name"][2:]}(), "${field["name"]}");
}
% endfor
// Set: default fields return the assigned value.
${name}Builder setBuilder{
accountValue,
% for field in required_fields:
${field["paramName"]}Value,
% endfor
sequenceValue,
feeValue
};
% for field in default_fields:
setBuilder.set${field["name"][2:]}(${canonical_expr(field)});
% endfor
auto const setTx = setBuilder.build(publicKey, secretKey);
% for field in default_fields:
{
auto const expected = ${canonical_expr(field)};
expectEqualField(expected, setTx.get${field["name"][2:]}(), "${field["name"]}");
}
% endfor
}
% endif
}