Isolate Application object in Env:

This change causes each instance of Env to construct its own
isolated Application object for testing. Also included is
part of a framework to create multiple Application objects
in the same unit test and connect them together.
This commit is contained in:
Vinnie Falco
2015-09-17 10:18:01 -07:00
parent 19903674af
commit 9315d98aa9
20 changed files with 630 additions and 368 deletions

View File

@@ -3760,6 +3760,16 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\test\jtx\utility.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\test\mao\impl\Net.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\test\mao\impl\Net_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\test\mao\Net.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\unity\app_ledger.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug.classic|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release.classic|x64'">True</ExcludedFromBuild>

View File

@@ -421,6 +421,12 @@
<Filter Include="ripple\test\jtx\impl">
<UniqueIdentifier>{27D70888-7145-691C-0E0A-E511EB3A80A2}</UniqueIdentifier>
</Filter>
<Filter Include="ripple\test\mao">
<UniqueIdentifier>{94B5035A-6D4A-E4FC-DB50-E7E804DC9F13}</UniqueIdentifier>
</Filter>
<Filter Include="ripple\test\mao\impl">
<UniqueIdentifier>{23DE6C05-81D2-7471-D9BB-3AA1D49DE429}</UniqueIdentifier>
</Filter>
<Filter Include="ripple\unity">
<UniqueIdentifier>{5DB3CD0B-B361-B301-9562-697CA8A52B68}</UniqueIdentifier>
</Filter>
@@ -4437,6 +4443,15 @@
<ClInclude Include="..\..\src\ripple\test\jtx\utility.h">
<Filter>ripple\test\jtx</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\test\mao\impl\Net.cpp">
<Filter>ripple\test\mao\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\test\mao\impl\Net_test.cpp">
<Filter>ripple\test\mao\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\test\mao\Net.h">
<Filter>ripple\test\mao</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\unity\app_ledger.cpp">
<Filter>ripple\unity</Filter>
</ClCompile>

View File

