Don't include unit test sources in code coverage (RIPD-1132):

Most files containing unit test code are moved to
src/test. JTx and the test client code are not yet moved.
This commit is contained in:
Brad Chase
2016-09-02 15:25:05 -04:00
committed by Vinnie Falco
parent 8687f64429
commit 8f97889176
165 changed files with 2090 additions and 1693 deletions

View File

@@ -0,0 +1,794 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2015 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/contract.h>
#include <ripple/core/Config.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/basics/TestSuite.h>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <fstream>
#include <iostream>
namespace ripple {
namespace detail {
std::string configContents (std::string const& dbPath,
std::string const& validatorsFile)
{
static boost::format configContentsTemplate (R"rippleConfig(
[server]
port_rpc
port_peer
port_wss_admin
[port_rpc]
port = 5005
ip = 127.0.0.1
admin = 127.0.0.1
protocol = https
[port_peer]
port = 51235
ip = 0.0.0.0
protocol = peer
[port_wss_admin]
port = 6006
ip = 127.0.0.1
admin = 127.0.0.1
protocol = wss
#[port_ws_public]
#port = 5005
#ip = 127.0.0.1
#protocol = wss
#-------------------------------------------------------------------------------
[node_size]
medium
# This is primary persistent datastore for rippled. This includes transaction
# metadata, account states, and ledger headers. Helpful information can be
# found here: https://ripple.com/wiki/NodeBackEnd
# delete old ledgers while maintaining at least 2000. Do not require an
# external administrative command to initiate deletion.
[node_db]
type=memory
path=/Users/dummy/ripple/config/db/rocksdb
open_files=2000
filter_bits=12
cache_mb=256
file_size_mb=8
file_size_mult=2
%1%
%2%
# This needs to be an absolute directory reference, not a relative one.
# Modify this value as required.
[debug_logfile]
/Users/dummy/ripple/config/log/debug.log
[sntp_servers]
time.windows.com
time.apple.com
time.nist.gov
pool.ntp.org
# Where to find some other servers speaking the Ripple protocol.
#
[ips]
r.ripple.com 51235
# Turn down default logging to save disk space in the long run.
# Valid values here are trace, debug, info, warning, error, and fatal
[rpc_startup]
{ "command": "log_level", "severity": "warning" }
# Defaults to 1 ("yes") so that certificates will be validated. To allow the use
# of self-signed certificates for development or internal use, set to 0 ("no").
[ssl_verify]
0
[sqdb]
backend=sqlite
)rippleConfig");
std::string dbPathSection =
dbPath.empty () ? "" : "[database_path]\n" + dbPath;
std::string valFileSection =
validatorsFile.empty () ? "" : "[validators_file]\n" + validatorsFile;
return boost::str (
configContentsTemplate % dbPathSection % valFileSection);
}
/**
Write a config file and remove when done.
*/
class ConfigGuard
{
private:
bool rmSubDir_{false};
protected:
using path = boost::filesystem::path;
path subDir_;
beast::unit_test::suite& test_;
auto rmDir (path const& toRm)
{
if (is_directory (toRm) && is_empty (toRm))
remove (toRm);
else
test_.log << "Expected " << toRm.string ()
<< " to be an empty existing directory." << std::endl;
};
public:
ConfigGuard (beast::unit_test::suite& test, std::string subDir)
: subDir_ (std::move (subDir))
, test_ (test)
{
using namespace boost::filesystem;
{
if (!exists (subDir_))
{
create_directory (subDir_);
rmSubDir_ = true;
}
else if (is_directory (subDir_))
rmSubDir_ = false;
else
{
// Cannot run the test. Someone created a file where we want to
// put our directory
Throw<std::runtime_error> (
"Cannot create directory: " + subDir_.string ());
}
}
}
~ConfigGuard ()
{
try
{
using namespace boost::filesystem;
if (rmSubDir_)
rmDir (subDir_);
else
test_.log << "Skipping rm dir: "
<< subDir_.string () << std::endl;
}
catch (std::exception& e)
{
// if we throw here, just let it die.
test_.log << "Error in ~ConfigGuard: " << e.what () << std::endl;
};
}
};
/**
Write a rippled config file and remove when done.
*/
class RippledCfgGuard : ConfigGuard
{
private:
path configFile_;
path dataDir_;
bool rmDataDir_{false};
Config config_;
public:
RippledCfgGuard (beast::unit_test::suite& test,
std::string subDir, std::string const& dbPath,
std::string const& validatorsFile)
: ConfigGuard (test, std::move (subDir)), dataDir_ (dbPath)
{
if (dbPath.empty ())
dataDir_ = subDir_ / path (Config::databaseDirName);
configFile_ = subDir_ / path (Config::configFileName);
if (!exists (configFile_))
{
std::ofstream o (configFile_.string ());
o << configContents (dbPath, validatorsFile);
}
else
{
Throw<std::runtime_error> (
"Refusing to overwrite existing config file: " +
configFile_.string ());
}
rmDataDir_ = !exists (dataDir_);
config_.setup (configFile_.string (), /*bQuiet*/ false,
/* bSilent */ false, /* bStandalone */ false);
}
Config& config ()
{
return config_;
}
std::string configFile() const
{
return configFile_.string();
}
bool dataDirExists () const
{
return boost::filesystem::is_directory (dataDir_);
}
bool configFileExists () const
{
return boost::filesystem::exists (configFile_);
}
~RippledCfgGuard ()
{
try
{
using namespace boost::filesystem;
if (!boost::filesystem::exists (configFile_))
test_.log << "Expected " << configFile_.string ()
<< " to be an existing file." << std::endl;
else
remove (configFile_.string ());
if (rmDataDir_)
rmDir (dataDir_);
else
test_.log << "Skipping rm dir: "
<< dataDir_.string () << std::endl;
}
catch (std::exception& e)
{
// if we throw here, just let it die.
test_.log << "Error in ~RippledCfgGuard: "
<< e.what () << std::endl;
};
}
};
std::string valFileContents (boost::optional<int> const& quorum)
{
static boost::format configContentsTemplate (R"rippleConfig(
[validators]
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
[validator_keys]
nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5
nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
nHUPDdcdb2Y5DZAJne4c2iabFuAP3F34xZUgYQT2NH7qfkdapgnz
%1%
)rippleConfig");
std::string quorumSection =
quorum ? "[validation_quorum]\n" + to_string(*quorum) : "";
return boost::str (
configContentsTemplate % quorumSection);
}
/**
Write a validators.txt file and remove when done.
*/
class ValidatorsTxtGuard : ConfigGuard
{
private:
path validatorsFile_;
public:
ValidatorsTxtGuard (beast::unit_test::suite& test,
std::string subDir, std::string const& validatorsFileName,
boost::optional<int> const& quorum)
: ConfigGuard (test, std::move (subDir))
{
using namespace boost::filesystem;
validatorsFile_ = current_path () / subDir_ / path (
validatorsFileName.empty () ? Config::validatorsFileName :
validatorsFileName);
if (!exists (validatorsFile_))
{
std::ofstream o (validatorsFile_.string ());
o << valFileContents (quorum);
}
else
{
Throw<std::runtime_error> (
"Refusing to overwrite existing config file: " +
validatorsFile_.string ());
}
}
bool validatorsFileExists () const
{
return boost::filesystem::exists (validatorsFile_);
}
std::string validatorsFile () const
{
return validatorsFile_.string ();
}
~ValidatorsTxtGuard ()
{
try
{
using namespace boost::filesystem;
if (!boost::filesystem::exists (validatorsFile_))
test_.log << "Expected " << validatorsFile_.string ()
<< " to be an existing file." << std::endl;
else
remove (validatorsFile_.string ());
}
catch (std::exception& e)
{
// if we throw here, just let it die.
test_.log << "Error in ~ValidatorsTxtGuard: "
<< e.what () << std::endl;
};
}
};
} // detail
class Config_test final : public TestSuite
{
private:
using path = boost::filesystem::path;
public:
void testLegacy ()
{
testcase ("legacy");
Config c;
std::string toLoad(R"rippleConfig(
[server]
port_rpc
port_peer
port_wss_admin
[ssl_verify]
0
[validation_quorum]
3
)rippleConfig");
c.loadFromString (toLoad);
BEAST_EXPECT(c.legacy ("ssl_verify") == "0");
expectException ([&c] {c.legacy ("server");}); // not a single line
// set a legacy value
BEAST_EXPECT(c.legacy ("not_in_file") == "");
c.legacy ("not_in_file", "new_value");
BEAST_EXPECT(c.legacy ("not_in_file") == "new_value");
}
void testDbPath ()
{
testcase ("database_path");
using namespace boost::filesystem;
{
boost::format cc ("[database_path]\n%1%\n");
auto const cwd = current_path ();
path const dataDirRel ("test_data_dir");
path const dataDirAbs (cwd / dataDirRel);
{
// Dummy test - do we get back what we put in
Config c;
c.loadFromString (boost::str (cc % dataDirAbs.string ()));
BEAST_EXPECT(c.legacy ("database_path") == dataDirAbs.string ());
}
{
// Rel paths should convert to abs paths
Config c;
c.loadFromString (boost::str (cc % dataDirRel.string ()));
BEAST_EXPECT(c.legacy ("database_path") == dataDirAbs.string ());
}
{
// No db section.
// N.B. Config::setup will give database_path a default,
// load will not.
Config c;
c.loadFromString ("");
BEAST_EXPECT(c.legacy ("database_path") == "");
}
}
{
// read from file absolute path
auto const cwd = current_path ();
path const dataDirRel ("test_data_dir");
path const dataDirAbs (cwd / path ("test_db") / dataDirRel);
detail::RippledCfgGuard g (*this, "test_db", dataDirAbs.string (), "");
auto& c (g.config ());
BEAST_EXPECT(g.dataDirExists ());
BEAST_EXPECT(g.configFileExists ());
BEAST_EXPECT(c.legacy ("database_path") == dataDirAbs.string ());
}
{
// read from file relative path
std::string const dbPath ("my_db");
detail::RippledCfgGuard g (*this, "test_db", dbPath, "");
auto& c (g.config ());
std::string const nativeDbPath = absolute (path (dbPath)).string ();
BEAST_EXPECT(g.dataDirExists ());
BEAST_EXPECT(g.configFileExists ());
BEAST_EXPECT(c.legacy ("database_path") == nativeDbPath);
}
{
// read from file no path
detail::RippledCfgGuard g (*this, "test_db", "", "");
auto& c (g.config ());
std::string const nativeDbPath =
absolute (path ("test_db") /
path (Config::databaseDirName))
.string ();
BEAST_EXPECT(g.dataDirExists ());
BEAST_EXPECT(g.configFileExists ());
BEAST_EXPECT(c.legacy ("database_path") == nativeDbPath);
}
}
void testValidatorsFile ()
{
testcase ("validators_file");
using namespace boost::filesystem;
{
// load should throw for missing specified validators file
Config c;
boost::format cc ("[validators_file]\n%1%\n");
std::string error;
std::string const missingPath = "/no/way/this/path/exists";
auto const expectedError =
"The file specified in [validators_file] does not exist: " +
missingPath;
try {
c.loadFromString (boost::str (cc % missingPath));
} catch (std::runtime_error& e) {
error = e.what();
}
BEAST_EXPECT(error == expectedError);
}
{
// load should throw for invalid [validators_file]
int const quorum = 3;
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", "validators.cfg", quorum);
Config c;
path const invalidFile = current_path () / "test_cfg";
boost::format cc ("[validators_file]\n%1%\n");
std::string error;
auto const expectedError =
"Invalid file specified in [validators_file]: " +
invalidFile.string ();
try {
c.loadFromString (boost::str (cc % invalidFile.string ()));
} catch (std::runtime_error& e) {
error = e.what();
}
BEAST_EXPECT(error == expectedError);
}
{
// load validators and quorum from config
Config c;
std::string toLoad(R"rippleConfig(
[validators]
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
[validator_keys]
nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5
nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
[validation_quorum]
4
)rippleConfig");
c.loadFromString (toLoad);
BEAST_EXPECT(c.legacy ("validators_file").empty ());
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 3);
BEAST_EXPECT(c.section (SECTION_VALIDATOR_KEYS).values ().size () == 2);
BEAST_EXPECT(c.VALIDATION_QUORUM == 4);
}
{
// load from specified [validators_file] absolute path
int const quorum = 3;
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", "validators.cfg", quorum);
BEAST_EXPECT(vtg.validatorsFileExists ());
Config c;
boost::format cc ("[validators_file]\n%1%\n");
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 5);
BEAST_EXPECT(c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
BEAST_EXPECT(c.VALIDATION_QUORUM == quorum);
}
{
// load from specified [validators_file] file name
// in config directory
int const quorum = 3;
std::string const valFileName = "validators.txt";
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", valFileName, quorum);
detail::RippledCfgGuard rcg (
*this, "test_cfg", "", valFileName);
BEAST_EXPECT(vtg.validatorsFileExists ());
BEAST_EXPECT(rcg.configFileExists ());
auto& c (rcg.config ());
BEAST_EXPECT(c.legacy ("validators_file") == valFileName);
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 5);
BEAST_EXPECT(c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
BEAST_EXPECT(c.VALIDATION_QUORUM == quorum);
}
{
// load from specified [validators_file] relative path
// to config directory
int const quorum = 3;
std::string const valFilePath = "../test_cfg/validators.txt";
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", "validators.txt", quorum);
detail::RippledCfgGuard rcg (
*this, "test_cfg", "", valFilePath);
BEAST_EXPECT(vtg.validatorsFileExists ());
BEAST_EXPECT(rcg.configFileExists ());
auto& c (rcg.config ());
BEAST_EXPECT(c.legacy ("validators_file") == valFilePath);
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 5);
BEAST_EXPECT(c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
BEAST_EXPECT(c.VALIDATION_QUORUM == quorum);
}
{
// load from validators file in default location
int const quorum = 3;
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", "validators.txt", quorum);
detail::RippledCfgGuard rcg (*this, "test_cfg", "", "");
BEAST_EXPECT(vtg.validatorsFileExists ());
BEAST_EXPECT(rcg.configFileExists ());
auto& c (rcg.config ());
BEAST_EXPECT(c.legacy ("validators_file").empty ());
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 5);
BEAST_EXPECT(c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
BEAST_EXPECT(c.VALIDATION_QUORUM == quorum);
}
{
// load from specified [validators_file] instead
// of default location
int const quorum = 3;
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", "validators.cfg", quorum);
BEAST_EXPECT(vtg.validatorsFileExists ());
detail::ValidatorsTxtGuard vtgDefault (
*this, "test_cfg", "validators.txt", 4);
BEAST_EXPECT(vtgDefault.validatorsFileExists ());
detail::RippledCfgGuard rcg (
*this, "test_cfg", "", vtg.validatorsFile ());
BEAST_EXPECT(rcg.configFileExists ());
auto& c (rcg.config ());
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 5);
BEAST_EXPECT(c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
BEAST_EXPECT(c.VALIDATION_QUORUM == quorum);
}
{
// do not load quorum from validators file if in config
boost::format cc (R"rippleConfig(
[validators_file]
%1%
[validation_quorum]
4
)rippleConfig");
int const quorum = 3;
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", "validators.cfg", quorum);
BEAST_EXPECT(vtg.validatorsFileExists ());
Config c;
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 5);
BEAST_EXPECT(c.section (SECTION_VALIDATOR_KEYS).values ().size () == 3);
BEAST_EXPECT(c.VALIDATION_QUORUM == 4);
}
{
// load validators from both config and validators file
boost::format cc (R"rippleConfig(
[validators_file]
%1%
[validators]
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS
n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
[validator_keys]
nHB1X37qrniVugfQcuBTAjswphC1drx7QjFFojJPZwKHHnt8kU7v
nHUkAWDR4cB8AgPg7VXMX6et8xRTQb2KJfgv1aBEXozwrawRKgMB
)rippleConfig");
int const quorum = 3;
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", "validators.cfg", quorum);
BEAST_EXPECT(vtg.validatorsFileExists ());
Config c;
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
BEAST_EXPECT(c.legacy ("validators_file") == vtg.validatorsFile ());
BEAST_EXPECT(c.section (SECTION_VALIDATORS).values ().size () == 10);
BEAST_EXPECT(c.section (SECTION_VALIDATOR_KEYS).values ().size () == 5);
BEAST_EXPECT(c.VALIDATION_QUORUM == quorum);
}
{
// load should throw if [validators] and [validator_keys] are
// missing from rippled cfg and validators file
Config c;
boost::format cc ("[validators_file]\n%1%\n");
std::string error;
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", "validators.cfg", boost::none);
BEAST_EXPECT(vtg.validatorsFileExists ());
auto const expectedError =
"The file specified in [validators_file] does not contain a "
"[validators] or [validator_keys] section: " +
vtg.validatorsFile ();
std::ofstream o (vtg.validatorsFile ());
o << "[validation_quorum]\n3\n";
try {
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
} catch (std::runtime_error& e) {
error = e.what();
}
BEAST_EXPECT(error == expectedError);
}
{
// load should throw if [validation_quorum] is
// missing from rippled cfg and validators file
Config c;
boost::format cc ("[validators_file]\n%1%\n");
std::string error;
detail::ValidatorsTxtGuard vtg (
*this, "test_cfg", "validators.cfg", boost::none);
BEAST_EXPECT(vtg.validatorsFileExists ());
auto const expectedError =
"The file specified in [validators_file] does not contain a "
"[validation_quorum] section: " + vtg.validatorsFile ();
try {
c.loadFromString (boost::str (cc % vtg.validatorsFile ()));
} catch (std::runtime_error& e) {
error = e.what();
}
BEAST_EXPECT(error == expectedError);
}
}
void testSetup(bool explicitPath)
{
detail::RippledCfgGuard cfg(*this, "testSetup",
explicitPath ? "test_db" : "", "");
/* ConfigGuard has a Config object that gets loaded on construction,
but Config::setup is not reentrant, so we need a fresh config
for every test case, so ignore it.
*/
{
Config config;
config.setup(cfg.configFile(), /*bQuiet*/ false,
/* bSilent */ false, /* bStandalone */ false);
BEAST_EXPECT(!config.quiet());
BEAST_EXPECT(!config.silent());
BEAST_EXPECT(!config.standalone());
BEAST_EXPECT(config.LEDGER_HISTORY == 256);
BEAST_EXPECT(!config.legacy("database_path").empty());
}
{
Config config;
config.setup(cfg.configFile(), /*bQuiet*/ true,
/* bSilent */ false, /* bStandalone */ false);
BEAST_EXPECT(config.quiet());
BEAST_EXPECT(!config.silent());
BEAST_EXPECT(!config.standalone());
BEAST_EXPECT(config.LEDGER_HISTORY == 256);
BEAST_EXPECT(!config.legacy("database_path").empty());
}
{
Config config;
config.setup(cfg.configFile(), /*bQuiet*/ false,
/* bSilent */ true, /* bStandalone */ false);
BEAST_EXPECT(config.quiet());
BEAST_EXPECT(config.silent());
BEAST_EXPECT(!config.standalone());
BEAST_EXPECT(config.LEDGER_HISTORY == 256);
BEAST_EXPECT(!config.legacy("database_path").empty());
}
{
Config config;
config.setup(cfg.configFile(), /*bQuiet*/ true,
/* bSilent */ true, /* bStandalone */ false);
BEAST_EXPECT(config.quiet());
BEAST_EXPECT(config.silent());
BEAST_EXPECT(!config.standalone());
BEAST_EXPECT(config.LEDGER_HISTORY == 256);
BEAST_EXPECT(!config.legacy("database_path").empty());
}
{
Config config;
config.setup(cfg.configFile(), /*bQuiet*/ false,
/* bSilent */ false, /* bStandalone */ true);
BEAST_EXPECT(!config.quiet());
BEAST_EXPECT(!config.silent());
BEAST_EXPECT(config.standalone());
BEAST_EXPECT(config.LEDGER_HISTORY == 0);
BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath);
}
{
Config config;
config.setup(cfg.configFile(), /*bQuiet*/ true,
/* bSilent */ false, /* bStandalone */ true);
BEAST_EXPECT(config.quiet());
BEAST_EXPECT(!config.silent());
BEAST_EXPECT(config.standalone());
BEAST_EXPECT(config.LEDGER_HISTORY == 0);
BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath);
}
{
Config config;
config.setup(cfg.configFile(), /*bQuiet*/ false,
/* bSilent */ true, /* bStandalone */ true);
BEAST_EXPECT(config.quiet());
BEAST_EXPECT(config.silent());
BEAST_EXPECT(config.standalone());
BEAST_EXPECT(config.LEDGER_HISTORY == 0);
BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath);
}
{
Config config;
config.setup(cfg.configFile(), /*bQuiet*/ true,
/* bSilent */ true, /* bStandalone */ true);
BEAST_EXPECT(config.quiet());
BEAST_EXPECT(config.silent());
BEAST_EXPECT(config.standalone());
BEAST_EXPECT(config.LEDGER_HISTORY == 0);
BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath);
}
}
void run ()
{
testLegacy ();
testDbPath ();
testValidatorsFile ();
testSetup (false);
testSetup (true);
}
};
BEAST_DEFINE_TESTSUITE (Config, core, ripple);
} // ripple

