Normalize files containing unit test code:

Source files are split to place all unit test code into translation
units ending in .test.cpp with no other business logic in the same file,
and in directories named "test".

A new target is added to the SConstruct, invoked by:
    scons count
This prints the total number of source code lines occupied by unit tests,
in rippled specific code and excluding library subtrees.
This commit is contained in:
Vinnie Falco
2014-12-23 12:28:19 -08:00
parent 9eb7c8344f
commit 9a3214d46e
65 changed files with 2396 additions and 2788 deletions

View File

@@ -550,551 +550,5 @@ transactionSign (
}
}
//------------------------------------------------------------------------------
// Struct used to test calls to transactionSign and transactionSubmit.
struct TxnTestData
{
// Gah, without constexpr I can't make this an enum and initialize
// OR operators at compile time. Punting with integer constants.
static unsigned int const allGood = 0x0;
static unsigned int const signFail = 0x1;
static unsigned int const submitFail = 0x2;
char const* const json;
unsigned int result;
TxnTestData () = delete;
TxnTestData (TxnTestData const&) = delete;
TxnTestData& operator= (TxnTestData const&) = delete;
TxnTestData (char const* jsonIn, unsigned int resultIn)
: json (jsonIn)
, result (resultIn)
{ }
};
// Declare storage for statics to avoid link errors.
unsigned int const TxnTestData::allGood;
unsigned int const TxnTestData::signFail;
unsigned int const TxnTestData::submitFail;
static TxnTestData const txnTestArray [] =
{
// Minimal payment.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// Pass in Fee with minimal payment.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Fee": 10,
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// Pass in Sequence.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Sequence": 0,
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// Pass in Sequence and Fee with minimal payment.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Sequence": 0,
"Fee": 10,
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// Add "fee_mult_max" field.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"fee_mult_max": 7,
"tx_json": {
"Sequence": 0,
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// "fee_mult_max is ignored if "Fee" is present.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"fee_mult_max": 0,
"tx_json": {
"Sequence": 0,
"Fee": 10,
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// Invalid "fee_mult_max" field.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"fee_mult_max": "NotAFeeMultiplier",
"tx_json": {
"Sequence": 0,
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// Invalid value for "fee_mult_max" field.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"fee_mult_max": 0,
"tx_json": {
"Sequence": 0,
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// Missing "Amount".
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// Invalid "Amount".
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "NotAnAmount",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// Missing "Destination".
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// Invalid "Destination".
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "NotADestination",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// Cannot create XRP to XRP paths.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"build_path": 1,
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// Successful "build_path".
{R"({
"command": "submit",
"secret": "masterpassphrase",
"build_path": 1,
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": {
"value": "10",
"currency": "USD",
"issuer": "0123456789012345678901234567890123456789"
},
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// Not valid to include both "Paths" and "build_path".
{R"({
"command": "submit",
"secret": "masterpassphrase",
"build_path": 1,
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": {
"value": "10",
"currency": "USD",
"issuer": "0123456789012345678901234567890123456789"
},
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"Paths": "",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// Successful "SendMax".
{R"({
"command": "submit",
"secret": "masterpassphrase",
"build_path": 1,
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": {
"value": "10",
"currency": "USD",
"issuer": "0123456789012345678901234567890123456789"
},
"SendMax": {
"value": "5",
"currency": "USD",
"issuer": "0123456789012345678901234567890123456789"
},
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// Even though "Amount" may not be XRP for pathfinding, "SendMax" may be XRP.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"build_path": 1,
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": {
"value": "10",
"currency": "USD",
"issuer": "0123456789012345678901234567890123456789"
},
"SendMax": 10000,
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// "secret" must be present.
{R"({
"command": "submit",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// "secret" must be non-empty.
{R"({
"command": "submit",
"secret": "",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// "tx_json" must be present.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"rx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// "TransactionType" must be present.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// The "TransactionType" must be one of the pre-established transaction types.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "tt"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// The "TransactionType", however, may be represented with an integer.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": 0
}
})", TxnTestData::allGood},
// "Account" must be present.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// "Account" must be well formed.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Account": "NotAnAccount",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// The "offline" tag may be added to the transaction.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"offline": 0,
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// If "offline" is true then a "Sequence" field must be supplied.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"offline": 1,
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// Valid transaction if "offline" is true.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"offline": 1,
"tx_json": {
"Sequence": 0,
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// A "Flags' field may be specified.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Flags": 0,
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
// The "Flags" field must be numeric.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"tx_json": {
"Flags": "NotGoodFlags",
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::signFail | TxnTestData::submitFail},
// It's okay to add a "debug_signing" field.
{R"({
"command": "submit",
"secret": "masterpassphrase",
"debug_signing": 0,
"tx_json": {
"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Amount": "1000000000",
"Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
"TransactionType": "Payment"
}
})", TxnTestData::allGood},
};
class JSONRPC_test : public beast::unit_test::suite
{
public:
void testAutoFillFees ()
{
RippleAddress rootSeedMaster
= RippleAddress::createSeedGeneric ("masterpassphrase");
RippleAddress rootGeneratorMaster
= RippleAddress::createGeneratorPublic (rootSeedMaster);
RippleAddress rootAddress
= RippleAddress::createAccountPublic (rootGeneratorMaster, 0);
std::uint64_t startAmount (100000);
Ledger::pointer ledger (std::make_shared <Ledger> (
rootAddress, startAmount));
using namespace RPCDetail;
LedgerFacade facade (LedgerFacade::noNetOPs, ledger);
{
Json::Value req;
Json::Value result;
Json::Reader ().parse (
R"({ "fee_mult_max" : 1, "tx_json" : { } } )"
, req);
autofill_fee (req, facade, result, true);
expect (! contains_error (result));
}
{
Json::Value req;
Json::Value result;
Json::Reader ().parse (
R"({ "fee_mult_max" : 0, "tx_json" : { } } )"
, req);
autofill_fee (req, facade, result, true);
expect (contains_error (result));
}
}
void testTransactionRPC ()
{
// This loop is forward-looking for when there are separate
// transactionSign () and transcationSubmit () functions. For now
// they just have a bool (false = sign, true = submit) and a flag
// to help classify failure types.
using TestStuff = std::pair <bool, unsigned int>;
static TestStuff const testFuncs [] =
{
TestStuff {false, TxnTestData::signFail},
TestStuff {true, TxnTestData::submitFail},
};
for (auto testFunc : testFuncs)
{
// For each JSON test.
for (auto const& txnTest : txnTestArray)
{
Json::Value req;
Json::Reader ().parse (txnTest.json, req);
if (contains_error (req))
throw std::runtime_error (
"Internal JSONRPC_test error. Bad test JSON.");
static Role const testedRoles[] =
{Role::GUEST, Role::USER, Role::ADMIN, Role::FORBID};
for (Role testRole : testedRoles)
{
// Mock so we can run without a ledger.
RPCDetail::LedgerFacade fakeNetOPs (
RPCDetail::LedgerFacade::noNetOPs);
Json::Value result = transactionSign (
req,
testFunc.first,
true,
fakeNetOPs,
testRole);
expect (contains_error (result) ==
static_cast <bool> (txnTest.result & testFunc.second));
}
}
}
}
void run ()
{
testAutoFillFees ();
testTransactionRPC ();
}
};
BEAST_DEFINE_TESTSUITE(JSONRPC,ripple_app,ripple);
} // RPC
} // ripple