@@ -79,33 +79,6 @@ namespace ripple {
// 204/256 about 80%
static int const MAJORITY_FRACTION (204);
// This hack lets the s_instance variable remain set during
// the call to ~Application
class ApplicationImpBase : public Application
{
public:
ApplicationImpBase ()
{
assert (s_instance == nullptr);
s_instance = this;
}
~ApplicationImpBase ()
{
s_instance = nullptr;
}
static Application* s_instance;
static Application& getInstance ()
{
bassert (s_instance != nullptr);
return *s_instance;
}
};
Application* ApplicationImpBase::s_instance;
//------------------------------------------------------------------------------
namespace detail {
@@ -239,7 +212,7 @@ public:
// VFALCO TODO Move the function definitions into the class declaration
class ApplicationImp
: public ApplicationImpBase
: public Application
, public beast::RootStoppable
, public beast::DeadlineTimer::Listener
, public BasicApp
@@ -523,6 +496,25 @@ public:
//--------------------------------------------------------------------------
void setup() override;
void run() override;
bool isShutdown() override;
void signalStop() override;
//--------------------------------------------------------------------------
Logs&
logs() override
{
return *logs_;
}
Config const&
config() const override
{
return *config_;
}
CollectorManager& getCollectorManager () override
{
return *m_collectorManager;
@@ -555,18 +547,6 @@ public:
return *m_networkOPs;
}
Config const&
config() const override
{
return *config_;
}
Logs&
logs() override
{
return *logs_;
}
boost::asio::io_service& getIOService () override
{
return get_io_service();
@@ -702,12 +682,6 @@ public:
return *m_overlay;
}
// VFALCO TODO Move these to the .cpp
bool running () override
{
return mTxnDB != nullptr;
}
DatabaseCon& getTxnDB () override
{
assert (mTxnDB.get() != nullptr);
@@ -724,12 +698,6 @@ public:
return *mWalletDB;
}
bool isShutdown () override
{
// from Stoppable mixin
return isStopped();
}
bool serverOkay (std::string& reason) override;
beast::Journal journal (std::string const& name) override;
@@ -774,185 +742,6 @@ public:
}
}
// VFALCO TODO Break this function up into many small initialization segments.
// Or better yet refactor these initializations into RAII classes
// which are members of the Application object.
//
void setup () override
{
// VFALCO NOTE: 0 means use heuristics to determine the thread count.
m_jobQueue->setThreadCount (0, config_->RUN_STANDALONE);
// We want to intercept and wait for CTRL-C to terminate the process
m_signals.add (SIGINT);
m_signals.async_wait(std::bind(&ApplicationImp::signalled, this,
std::placeholders::_1, std::placeholders::_2));
assert (mTxnDB == nullptr);
auto debug_log = config_->getDebugLogFile ();
if (!debug_log.empty ())
{
// Let debug messages go to the file but only WARNING or higher to
// regular output (unless verbose)
if (!logs_->open(debug_log))
std::cerr << "Can't open log file " << debug_log << '\n';
if (logs_->severity() > beast::Journal::kDebug)
logs_->severity (beast::Journal::kDebug);
}
if (!config_->RUN_STANDALONE)
timeKeeper_->run(config_->SNTP_SERVERS);
if (!initSqliteDbs ())
{
m_journal.fatal << "Can not create database connections!";
exitWithCode(3);
}
getLedgerDB ().getSession ()
<< boost::str (boost::format ("PRAGMA cache_size=-%d;") %
(config_->getSize (siLgrDBCache) * 1024));
getTxnDB ().getSession ()
<< boost::str (boost::format ("PRAGMA cache_size=-%d;") %
(config_->getSize (siTxnDBCache) * 1024));
mTxnDB->setupCheckpointing (m_jobQueue.get(), logs());
mLedgerDB->setupCheckpointing (m_jobQueue.get(), logs());
if (!config_->RUN_STANDALONE)
updateTables ();
m_amendmentTable->addInitial (
config_->section (SECTION_AMENDMENTS));
initializePathfinding ();
m_ledgerMaster->setMinValidations (
config_->VALIDATION_QUORUM, config_->LOCK_QUORUM);
auto const startUp = config_->START_UP;
if (startUp == Config::FRESH)
{
m_journal.info << "Starting new Ledger";
startGenesisLedger ();
}
else if (startUp == Config::LOAD ||
startUp == Config::LOAD_FILE ||
startUp == Config::REPLAY)
{
m_journal.info << "Loading specified Ledger";
if (!loadOldLedger (config_->START_LEDGER,
startUp == Config::REPLAY,
startUp == Config::LOAD_FILE))
{
exitWithCode(-1);
}
}
else if (startUp == Config::NETWORK)
{
// This should probably become the default once we have a stable network.
if (!config_->RUN_STANDALONE)
m_networkOPs->needNetworkLedger ();
startGenesisLedger ();
}
else
{
startGenesisLedger ();
}
m_orderBookDB.setup (getLedgerMaster ().getCurrentLedger ());
// Begin validation and ip maintenance.
//
// - LocalCredentials maintains local information: including identity
// - and network connection persistence information.
//
// VFALCO NOTE this starts the UNL
m_localCredentials.start ();
//
// Set up UNL.
//
if (!config_->RUN_STANDALONE)
getUNL ().nodeBootstrap ();
mValidations->tune (config_->getSize (siValidationsSize), config_->getSize (siValidationsAge));
m_nodeStore->tune (config_->getSize (siNodeCacheSize), config_->getSize (siNodeCacheAge));
m_ledgerMaster->tune (config_->getSize (siLedgerSize), config_->getSize (siLedgerAge));
family().treecache().setTargetSize (config_->getSize (siTreeCacheSize));
family().treecache().setTargetAge (config_->getSize (siTreeCacheAge));
//----------------------------------------------------------------------
//
// Server
//
//----------------------------------------------------------------------
// VFALCO NOTE Unfortunately, in stand-alone mode some code still
// foolishly calls overlay(). When this is fixed we can
// move the instantiation inside a conditional:
//
// if (!config_.RUN_STANDALONE)
m_overlay = make_Overlay (*this, setup_Overlay(*config_), *m_jobQueue,
*serverHandler_, *m_resourceManager, *m_resolver, get_io_service(),
*config_);
add (*m_overlay); // add to PropertyStream
m_overlay->setupValidatorKeyManifests (*config_, getWalletDB ());
{
auto setup = setup_ServerHandler(*config_, std::cerr);
setup.makeContexts();
serverHandler_->setup (setup, m_journal);
}
// Create websocket servers.
for (auto const& port : serverHandler_->setup().ports)
{
if (! port.websockets())
continue;
auto server = websocket::makeServer (
{*this, port, *m_resourceManager, getOPs(), m_journal, *config_,
*m_collectorManager});
if (!server)
{
m_journal.fatal << "Could not create Websocket for [" <<
port.name << "]";
throw std::exception();
}
websocketServers_.emplace_back (std::move (server));
}
//----------------------------------------------------------------------
// Begin connecting to network.
if (!config_->RUN_STANDALONE)
{
// Should this message be here, conceptually? In theory this sort
// of message, if displayed, should be displayed from PeerFinder.
if (config_->PEER_PRIVATE && config_->IPS.empty ())
m_journal.warning << "No outbound peer connections will be made";
// VFALCO NOTE the state timer resets the deadlock detector.
//
m_networkOPs->setStateTimer ();
}
else
{
m_journal.warning << "Running in standalone mode";
m_networkOPs->setStandAlone ();
}
}
//--------------------------------------------------------------------------
//
// Stoppable
@@ -1018,37 +807,6 @@ public:
//------------------------------------------------------------------------------
void run () override
{
// VFALCO NOTE I put this here in the hopes that when unit tests run (which
// tragically require an Application object to exist or else they
// crash), the run() function will not get called and we will
// avoid doing silly things like contacting the SNTP server, or
// running the various logic threads like Validators, PeerFinder, etc.
prepare ();
start ();
{
if (!config_->RUN_STANDALONE)
{
// VFALCO NOTE This seems unnecessary. If we properly refactor the load
// manager then the deadlock detector can just always be "armed"
//
getLoadManager ().activateDeadlockDetector ();
}
}
m_stop.wait ();
// Stop the server. When this returns, all
// Stoppable objects should be stopped.
m_journal.info << "Received shutdown request";
stop (m_journal);
m_journal.info << "Done.";
StopSustain();
}
void exitWithCode(int code)
{
StopSustain();
@@ -1057,13 +815,6 @@ public:
std::exit(code);
}
void signalStop () override
{
// Unblock the main thread (which is sitting in run()).
//
m_stop.signal();
}
void onDeadlineTimer (beast::DeadlineTimer& timer) override
{
if (timer == m_entropyTimer)
@@ -1124,7 +875,236 @@ private:
//------------------------------------------------------------------------------
void ApplicationImp::startGenesisLedger ()
// VFALCO TODO Break this function up into many small initialization segments.
// Or better yet refactor these initializations into RAII classes
// which are members of the Application object.
//
void ApplicationImp::setup()
{
// VFALCO NOTE: 0 means use heuristics to determine the thread count.
m_jobQueue->setThreadCount (0, config_->RUN_STANDALONE);
// We want to intercept and wait for CTRL-C to terminate the process
m_signals.add (SIGINT);
m_signals.async_wait(std::bind(&ApplicationImp::signalled, this,
std::placeholders::_1, std::placeholders::_2));
assert (mTxnDB == nullptr);
auto debug_log = config_->getDebugLogFile ();
if (!debug_log.empty ())
{
// Let debug messages go to the file but only WARNING or higher to
// regular output (unless verbose)
if (!logs_->open(debug_log))
std::cerr << "Can't open log file " << debug_log << '\n';
if (logs_->severity() > beast::Journal::kDebug)
logs_->severity (beast::Journal::kDebug);
}
if (!config_->RUN_STANDALONE)
timeKeeper_->run(config_->SNTP_SERVERS);
if (!initSqliteDbs ())
{
m_journal.fatal << "Can not create database connections!";
exitWithCode(3);
}
getLedgerDB ().getSession ()
<< boost::str (boost::format ("PRAGMA cache_size=-%d;") %
(config_->getSize (siLgrDBCache) * 1024));
getTxnDB ().getSession ()
<< boost::str (boost::format ("PRAGMA cache_size=-%d;") %
(config_->getSize (siTxnDBCache) * 1024));
mTxnDB->setupCheckpointing (m_jobQueue.get(), logs());
mLedgerDB->setupCheckpointing (m_jobQueue.get(), logs());
if (!config_->RUN_STANDALONE)
updateTables ();
m_amendmentTable->addInitial (
config_->section (SECTION_AMENDMENTS));
initializePathfinding ();
m_ledgerMaster->setMinValidations (
config_->VALIDATION_QUORUM, config_->LOCK_QUORUM);
auto const startUp = config_->START_UP;
if (startUp == Config::FRESH)
{
m_journal.info << "Starting new Ledger";
startGenesisLedger ();
}
else if (startUp == Config::LOAD ||
startUp == Config::LOAD_FILE ||
startUp == Config::REPLAY)
{
m_journal.info << "Loading specified Ledger";
if (!loadOldLedger (config_->START_LEDGER,
startUp == Config::REPLAY,
startUp == Config::LOAD_FILE))
{
exitWithCode(-1);
}
}
else if (startUp == Config::NETWORK)
{
// This should probably become the default once we have a stable network.
if (!config_->RUN_STANDALONE)
m_networkOPs->needNetworkLedger ();
startGenesisLedger ();
}
else
{
startGenesisLedger ();
}
m_orderBookDB.setup (getLedgerMaster ().getCurrentLedger ());
// Begin validation and ip maintenance.
//
// - LocalCredentials maintains local information: including identity
// - and network connection persistence information.
//
// VFALCO NOTE this starts the UNL
m_localCredentials.start ();
//
// Set up UNL.
//
if (!config_->RUN_STANDALONE)
getUNL ().nodeBootstrap ();
mValidations->tune (config_->getSize (siValidationsSize), config_->getSize (siValidationsAge));
m_nodeStore->tune (config_->getSize (siNodeCacheSize), config_->getSize (siNodeCacheAge));
m_ledgerMaster->tune (config_->getSize (siLedgerSize), config_->getSize (siLedgerAge));
family().treecache().setTargetSize (config_->getSize (siTreeCacheSize));
family().treecache().setTargetAge (config_->getSize (siTreeCacheAge));
//----------------------------------------------------------------------
//
// Server
//
//----------------------------------------------------------------------
// VFALCO NOTE Unfortunately, in stand-alone mode some code still
// foolishly calls overlay(). When this is fixed we can
// move the instantiation inside a conditional:
//
// if (!config_.RUN_STANDALONE)
m_overlay = make_Overlay (*this, setup_Overlay(*config_), *m_jobQueue,
*serverHandler_, *m_resourceManager, *m_resolver, get_io_service(),
*config_);
add (*m_overlay); // add to PropertyStream
m_overlay->setupValidatorKeyManifests (*config_, getWalletDB ());
{
auto setup = setup_ServerHandler(*config_, std::cerr);
setup.makeContexts();
serverHandler_->setup (setup, m_journal);
}
// Create websocket servers.
for (auto const& port : serverHandler_->setup().ports)
{
if (! port.websockets())
continue;
auto server = websocket::makeServer (
{*this, port, *m_resourceManager, getOPs(), m_journal, *config_,
*m_collectorManager});
if (!server)
{
m_journal.fatal << "Could not create Websocket for [" <<
port.name << "]";
throw std::exception();
}
websocketServers_.emplace_back (std::move (server));
}
//----------------------------------------------------------------------
// Begin connecting to network.
if (!config_->RUN_STANDALONE)
{
// Should this message be here, conceptually? In theory this sort
// of message, if displayed, should be displayed from PeerFinder.
if (config_->PEER_PRIVATE && config_->IPS.empty ())
m_journal.warning << "No outbound peer connections will be made";
// VFALCO NOTE the state timer resets the deadlock detector.
//
m_networkOPs->setStateTimer ();
}
else
{
m_journal.warning << "Running in standalone mode";
m_networkOPs->setStandAlone ();
}
}
void
ApplicationImp::run()
{
// VFALCO NOTE I put this here in the hopes that when unit tests run (which
// tragically require an Application object to exist or else they
// crash), the run() function will not get called and we will
// avoid doing silly things like contacting the SNTP server, or
// running the various logic threads like Validators, PeerFinder, etc.
prepare ();
start ();
{
if (!config_->RUN_STANDALONE)
{
// VFALCO NOTE This seems unnecessary. If we properly refactor the load
// manager then the deadlock detector can just always be "armed"
//
getLoadManager ().activateDeadlockDetector ();
}
}
m_stop.wait ();
// Stop the server. When this returns, all
// Stoppable objects should be stopped.
m_journal.info << "Received shutdown request";
stop (m_journal);
m_journal.info << "Done.";
StopSustain();
}
void
ApplicationImp::signalStop()
{
// Unblock the main thread (which is sitting in run()).
//
m_stop.signal();
}
bool
ApplicationImp::isShutdown()
{
// from Stoppable mixin
return isStopped();
}
//------------------------------------------------------------------------------
void
ApplicationImp::startGenesisLedger()
{
std::shared_ptr<Ledger> const genesis =
std::make_shared<Ledger>(
@@ -1485,14 +1465,17 @@ bool ApplicationImp::serverOkay (std::string& reason)
return true;
}
beast::Journal ApplicationImp::journal (std::string const& name)
beast::Journal
ApplicationImp::journal (std::string const& name)
{
return logs_->journal (name);
}
//VFALCO TODO clean this up since it is just a file holding a single member function definition
static std::vector<std::string> getSchema (DatabaseCon& dbc, std::string const& dbName)
static
std::vector<std::string>
getSchema (DatabaseCon& dbc, std::string const& dbName)
{
std::vector<std::string> schema;
schema.reserve(32);
@@ -1653,6 +1636,8 @@ Application::Application ()
{
}
//------------------------------------------------------------------------------
std::unique_ptr<Application>
make_Application (
std::unique_ptr<Config const> config,
@@ -1662,9 +1647,14 @@ make_Application (
std::move(config), std::move(logs));
}
Application& getApp ()
void
setupConfigForUnitTests (Config& config)
{
return ApplicationImpBase::getInstance ();
config.overwrite (ConfigSection::nodeDatabase (), "type", "memory");
config.overwrite (ConfigSection::nodeDatabase (), "path", "main");
config.deprecatedClearSection (ConfigSection::importNodeDatabase ());
config.legacy("database_path", "DummyForUnitTests");
}
}

View File

@@ -90,8 +90,18 @@ public:
Application ();
virtual ~Application () = default;
virtual Config const& config() const = 0;
virtual void setup() = 0;
virtual void run() = 0;
virtual bool isShutdown () = 0;
virtual void signalStop () = 0;
//
// ---
//
virtual Logs& logs() = 0;
virtual Config const& config() const = 0;
virtual boost::asio::io_service& getIOService () = 0;
virtual CollectorManager& getCollectorManager () = 0;
virtual Family& family() = 0;
@@ -138,12 +148,6 @@ public:
// NOTE This will be replaced by class Validators
//
virtual DatabaseCon& getWalletDB () = 0;
virtual bool isShutdown () = 0;
virtual bool running () = 0;
virtual void setup () = 0;
virtual void run () = 0;
virtual void signalStop () = 0;
};
std::unique_ptr <Application>
@@ -151,8 +155,9 @@ make_Application(
std::unique_ptr<Config const> config,
std::unique_ptr<Logs> logs);
// DEPRECATED
extern Application& getApp ();
extern
void
setupConfigForUnitTests (Config& config);
}