View File

@@ -0,0 +1,185 @@
//------------------------------------------------------------------------------
/*
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/core/JobQueue.h>
#include <ripple/test/jtx.h>
#include <chrono>
#include <condition_variable>
#include <mutex>
namespace ripple {
namespace test {
class Coroutine_test : public beast::unit_test::suite
{
public:
class gate
{
private:
std::condition_variable cv_;
std::mutex mutex_;
bool signaled_ = false;
public:
// Thread safe, blocks until signaled or period expires.
// Returns `true` if signaled.
template <class Rep, class Period>
bool
wait_for(std::chrono::duration<Rep, Period> const& rel_time)
{
std::unique_lock<std::mutex> lk(mutex_);
auto b = cv_.wait_for(lk, rel_time, [=]{ return signaled_; });
signaled_ = false;
return b;
}
void
signal()
{
std::lock_guard<std::mutex> lk(mutex_);
signaled_ = true;
cv_.notify_all();
}
};
void
correct_order()
{
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
auto& jq = env.app().getJobQueue();
jq.setThreadCount(0, false);
gate g1, g2;
std::shared_ptr<JobCoro> jc;
jq.postCoro(jtCLIENT, "Coroutine-Test",
[&](auto const& jcr)
{
jc = jcr;
g1.signal();
jc->yield();
g2.signal();
});
BEAST_EXPECT(g1.wait_for(5s));
jc->join();
jc->post();
BEAST_EXPECT(g2.wait_for(5s));
}
void
incorrect_order()
{
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
auto& jq = env.app().getJobQueue();
jq.setThreadCount(0, false);
gate g;
jq.postCoro(jtCLIENT, "Coroutine-Test",
[&](auto const& jc)
{
jc->post();
jc->yield();
g.signal();
});
BEAST_EXPECT(g.wait_for(5s));
}
void
thread_specific_storage()
{
using namespace std::chrono_literals;
using namespace jtx;
Env env(*this);
auto& jq = env.app().getJobQueue();
jq.setThreadCount(0, true);
static int const N = 4;
std::array<std::shared_ptr<JobCoro>, N> a;
LocalValue<int> lv(-1);
BEAST_EXPECT(*lv == -1);
gate g;
jq.addJob(jtCLIENT, "LocalValue-Test",
[&](auto const& job)
{
this->BEAST_EXPECT(*lv == -1);
*lv = -2;
this->BEAST_EXPECT(*lv == -2);
g.signal();
});
BEAST_EXPECT(g.wait_for(5s));
BEAST_EXPECT(*lv == -1);
for(int i = 0; i < N; ++i)
{
jq.postCoro(jtCLIENT, "Coroutine-Test",
[&, id = i](auto const& jc)
{
a[id] = jc;
g.signal();
jc->yield();
this->BEAST_EXPECT(*lv == -1);
*lv = id;
this->BEAST_EXPECT(*lv == id);
g.signal();
jc->yield();
this->BEAST_EXPECT(*lv == id);
});
BEAST_EXPECT(g.wait_for(5s));
a[i]->join();
}
for(auto const& jc : a)
{
jc->post();
BEAST_EXPECT(g.wait_for(5s));
jc->join();
}
for(auto const& jc : a)
{
jc->post();
jc->join();
}
jq.addJob(jtCLIENT, "LocalValue-Test",
[&](auto const& job)
{
this->BEAST_EXPECT(*lv == -2);
g.signal();
});
BEAST_EXPECT(g.wait_for(5s));
BEAST_EXPECT(*lv == -1);
}
void
run()
{
correct_order();
incorrect_order();
thread_specific_storage();
}
};
BEAST_DEFINE_TESTSUITE(Coroutine,core,ripple);
} // test
} // ripple

View File

@@ -0,0 +1,379 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2015 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/core/ConfigSections.h>
#include <ripple/core/SociDB.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/TestSuite.h>
#include <ripple/basics/BasicConfig.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
namespace ripple {
class SociDB_test final : public TestSuite
{
private:
static void setupSQLiteConfig (BasicConfig& config,
boost::filesystem::path const& dbPath)
{
config.overwrite ("sqdb", "backend", "sqlite");
auto value = dbPath.string ();
if (!value.empty ())
config.legacy ("database_path", value);
}
static void cleanupDatabaseDir (boost::filesystem::path const& dbPath)
{
using namespace boost::filesystem;
if (!exists (dbPath) || !is_directory (dbPath) || !is_empty (dbPath))
return;
remove (dbPath);
}
static void setupDatabaseDir (boost::filesystem::path const& dbPath)
{
using namespace boost::filesystem;
if (!exists (dbPath))
{
create_directory (dbPath);
return;
}
if (!is_directory (dbPath))
{
// someone created a file where we want to put out directory
Throw<std::runtime_error> (
"Cannot create directory: " + dbPath.string ());
}
}
static boost::filesystem::path getDatabasePath ()
{
return boost::filesystem::current_path () / "socidb_test_databases";
}
public:
SociDB_test ()
{
try
{
setupDatabaseDir (getDatabasePath ());
}
catch (std::exception const&)
{
}
}
~SociDB_test ()
{
try
{
cleanupDatabaseDir (getDatabasePath ());
}
catch (std::exception const&)
{
}
}
void testSQLiteFileNames ()
{
// confirm that files are given the correct exensions
testcase ("sqliteFileNames");
BasicConfig c;
setupSQLiteConfig (c, getDatabasePath ());
std::vector<std::pair<std::string, std::string>> const d (
{{"peerfinder", ".sqlite"},
{"state", ".db"},
{"random", ".db"},
{"validators", ".sqlite"}});
for (auto const& i : d)
{
SociConfig sc (c, i.first);
BEAST_EXPECT(boost::ends_with (sc.connectionString (),
i.first + i.second));
}
}
void testSQLiteSession ()
{
testcase ("open");
BasicConfig c;
setupSQLiteConfig (c, getDatabasePath ());
SociConfig sc (c, "SociTestDB");
std::vector<std::string> const stringData (
{"String1", "String2", "String3"});
std::vector<int> const intData ({1, 2, 3});
auto checkValues = [this, &stringData, &intData](soci::session& s)
{
// Check values in db
std::vector<std::string> stringResult (20 * stringData.size ());
std::vector<int> intResult (20 * intData.size ());
s << "SELECT StringData, IntData FROM SociTestTable;",
soci::into (stringResult), soci::into (intResult);
BEAST_EXPECT(stringResult.size () == stringData.size () &&
intResult.size () == intData.size ());
for (int i = 0; i < stringResult.size (); ++i)
{
auto si = std::distance (stringData.begin (),
std::find (stringData.begin (),
stringData.end (),
stringResult[i]));
auto ii = std::distance (
intData.begin (),
std::find (intData.begin (), intData.end (), intResult[i]));
BEAST_EXPECT(si == ii && si < stringResult.size ());
}
};
{
soci::session s;
sc.open (s);
s << "CREATE TABLE IF NOT EXISTS SociTestTable ("
" Key INTEGER PRIMARY KEY,"
" StringData TEXT,"
" IntData INTEGER"
");";
s << "INSERT INTO SociTestTable (StringData, IntData) VALUES "
"(:stringData, :intData);",
soci::use (stringData), soci::use (intData);
checkValues (s);
}
{
// Check values in db after session was closed
soci::session s;
sc.open (s);
checkValues (s);
}
{
namespace bfs = boost::filesystem;
// Remove the database
bfs::path dbPath (sc.connectionString ());
if (bfs::is_regular_file (dbPath))
bfs::remove (dbPath);
}
}
void testSQLiteSelect ()
{
testcase ("select");
BasicConfig c;
setupSQLiteConfig (c, getDatabasePath ());
SociConfig sc (c, "SociTestDB");
std::vector<std::uint64_t> const ubid (
{(std::uint64_t)std::numeric_limits<std::int64_t>::max (), 20, 30});
std::vector<std::int64_t> const bid ({-10, -20, -30});
std::vector<std::uint32_t> const uid (
{std::numeric_limits<std::uint32_t>::max (), 2, 3});
std::vector<std::int32_t> const id ({-1, -2, -3});
{
soci::session s;
sc.open (s);
s << "DROP TABLE IF EXISTS STT;";
s << "CREATE TABLE STT ("
" I INTEGER,"
" UI INTEGER UNSIGNED,"
" BI BIGINT,"
" UBI BIGINT UNSIGNED"
");";
s << "INSERT INTO STT (I, UI, BI, UBI) VALUES "
"(:id, :idu, :bid, :bidu);",
soci::use (id), soci::use (uid), soci::use (bid),
soci::use (ubid);
try
{
std::int32_t ig = 0;
std::uint32_t uig = 0;
std::int64_t big = 0;
std::uint64_t ubig = 0;
s << "SELECT I, UI, BI, UBI from STT;", soci::into (ig),
soci::into (uig), soci::into (big), soci::into (ubig);
BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
ubig == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
try
{
boost::optional<std::int32_t> ig;
boost::optional<std::uint32_t> uig;
boost::optional<std::int64_t> big;
boost::optional<std::uint64_t> ubig;
s << "SELECT I, UI, BI, UBI from STT;", soci::into (ig),
soci::into (uig), soci::into (big), soci::into (ubig);
BEAST_EXPECT(*ig == id[0] && *uig == uid[0] && *big == bid[0] &&
*ubig == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
// There are too many issues when working with soci::row and boost::tuple. DO NOT USE
// soci row! I had a set of workarounds to make soci row less error prone, I'm keeping
// these tests in case I try to add soci::row and boost::tuple back into soci.
#if 0
try
{
std::int32_t ig = 0;
std::uint32_t uig = 0;
std::int64_t big = 0;
std::uint64_t ubig = 0;
soci::row r;
s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
ig = r.get<std::int32_t>(0);
uig = r.get<std::uint32_t>(1);
big = r.get<std::int64_t>(2);
ubig = r.get<std::uint64_t>(3);
BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
ubig == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
try
{
std::int32_t ig = 0;
std::uint32_t uig = 0;
std::int64_t big = 0;
std::uint64_t ubig = 0;
soci::row r;
s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
ig = r.get<std::int32_t>("I");
uig = r.get<std::uint32_t>("UI");
big = r.get<std::int64_t>("BI");
ubig = r.get<std::uint64_t>("UBI");
BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
ubig == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
try
{
boost::tuple<std::int32_t,
std::uint32_t,
std::int64_t,
std::uint64_t> d;
s << "SELECT I, UI, BI, UBI from STT", soci::into (d);
BEAST_EXPECT(get<0>(d) == id[0] && get<1>(d) == uid[0] &&
get<2>(d) == bid[0] && get<3>(d) == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
#endif
}
{
namespace bfs = boost::filesystem;
// Remove the database
bfs::path dbPath (sc.connectionString ());
if (bfs::is_regular_file (dbPath))
bfs::remove (dbPath);
}
}
void testSQLiteDeleteWithSubselect()
{
testcase ("deleteWithSubselect");
BasicConfig c;
setupSQLiteConfig (c, getDatabasePath ());
SociConfig sc (c, "SociTestDB");
{
soci::session s;
sc.open (s);
const char* dbInit[] = {
"BEGIN TRANSACTION;",
"CREATE TABLE Ledgers ( \
LedgerHash CHARACTER(64) PRIMARY KEY, \
LedgerSeq BIGINT UNSIGNED \
);",
"CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);",
"CREATE TABLE Validations ( \
LedgerHash CHARACTER(64) \
);",
"CREATE INDEX ValidationsByHash ON \
Validations(LedgerHash);",
"END TRANSACTION;"};
int dbInitCount = std::extent<decltype(dbInit)>::value;
for (int i = 0; i < dbInitCount; ++i)
{
s << dbInit[i];
}
char lh[65];
memset (lh, 'a', 64);
lh[64] = '\0';
int toIncIndex = 63;
int const numRows = 16;
std::vector<std::string> ledgerHashes;
std::vector<int> ledgerIndexes;
ledgerHashes.reserve(numRows);
ledgerIndexes.reserve(numRows);
for (int i = 0; i < numRows; ++i)
{
++lh[toIncIndex];
if (lh[toIncIndex] == 'z')
--toIncIndex;
ledgerHashes.emplace_back(lh);
ledgerIndexes.emplace_back(i);
}
s << "INSERT INTO Ledgers (LedgerHash, LedgerSeq) VALUES "
"(:lh, :li);",
soci::use (ledgerHashes), soci::use (ledgerIndexes);
s << "INSERT INTO Validations (LedgerHash) VALUES "
"(:lh);", soci::use (ledgerHashes);
std::vector<int> ledgersLS (numRows * 2);
std::vector<std::string> validationsLH (numRows * 2);
s << "SELECT LedgerSeq FROM Ledgers;", soci::into (ledgersLS);
s << "SELECT LedgerHash FROM Validations;",
soci::into (validationsLH);
BEAST_EXPECT(ledgersLS.size () == numRows &&
validationsLH.size () == numRows);
}
namespace bfs = boost::filesystem;
// Remove the database
bfs::path dbPath (sc.connectionString ());
if (bfs::is_regular_file (dbPath))
bfs::remove (dbPath);
}
void testSQLite ()
{
testSQLiteFileNames ();
testSQLiteSession ();
testSQLiteSelect ();
testSQLiteDeleteWithSubselect();
}
void run ()
{
testSQLite ();
}
};
BEAST_DEFINE_TESTSUITE(SociDB,core,ripple);
} // ripple

View File

@@ -0,0 +1,455 @@
//------------------------------------------------------------------------------
/*
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 <ripple/core/Stoppable.h>
#include <ripple/beast/unit_test.h>
#include <thread>
namespace ripple {
namespace test {
class Stoppable_test
: public beast::unit_test::suite
{
/*
R
/ | \
/ | \
A B C
/ | \ /\ |
D E F G H I
|
J
*/
unsigned count = 0;
class D
: public Stoppable
{
Stoppable_test& test_;
public:
D(Stoppable& parent, Stoppable_test& test)
: Stoppable("D", parent)
, test_(test)
{}
void onPrepare() override
{
test_.expect(++test_.count == 9, "D::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 0, "D::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 11, "D::onStop called out of order");
}
void onChildrenStopped() override
{
Stoppable::stopped();
test_.expect(--test_.count == 2, "D::onChildrenStopped called out of order");
}
};
class J
: public Stoppable
{
Stoppable_test& test_;
public:
J(Stoppable& parent, Stoppable_test& test)
: Stoppable("J", parent)
, test_(test)
{}
void onPrepare() override
{
test_.expect(++test_.count == 7, "J::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 1, "J::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 10, "J::onStop called out of order");
}
void onChildrenStopped() override
{
Stoppable::stopped();
test_.expect(--test_.count == 4, "J::onChildrenStopped called out of order");
}
};
class E
: public Stoppable
{
J j_;
Stoppable_test& test_;
public:
E(Stoppable& parent, Stoppable_test& test)
: Stoppable("E", parent)
, j_(*this, test)
, test_(test)
{}
void onPrepare() override
{
test_.expect(++test_.count == 8, "E::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 2, "E::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 9, "E::onStop called out of order");
}
void onChildrenStopped() override
{
Stoppable::stopped();
test_.expect(--test_.count == 3, "E::onChildrenStopped called out of order");
}
};
class F
: public Stoppable
{
Stoppable_test& test_;
public:
F(Stoppable& parent, Stoppable_test& test)
: Stoppable("F", parent)
, test_(test)
{}
void onPrepare() override
{
test_.expect(++test_.count == 6, "F::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 3, "F::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 8, "F::onStop called out of order");
}
void onChildrenStopped() override
{
Stoppable::stopped();
test_.expect(--test_.count == 5, "F::onChildrenStopped called out of order");
}
};
class A
: public Stoppable
{
enum {running, please_stop, stopping, stopped};
D d_;
E e_;
F f_;
Stoppable_test& test_;
std::atomic<int> stop_;
public:
A(Stoppable& parent, Stoppable_test& test)
: Stoppable("A", parent)
, d_(*this, test)
, e_(*this, test)
, f_(*this, test)
, test_(test)
, stop_(running)
{}
~A()
{
while (stop_ != stopped)
;
}
void run()
{
while (stop_ == running)
;
stop_ = stopping;
}
void onPrepare() override
{
test_.expect(++test_.count == 10, "A::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 4, "A::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 7, "A::onStop called out of order");
}
void onChildrenStopped() override
{
stop_ = please_stop;
while (stop_ != stopping)
;
Stoppable::stopped();
test_.expect(--test_.count == 1, "A::onChildrenStopped called out of order");
stop_ = stopped;
}
};
class G
: public Stoppable
{
Stoppable_test& test_;
public:
G(Stoppable& parent, Stoppable_test& test)
: Stoppable("G", parent)
, test_(test)
{}
void onPrepare() override
{
test_.expect(++test_.count == 4, "G::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 5, "G::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 6, "G::onStop called out of order");
}
void onChildrenStopped() override
{
Stoppable::stopped();
test_.expect(--test_.count == 7, "G::onChildrenStopped called out of order");
}
};
class H
: public Stoppable
{
Stoppable_test& test_;
public:
H(Stoppable& parent, Stoppable_test& test)
: Stoppable("H", parent)
, test_(test)
{}
void onPrepare() override
{
test_.expect(++test_.count == 3, "H::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 6, "H::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 5, "H::onStop called out of order");
}
void onChildrenStopped() override
{
Stoppable::stopped();
test_.expect(--test_.count == 8, "H::onChildrenStopped called out of order");
}
};
class B
: public Stoppable
{
G g_;
H h_;
Stoppable_test& test_;
public:
B(Stoppable& parent, Stoppable_test& test)
: Stoppable("B", parent)
, g_(*this, test)
, h_(*this, test)
, test_(test)
{}
void onPrepare() override
{
test_.expect(++test_.count == 5, "B::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 7, "B::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 4, "B::onStop called out of order");
}
void onChildrenStopped() override
{
Stoppable::stopped();
test_.expect(--test_.count == 6, "B::onChildrenStopped called out of order");
}
};
class I
: public Stoppable
{
Stoppable_test& test_;
public:
I(Stoppable& parent, Stoppable_test& test)
: Stoppable("I", parent)
, test_(test)
{}
void onPrepare() override
{
test_.expect(++test_.count == 1, "I::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 8, "I::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 3, "I::onStop called out of order");
}
void onChildrenStopped() override
{
Stoppable::stopped();
test_.expect(--test_.count == 10, "I::onChildrenStopped called out of order");
}
};
class C
: public Stoppable
{
I i_;
Stoppable_test& test_;
public:
C(Stoppable& parent, Stoppable_test& test)
: Stoppable("C", parent)
, i_(*this, test)
, test_(test)
{}
void onPrepare() override
{
test_.expect(++test_.count == 2, "C::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 9, "C::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 2, "C::onStop called out of order");
}
void onChildrenStopped() override
{
Stoppable::stopped();
test_.expect(--test_.count == 9, "C::onChildrenStopped called out of order");
}
};
class Root
: public RootStoppable
{
std::thread a_;
B b_;
C c_;
Stoppable_test& test_;
public:
Root(Stoppable_test& test)
: RootStoppable("R")
, a_(&A::run, std::make_unique<A>(*this, test))
, b_(*this, test)
, c_(*this, test)
, test_(test)
{}
void run()
{
prepare();
start();
stop({});
}
void onPrepare() override
{
test_.expect(++test_.count == 11, "Root::onPrepare called out of order");
}
void onStart() override
{
test_.expect(--test_.count == 10, "Root::onStart called out of order");
}
void onStop() override
{
test_.expect(++test_.count == 1, "Root::onStop called out of order");
}
void onChildrenStopped() override
{
a_.join();
Stoppable::stopped();
test_.expect(--test_.count == 0, "Root::onChildrenStopped called out of order");
}
};
public:
void run()
{
{
Root rt(*this);
rt.run();
}
pass();
}
};
BEAST_DEFINE_TESTSUITE(Stoppable,core,ripple);
}
}

