Extend View.cpp unit test coverage.

This commit is contained in:
Scott Schurr
2016-02-11 14:32:42 -08:00
committed by Vinnie Falco
parent 2c3128d9ba
commit 0703441ee7
2 changed files with 399 additions and 2 deletions

View File

@@ -421,8 +421,8 @@ def get_libs(lib, static):
else:
static_libs.append(l)
return (static_libs, dynamic_libs)
except:
raise Exception('pkg-config failed for ' + lib)
except Exception as e:
raise Exception('pkg-config failed for ' + lib + '; Exception: ' + str(e))
def add_sanitizer (toolchain, env):
san = GetOption('sanitize')

View File

@@ -24,6 +24,7 @@
#include <ripple/ledger/OpenView.h>
#include <ripple/ledger/PaymentSandbox.h>
#include <ripple/ledger/Sandbox.h>
#include <ripple/protocol/Feature.h>
#include <type_traits>
namespace ripple {
@@ -523,6 +524,227 @@ class View_test
}
}
void
testFlags()
{
using namespace jtx;
Env env(*this);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const gw = Account("gateway");
auto const USD = gw["USD"];
auto const EUR = gw["EUR"];
env.fund(XRP(10000), alice, bob, carol, gw);
env.trust(USD(100), alice, bob, carol);
{
// Global freezing.
env(pay(gw, alice, USD(50)));
env(offer(alice, XRP(5), USD(5)));
// Now freeze gw.
env(fset (gw, asfGlobalFreeze));
env.close();
env(offer(alice, XRP(4), USD(5)), ter(tecFROZEN));
env.close();
// Alice's USD balance should be zero if frozen.
expect(USD(0) == accountHolds (*env.closed(),
alice, USD.currency, gw, fhZERO_IF_FROZEN, env.journal));
// Thaw gw and try again.
env(fclear (gw, asfGlobalFreeze));
env.close();
env(offer("alice", XRP(4), USD(5)));
}
{
// Local freezing.
env(pay(gw, bob, USD(50)));
env.close();
// Now gw freezes bob's USD trust line.
env(trust(gw, USD(100), bob, tfSetFreeze));
env.close();
// Bob's balance should be zero if frozen.
expect(USD(0) == accountHolds (*env.closed(),
bob, USD.currency, gw, fhZERO_IF_FROZEN, env.journal));
// gw thaws bob's trust line. bob gets his money back.
env(trust(gw, USD(100), bob, tfClearFreeze));
env.close();
expect(USD(50) == accountHolds (*env.closed(),
bob, USD.currency, gw, fhZERO_IF_FROZEN, env.journal));
}
{
// accountHolds().
env(pay(gw, carol, USD(50)));
env.close();
// carol has no EUR.
expect(EUR(0) == accountHolds (*env.closed(),
carol, EUR.currency, gw, fhZERO_IF_FROZEN, env.journal));
// But carol does have USD.
expect(USD(50) == accountHolds (*env.closed(),
carol, USD.currency, gw, fhZERO_IF_FROZEN, env.journal));
// carol's XRP balance should be her holdings minus her reserve.
auto const carolsXRP = accountHolds (*env.closed(), carol,
xrpCurrency(), xrpAccount(), fhZERO_IF_FROZEN, env.journal);
// carol's XRP balance: 10000
// base reserve: -200
// 1 trust line times its reserve: 1 * -50
// -------
// carol's available balance: 9750
expect(carolsXRP == XRP(9750));
// carol should be able to spend *more* than her XRP balance on
// a fee by eating into her reserve.
env(noop(carol), fee(carolsXRP + XRP(10)));
env.close();
// carol's XRP balance should now show as zero.
expect (XRP(0) == accountHolds (*env.closed(),
carol, xrpCurrency(), gw, fhZERO_IF_FROZEN, env.journal));
}
{
// accountFunds().
// Gateways have whatever funds they claim to have.
auto const gwUSD = accountFunds(
*env.closed(), gw, USD(314159), fhZERO_IF_FROZEN, env.journal);
expect (gwUSD == USD(314159));
// carol has funds from the gateway.
auto carolsUSD = accountFunds(
*env.closed(), carol, USD(0), fhZERO_IF_FROZEN, env.journal);
expect (carolsUSD == USD(50));
// If carol's funds are frozen she has no funds...
env(fset (gw, asfGlobalFreeze));
env.close();
carolsUSD = accountFunds(
*env.closed(), carol, USD(0), fhZERO_IF_FROZEN, env.journal);
expect (carolsUSD == USD(0));
// ... unless the query ignores the FROZEN state.
carolsUSD = accountFunds(
*env.closed(), carol, USD(0), fhIGNORE_FREEZE, env.journal);
expect (carolsUSD == USD(50));
// Just to be tidy, thaw gw.
env(fclear (gw, asfGlobalFreeze));
env.close();
}
}
void
testTransferRate()
{
using namespace jtx;
Env env(*this);
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const gw1 = Account("gw1");
env.fund(XRP(10000), alice, bob, gw1);
env.close();
auto rdView = env.closed();
// Test with no rate set on gw1.
expect (rippleTransferRate (*rdView, alice, bob, gw1) == 1000000000);
expect (rippleTransferRate (*rdView, gw1, alice, gw1) == 1000000000);
expect (rippleTransferRate (*rdView, alice, gw1, gw1) == 1000000000);
env(rate(gw1, 1.02));
env.close();
rdView = env.closed();
// Test with a non-unity rate set on gw1.
expect (rippleTransferRate (*rdView, alice, bob, gw1) == 1020000000);
expect (rippleTransferRate (*rdView, gw1, alice, gw1) == 1000000000);
expect (rippleTransferRate (*rdView, alice, gw1, gw1) == 1000000000);
}
void
testAreCompatible()
{
// This test requires incompatible ledgers. The good news we can
// construct and manage two different Env instances at the same
// time. So we can use two Env instances to produce mutually
// incompatible ledgers.
using namespace jtx;
auto const alice = Account("alice");
auto const bob = Account("bob");
// The first Env.
Env eA(*this);
eA.fund(XRP(10000), alice);
eA.close();
auto const rdViewA3 = eA.closed();
eA.fund(XRP(10000), bob);
eA.close();
auto const rdViewA4 = eA.closed();
// The two Env's can't share the same ports, so edit the config
// of the second Env.
auto getConfigWithNewPorts = [this] ()
{
auto cfg = std::make_unique<Config>();
setupConfigForUnitTests(*cfg);
for (auto const sectionName : {"port_peer", "port_http", "port_ws"})
{
Section& s = (*cfg)[sectionName];
auto const port = s.get<std::int32_t>("port");
this->expect (port);
if (port)
{
constexpr int portIncr = 5;
s.set ("port", std::to_string(*port + portIncr));
}
}
return cfg;
};
Env eB(*this, getConfigWithNewPorts());
// Make ledgers that are incompatible with the first ledgers. Note
// that bob is funded before alice.
eB.fund(XRP(10000), bob);
eB.close();
auto const rdViewB3 = eB.closed();
eB.fund(XRP(10000), alice);
eB.close();
auto const rdViewB4 = eB.closed();
// Check for compatibility.
auto jStream = eA.journal.stream(beast::Journal::kError);
expect ( areCompatible (*rdViewA3, *rdViewA4, jStream, ""));
expect ( areCompatible (*rdViewA4, *rdViewA3, jStream, ""));
expect ( areCompatible (*rdViewA4, *rdViewA4, jStream, ""));
expect (! areCompatible (*rdViewA3, *rdViewB4, jStream, ""));
expect (! areCompatible (*rdViewA4, *rdViewB3, jStream, ""));
expect (! areCompatible (*rdViewA4, *rdViewB4, jStream, ""));
// Try the other interface.
// Note that the different interface has different outcomes.
auto const& iA3 = rdViewA3->info();
auto const& iA4 = rdViewA4->info();
expect ( areCompatible (iA3.hash, iA3.seq, *rdViewA4, jStream, ""));
expect ( areCompatible (iA4.hash, iA4.seq, *rdViewA3, jStream, ""));
expect ( areCompatible (iA4.hash, iA4.seq, *rdViewA4, jStream, ""));
expect (! areCompatible (iA3.hash, iA3.seq, *rdViewB4, jStream, ""));
expect ( areCompatible (iA4.hash, iA4.seq, *rdViewB3, jStream, ""));
expect (! areCompatible (iA4.hash, iA4.seq, *rdViewB4, jStream, ""));
}
void
testRegressions()
{
@@ -573,11 +795,186 @@ class View_test
testStacked();
testContext();
testSles();
testFlags();
testTransferRate();
testAreCompatible();
testRegressions();
}
};
class GetAmendments_test
: public beast::unit_test::suite
{
static
std::unique_ptr<Config>
makeValidatorConfig()
{
auto p = std::make_unique<Config>();
setupConfigForUnitTests(*p);
// If the config has valid validation keys then we run as a validator.
auto const seed = parseBase58<Seed>("shUwVw52ofnCUX5m7kPTKzJdr4HEH");
if (!seed)
Throw<std::runtime_error> ("Invalid seed specified");
p->VALIDATION_PRIV = generateSecretKey (KeyType::secp256k1, *seed);
p->VALIDATION_PUB =
derivePublicKey (KeyType::secp256k1, p->VALIDATION_PRIV);
return p;
}
void
testGetAmendments()
{
using namespace jtx;
Env env(*this, makeValidatorConfig());
// Start out with no amendments.
auto majorities = getMajorityAmendments (*env.closed());
expect (majorities.empty());
// Now close ledgers until the amendments show up.
int i = 0;
for (i = 0; i <= 256; ++i)
{
env.close();
majorities = getMajorityAmendments (*env.closed());
if (! majorities.empty())
break;
}
// There should be at least 5 amendments. Don't do exact comparison
// to avoid maintenance as more amendments are added in the future.
expect (i == 254);
expect (majorities.size() >= 5);
// None of the amendments should be enabled yet.
auto enableds = getEnabledAmendments(*env.closed());
expect (enableds.empty());
// Now wait 2 weeks modulo 256 ledgers for the amendments to be
// enabled. Speed the process by closing ledgers every 80 minutes,
// which should get us to just past 2 weeks after 256 ledgers.
for (i = 0; i <= 256; ++i)
{
using namespace std::chrono_literals;
env.close(80min);
enableds = getEnabledAmendments(*env.closed());
if (! enableds.empty())
break;
}
expect (i == 255);
expect (enableds.size() >= 5);
}
void run() override
{
testGetAmendments();
}
};
class DirIsEmpty_test
: public beast::unit_test::suite
{
void
testDirIsEmpty()
{
using namespace jtx;
auto const alice = Account("alice");
auto const bogie = Account("bogie");
Env env(*this, features(featureMultiSign));
env.fund(XRP(10000), alice);
env.close();
// alice should have an empty directory.
expect (dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
// Give alice a signer list, then there will be stuff in the directory.
env(signers(alice, 1, { { bogie, 1} }));
env.close();
expect (! dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
env(signers(alice, jtx::none));
env.close();
expect (dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
// The next test is a bit awkward. It tests the case where alice
// uses 3 directory pages and then deletes all entries from the
// first 2 pages. dirIsEmpty() should still return false in this
// circumstance.
//
// Fill alice's directory with implicit trust lines (produced by
// taking offers) and then remove all but the last one.
auto const becky = Account ("becky");
auto const gw = Account ("gw");
env.fund(XRP(10000), becky, gw);
env.close();
// The DIR_NODE_MAX constant is hidden in View.cpp (Feb 2016). But,
// ideally, we'd verify we're doing a good test with the following:
// static_assert (64 >= (2 * DIR_NODE_MAX), "");
// Generate 64 currencies named AAA -> AAP and ADA -> ADP.
std::vector<IOU> currencies;
currencies.reserve(64);
for (char b = 'A'; b <= 'D'; ++b)
{
for (char c = 'A'; c <= 'P'; ++c)
{
currencies.push_back(gw[std::string("A") + b + c]);
IOU const& currency = currencies.back();
// Establish trust lines.
env(trust(becky, currency(50)));
env.close();
env(pay(gw, becky, currency(50)));
env.close();
env(offer(alice, currency(50), XRP(10)));
env(offer(becky, XRP(10), currency(50)));
env.close();
}
}
// Set up one more currency that alice will hold onto. We expect
// this one to go in the third directory page.
IOU const lastCurrency = gw["ZZZ"];
env(trust(becky, lastCurrency(50)));
env.close();
env(pay(gw, becky, lastCurrency(50)));
env.close();
env(offer(alice, lastCurrency(50), XRP(10)));
env(offer(becky, XRP(10), lastCurrency(50)));
env.close();
expect (! dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
// Now alice gives all the currencies except the last one back to becky.
for (auto currency : currencies)
{
env(pay(alice, becky, currency(50)));
env.close();
}
// This is the crux of the test.
expect (! dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
// Give the last currency to becky. Now alice's directory is empty.
env(pay(alice, becky, lastCurrency(50)));
env.close();
expect (dirIsEmpty (*env.closed(), keylet::ownerDir(alice)));
}
void run() override
{
testDirIsEmpty();
}
};
BEAST_DEFINE_TESTSUITE(View,ledger,ripple);
BEAST_DEFINE_TESTSUITE(GetAmendments,ledger,ripple);
BEAST_DEFINE_TESTSUITE(DirIsEmpty, ledger,ripple)
} // test
} // ripple