View File

@@ -162,17 +162,6 @@ void printHelp (const po::options_description& desc)
//------------------------------------------------------------------------------
static
void
setupConfigForUnitTests (Config& config)
{
config.overwrite (ConfigSection::nodeDatabase (), "type", "memory");
config.overwrite (ConfigSection::nodeDatabase (), "path", "main");
config.deprecatedClearSection (ConfigSection::importNodeDatabase ());
config.legacy("database_path", "DummyForUnitTests");
}
static int runShutdownTests (std::unique_ptr<Config> config)
{
// Shutdown tests can not be part of the normal unit tests in 'runUnitTests'
@@ -204,18 +193,10 @@ static int runShutdownTests (std::unique_ptr<Config> config)
return EXIT_SUCCESS;
}
static int runUnitTests (
std::unique_ptr<Config> config,
static int runUnitTests(
std::string const& pattern,
std::string const& argument)
{
// Config needs to be set up before creating Application
setupConfigForUnitTests (*config);
auto app = make_Application (
std::move(config),
std::make_unique<Logs>());
using namespace beast::unit_test;
beast::debug_ostream stream;
reporter r (stream);
@@ -363,9 +344,7 @@ int run (int argc, char** argv)
argument = vm["unittest-arg"].as<std::string>();
return runUnitTests(
std::make_unique<Config> (),
vm["unittest"].as<std::string>(),
argument);
vm["unittest"].as<std::string>(), argument);
}
auto config = std::make_unique<Config>();

