mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
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:
794
src/test/core/Config_test.cpp
Normal file
794
src/test/core/Config_test.cpp
Normal 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
|
||||
185
src/test/core/Coroutine_test.cpp
Normal file
185
src/test/core/Coroutine_test.cpp
Normal 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
|
||||
379
src/test/core/SociDB_test.cpp
Normal file
379
src/test/core/SociDB_test.cpp
Normal 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
|
||||
455
src/test/core/Stoppable_test.cpp
Normal file
455
src/test/core/Stoppable_test.cpp
Normal 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);
|
||||
|
||||
}
|
||||
}
|
||||
87
src/test/core/Workers_test.cpp
Normal file
87
src/test/core/Workers_test.cpp
Normal 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);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user