View File

@@ -0,0 +1,87 @@
//------------------------------------------------------------------------------
/*
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 <ripple/core/impl/Workers.h>
#include <ripple/beast/unit_test.h>
namespace ripple {
//------------------------------------------------------------------------------
class Workers_test : public beast::unit_test::suite
{
public:
struct TestCallback : Workers::Callback
{
explicit TestCallback(int count_)
: finished(false, count_ == 0)
, count(count_)
{
}
void processTask()
{
if (--count == 0)
finished.signal();
}
beast::WaitableEvent finished;
std::atomic <int> count;
};
void testThreads(int const threadCount)
{
testcase("threadCount = " + std::to_string(threadCount));
TestCallback cb(threadCount);
Workers w(cb, "Test", 0);
BEAST_EXPECT(w.getNumberOfThreads() == 0);
w.setNumberOfThreads(threadCount);
BEAST_EXPECT(w.getNumberOfThreads() == threadCount);
for (int i = 0; i < threadCount; ++i)
w.addTask();
// 10 seconds should be enough to finish on any system
//
bool signaled = cb.finished.wait(10 * 1000);
BEAST_EXPECT(signaled);
w.pauseAllThreadsAndWait();
// We had better finished all our work!
BEAST_EXPECT(cb.count.load() == 0);
}
void run()
{
testThreads(0);
testThreads(1);
testThreads(2);
testThreads(4);
testThreads(16);
testThreads(64);
}
};
BEAST_DEFINE_TESTSUITE(Workers, core, ripple);
}