View File

@@ -142,7 +142,7 @@ public:
env.require (owners (alice, 4));
// This should work.
auto const baseFee = env.config.FEE_DEFAULT;
auto const baseFee = env.app().config().FEE_DEFAULT;
std::uint32_t aliceSeq = env.seq (alice);
env(noop(alice), msig(bogie, demon), fee(3 * baseFee));
env.close();
@@ -232,7 +232,7 @@ public:
// alice multisigns a transaction. Should succeed.
std::uint32_t aliceSeq = env.seq (alice);
auto const baseFee = env.config.FEE_DEFAULT;
auto const baseFee = env.app().config().FEE_DEFAULT;
env(noop(alice), msig(bogie), fee(2 * baseFee));
env.close();
expect (env.seq(alice) == aliceSeq + 1);
@@ -265,7 +265,7 @@ public:
env.require (owners (alice, 10));
// This should work.
auto const baseFee = env.config.FEE_DEFAULT;
auto const baseFee = env.app().config().FEE_DEFAULT;
std::uint32_t aliceSeq = env.seq (alice);
env(noop(alice), msig(bogie), fee(2 * baseFee));
env.close();
@@ -348,7 +348,7 @@ public:
env.require (owners (alice, 4));
// Attempt a multisigned transaction that meets the quorum.
auto const baseFee = env.config.FEE_DEFAULT;
auto const baseFee = env.app().config().FEE_DEFAULT;
aliceSeq = env.seq (alice);
env(noop(alice), msig(cheri), fee(2 * baseFee));
env.close();
@@ -401,7 +401,7 @@ public:
env.close();
// Attempt a multisigned transaction that meets the quorum.
auto const baseFee = env.config.FEE_DEFAULT;
auto const baseFee = env.app().config().FEE_DEFAULT;
std::uint32_t aliceSeq = env.seq (alice);
env(noop(alice), msig(msig::Reg{cheri, cher}), fee(2 * baseFee));
env.close();
@@ -468,7 +468,7 @@ public:
env.require (owners (alice, 6));
// Each type of signer should succeed individually.
auto const baseFee = env.config.FEE_DEFAULT;
auto const baseFee = env.app().config().FEE_DEFAULT;
std::uint32_t aliceSeq = env.seq (alice);
env(noop(alice), msig(becky), fee(2 * baseFee));
env.close();
@@ -592,7 +592,7 @@ public:
env(regkey (alice, disabled), sig(alie));
// L0; A lone signer list cannot be removed.
auto const baseFee = env.config.FEE_DEFAULT;
auto const baseFee = env.app().config().FEE_DEFAULT;
env(signers(alice, jtx::none), msig(bogie),
fee(2 * baseFee), ter(tecNO_ALTERNATIVE_KEY));
@@ -645,7 +645,7 @@ public:
env.require (owners (alice, 4));
// Multisign a ttPAYMENT.
auto const baseFee = env.config.FEE_DEFAULT;
auto const baseFee = env.app().config().FEE_DEFAULT;
std::uint32_t aliceSeq = env.seq (alice);
env(pay(alice, env.master, XRP(1)),
msig(becky, bogie), fee(3 * baseFee));

View File

@@ -49,7 +49,7 @@ struct Regression_test : public beast::unit_test::suite
// be reproduced against an open ledger. Make a local
// closed ledger and work with it directly.
auto closed = std::make_shared<Ledger>(
create_genesis, env.config, env.app().family());
create_genesis, env.app().config(), env.app().family());
auto expectedDrops = SYSTEM_CURRENCY_START;
expect(closed->info().drops == expectedDrops);
@@ -67,7 +67,7 @@ struct Regression_test : public beast::unit_test::suite
auto const result = ripple::apply(env.app(),
accum, *jt.stx, tapENABLE_TESTING,
directSigVerify, env.config,
directSigVerify, env.app().config(),
env.journal);
expect(result.first == tesSUCCESS);
expect(result.second);
@@ -95,7 +95,7 @@ struct Regression_test : public beast::unit_test::suite
auto const result = ripple::apply(env.app(),
accum, *jt.stx, tapENABLE_TESTING,
directSigVerify, env.config,
directSigVerify, env.app().config(),
env.journal);
expect(result.first == tecINSUFF_FEE);
expect(result.second);

View File

@@ -175,6 +175,9 @@ public:
If the section does not exist, an empty section is returned.
*/
/** @{ */
Section&
section (std::string const& name);
Section const&
section (std::string const& name) const;
@@ -183,6 +186,12 @@ public:
{
return section(name);
}
Section&
operator[] (std::string const& name)
{
return section(name);
}
/** @} */
/** Overwrite a key/value pair with a command line argument

View File

@@ -96,6 +96,12 @@ BasicConfig::exists (std::string const& name) const
return map_.find(name) != map_.end();
}
Section&
BasicConfig::section (std::string const& name)
{
return map_[name];
}
Section const&
BasicConfig::section (std::string const& name) const
{

View File

@@ -162,9 +162,7 @@ public:
beast::File getModuleDatabasePath () const;
bool doImport = false;
bool QUIET = false;
bool ELB_SUPPORT = false;
std::string VALIDATORS_SITE; // Where to find validators.txt on the Internet.

View File

@@ -47,13 +47,8 @@ public:
Account (Account const&) = default;
Account& operator= (Account const&) = default;
#ifdef _MSC_VER
Account (Account&&);
Account& operator= (Account&&);
#else
Account (Account&&) = default;
Account& operator= (Account&&) = default;
#endif
/** Create an account from a key pair. */
Account (std::string name,

View File

@@ -29,6 +29,7 @@
#include <ripple/app/ledger/Ledger.h>
#include <ripple/app/ledger/OpenLedger.h>
#include <ripple/basics/chrono.h>
#include <ripple/basics/Log.h>
#include <ripple/core/Config.h>
#include <ripple/json/json_value.h>
#include <ripple/json/to_string.h>
@@ -126,13 +127,21 @@ public:
beast::Journal const journal;
/** Configuration used. */
Config const config;
/** The master account. */
Account const master;
private:
struct AppBundle
{
Application* app;
std::unique_ptr<Logs> logs;
std::unique_ptr<Application> owned;
AppBundle (std::unique_ptr<Config const> config);
AppBundle (Application* app_);
};
AppBundle bundle_;
std::shared_ptr<Ledger const> closed_;
CachedSLEs cachedSLEs_;
LogSquelcher logSquelcher_;
@@ -150,7 +159,7 @@ public:
Application&
app()
{
return getApp();
return *bundle_.app;
}
/** Returns the open ledger.

View File

@@ -26,28 +26,6 @@ namespace ripple {
namespace test {
namespace jtx {
#ifdef _MSC_VER
Account::Account (Account&& other)
: name_(std::move(other.name_))
, pk_(std::move(other.pk_))
, sk_(std::move(other.sk_))
, id_(std::move(other.id_))
, human_(std::move(other.human_))
{
}
Account&
Account::operator= (Account&& rhs)
{
name_ = std::move(rhs.name_);
pk_ = std::move(rhs.pk_);
sk_ = std::move(rhs.sk_);
id_ = std::move(rhs.id_);
human_ = std::move(rhs.human_);
return *this;
}
#endif
Account::Account(std::string name,
std::pair<PublicKey, SecretKey> const& keys)
: name_(std::move(name))
@@ -58,16 +36,15 @@ Account::Account(std::string name,
{
}
Account::Account (std::string name,
KeyType type)
#ifndef _MSC_VER
: Account(name,
#else
// Fails on Clang and possibly gcc
: Account(std::move(name),
#endif
generateKeyPair(type, generateSeed(name)))
Account::Account (std::string name, KeyType type)
: name_(std::move(name))
{
auto const keys = generateKeyPair(
type, generateSeed(name_));
pk_ = keys.first;
sk_ = keys.second;
id_ = calcAccountID(pk_);
human_ = toBase58(id_);
}
IOU

View File

@@ -50,17 +50,41 @@ namespace test {
namespace jtx {
Env::AppBundle::AppBundle(Application* app_)
: app (app_)
{
}
Env::AppBundle::AppBundle(std::unique_ptr<Config const> config)
{
auto logs = std::make_unique<Logs>();
owned = make_Application(
std::move(config), std::move(logs));
app = owned.get();
}
//------------------------------------------------------------------------------
static
std::unique_ptr<Config const>
makeConfig()
{
auto p = std::make_unique<Config>();
setupConfigForUnitTests(*p);
return std::unique_ptr<Config const>(p.release());
}
// VFALCO Could wrap the log in a Journal here
Env::Env (beast::unit_test::suite& test_)
: test(test_)
, config()
, master("master", generateKeyPair(
: test (test_)
, master ("master", generateKeyPair(
KeyType::secp256k1,
generateSeed("masterpassphrase")))
, bundle_ (makeConfig())
, closed_ (std::make_shared<Ledger>(
create_genesis, config, app().family()))
create_genesis, app().config(), app().family()))
, cachedSLEs_ (std::chrono::seconds(5), stopwatch_)
, openLedger (closed_, config, cachedSLEs_, journal)
, openLedger (closed_, app().config(), cachedSLEs_, journal)
{
memoize(master);
initializePathfinding();
@@ -98,7 +122,7 @@ Env::close(NetClock::time_point const& closeTime)
OpenView accum(&*next);
OpenLedger::apply(app(), accum, *closed_,
txs, retries, applyFlags(), *router,
config, journal);
app().config(), journal);
accum.apply(*next);
}
// To ensure that the close time is exact and not rounded, we don't
@@ -254,7 +278,7 @@ Env::submit (JTx const& jt)
{
std::tie(ter_, didApply) = ripple::apply(
app(), view, *stx, applyFlags(),
directSigVerify, config,
directSigVerify, app().config(),
beast::Journal{});
return didApply;
});

View File

@@ -360,7 +360,7 @@ public:
{ { "bob", 1 }, { "carol", 2 } }));
env(noop("alice"));
auto const baseFee = env.config.FEE_DEFAULT;
auto const baseFee = env.app().config().FEE_DEFAULT;
env(noop("alice"), msig("bob"), fee(2 * baseFee));
env(noop("alice"), msig("carol"), fee(2 * baseFee));
env(noop("alice"), msig("bob", "carol"), fee(3 * baseFee));

27
src/ripple/test/mao.h Normal file
View File

@@ -0,0 +1,27 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TEST_MAO_H_INCLUDED
#define RIPPLE_TEST_MAO_H_INCLUDED
// Convenience header that includes everything
#include <ripple/test/mao/Net.h>
#endif

39
src/ripple/test/mao/Net.h Normal file
View File

@@ -0,0 +1,39 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_TEST_MAO_H_INCLUDED
#define RIPPLE_TEST_MAO_H_INCLUDED
#include <ripple/app/main/Application.h>
namespace ripple {
namespace test {
namespace mao {
class Net
{
private:
public:
};
} // mao
} // test
} // ripple
#endif

View File

@@ -0,0 +1,31 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/test/mao/Net.h>
namespace ripple {
namespace test {
namespace mao {
} // mao
} // test
} // ripple

View File

@@ -0,0 +1,145 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/basics/Log.h>
#include <ripple/test/mao/Net.h>
#include <ripple/net/HTTPClient.h>
#include <ripple/net/RPCCall.h>
#include <beast/unit_test/suite.h>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
namespace ripple {
namespace test {
namespace mao {
struct TestApp
{
TestApp()
{
auto config = std::make_unique<Config>();
setupConfigForUnitTests(*config);
config->RUN_STANDALONE = true;
(*config)["server"].append("port_peer");
(*config)["port_peer"].set("ip", "127.0.0.1");
(*config)["port_peer"].set("port", "8080");
(*config)["port_peer"].set("protocol", "peer");
(*config)["server"].append("port_admin");
(*config)["port_admin"].set("ip", "127.0.0.1");
(*config)["port_admin"].set("port", "8081");
(*config)["port_admin"].set("protocol", "http");
(*config)["port_admin"].set("admin", "127.0.0.1");
// Hack so we dont have to call Config::setup
HTTPClient::initializeSSLContext(*config);
auto logs = std::make_unique<Logs>();
instance = make_Application(
std::move(config), std::move(logs));
instance->setup();
thread_ = std::thread(
[&]() { instance->run(); });
}
~TestApp()
{
if (thread_.joinable())
{
instance->signalStop();
thread_.join();
}
}
void
join()
{
thread_.join();
}
Application*
operator->()
{
return instance.get();
}
template <class T, class... Args>
void
rpc (T const& t, Args const&... args)
{
std::vector<std::string> v;
collect(v, t, args...);
RPCCall::fromCommandLine(
instance->config(), v,
instance->logs());
}
private:
inline
void
collect (std::vector<std::string>& v)
{
}
template <class T, class... Args>
void
collect (std::vector<std::string>& v,
T const& t, Args const&... args)
{
v.emplace_back(t);
collect(v, args...);
}
std::unique_ptr<Application> instance;
std::thread thread_;
std::mutex mutex_;
};
class Net_test : public beast::unit_test::suite
{
public:
void
testStartStop()
{
TestApp app;
pass();
}
void
testRPC()
{
TestApp app;
app.rpc("stop");
app.join();
pass();
}
void
run() override
{
testStartStop();
testRPC();
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(Net,mao,ripple)
} // mao
} // test
} // ripple

View File

@@ -24,6 +24,7 @@
#include <ripple/test/jtx/impl/balance.cpp>
#include <ripple/test/jtx/impl/delivermin.cpp>
#include <ripple/test/jtx/impl/Env.cpp>
#include <ripple/test/jtx/impl/Env_test.cpp>
#include <ripple/test/jtx/impl/fee.cpp>
#include <ripple/test/jtx/impl/flags.cpp>
#include <ripple/test/jtx/impl/jtx_json.cpp>
@@ -44,4 +45,6 @@
#include <ripple/test/jtx/impl/txflags.cpp>
#include <ripple/test/jtx/impl/utility.cpp>
#include <ripple/test/jtx/impl/Env_test.cpp>
#include <ripple/test/mao/impl/Net.cpp>
#include <ripple/test/mao/impl/Net_test.cpp>