Enable manual tests in CI:

Fixes: RIPD-1575. Fix argument passing to runner. Allow multiple unit
test selectors to be passed via --unittest argument. Add optional
integer priority value to test suite list. Fix several failing manual
tests. Update CLI usage message to make it clearer.
This commit is contained in:
Mike Ellery
2018-05-08 08:46:31 -07:00
committed by seelabs
parent 95eb5e1862
commit cfdc64d7cf
25 changed files with 243 additions and 100 deletions

5
Jenkinsfile vendored
View File

@@ -89,6 +89,7 @@ try {
stage ('Parallel Build') {
String[][] variants = [
['gcc.release.unity', '-Dassert=true', 'MANUAL_TESTS=true'],
['coverage'],
['docs'],
['msvc.debug'],
@@ -101,8 +102,8 @@ try {
['clang.debug.nounity'],
['gcc.debug.unity'],
['gcc.debug.nounity'],
['clang.release.unity'],
['gcc.release.unity'],
['clang.release.unity', '-Dassert=true'],
['gcc.release.unity', '-Dassert=true'],
// add a static build just to make sure it works
['gcc.debug.unity', '-Dstatic=true'],
// TODO - sanitizer runs currently fail

View File

@@ -96,8 +96,34 @@ echo "using APP_PATH: $APP_PATH"
# See what we've actually built
ldd $APP_PATH
function join_by { local IFS="$1"; shift; echo "$*"; }
# This is a list of manual tests
# in rippled that we want to run
declare -a manual_tests=(
"beast.chrono.abstract_clock"
"beast.unit_test.print"
"ripple.NodeStore.Timing"
"ripple.app.Flow_manual"
"ripple.app.NoRippleCheckLimits"
"ripple.app.PayStrandAllPairs"
"ripple.consensus.ByzantineFailureSim"
"ripple.consensus.DistributedValidators"
"ripple.consensus.ScaleFreeSim"
"ripple.ripple_data.digest"
"ripple.tx.CrossingLimits"
"ripple.tx.FindOversizeCross"
"ripple.tx.Offer_manual"
"ripple.tx.OversizeMeta"
"ripple.tx.PlumpBook"
)
if [[ ${APP} == "rippled" ]]; then
APP_ARGS+="--unittest --quiet --unittest-log"
if [[ ${MANUAL_TESTS:-} == true ]]; then
APP_ARGS+="--unittest=$(join_by , "${manual_tests[@]}")"
else
APP_ARGS+="--unittest --quiet --unittest-log"
fi
# Only report on src/ripple files
export LCOV_FILES="*/src/ripple/*"
# Nothing to explicitly exclude

View File

@@ -28,10 +28,10 @@ template<class Suite>
struct insert_suite
{
insert_suite(char const* name, char const* module,
char const* library, bool manual)
char const* library, bool manual, int priority)
{
global_suites().insert<Suite>(
name, module, library, manual);
name, module, library, manual, priority);
}
};

View File

@@ -627,10 +627,10 @@ run(runner& r)
// detail:
// This inserts the suite with the given manual flag
#define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual) \
#define BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,manual,priority) \
static beast::unit_test::detail::insert_suite <Class##_test> \
Library ## Module ## Class ## _test_instance( \
#Class, #Module, #Library, manual)
#Class, #Module, #Library, manual, priority)
//------------------------------------------------------------------------------
@@ -675,14 +675,19 @@ run(runner& r)
#if BEAST_NO_UNIT_TEST_INLINE
#define BEAST_DEFINE_TESTSUITE(Class,Module,Library)
#define BEAST_DEFINE_TESTSUITE_MANUAL(Class,Module,Library)
#define BEAST_DEFINE_TESTSUITE_PRIO(Class,Module,Library,Priority)
#define BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Class,Module,Library,Priority)
#else
#include <beast/unit_test/global_suites.hpp>
#define BEAST_DEFINE_TESTSUITE(Class,Module,Library) \
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,false)
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,false,0)
#define BEAST_DEFINE_TESTSUITE_MANUAL(Class,Module,Library) \
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,true)
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,true,0)
#define BEAST_DEFINE_TESTSUITE_PRIO(Class,Module,Library,Priority) \
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,false,Priority)
#define BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Class,Module,Library,Priority) \
BEAST_DEFINE_TESTSUITE_INSERT(Class,Module,Library,true,Priority)
#endif
#endif

View File

@@ -27,6 +27,7 @@ class suite_info
std::string module_;
std::string library_;
bool manual_;
int priority_;
run_type run_;
public:
@@ -35,11 +36,13 @@ public:
std::string module,
std::string library,
bool manual,
int priority,
run_type run)
: name_(std::move(name))
, module_(std::move(module))
, library_(std::move(library))
, manual_(manual)
, priority_(priority)
, run_(std::move(run))
{
}
@@ -87,9 +90,10 @@ public:
bool
operator<(suite_info const& lhs, suite_info const& rhs)
{
return
std::tie(lhs.library_, lhs.module_, lhs.name_) <
std::tie(rhs.library_, rhs.module_, rhs.name_);
// we want higher priority suites sorted first, thus the negation
// of priority value here
return std::forward_as_tuple(-lhs.priority_, lhs.library_, lhs.module_, lhs.name_) <
std::forward_as_tuple(-rhs.priority_, rhs.library_, rhs.module_, rhs.name_);
}
};
@@ -102,13 +106,15 @@ make_suite_info(
std::string name,
std::string module,
std::string library,
bool manual)
bool manual,
int priority)
{
return suite_info(
std::move(name),
std::move(module),
std::move(library),
manual,
priority,
[](runner& r)
{
Suite{}(r);

View File

@@ -39,7 +39,8 @@ public:
char const* name,
char const* module,
char const* library,
bool manual);
bool manual,
int priority);
};
//------------------------------------------------------------------------------
@@ -50,7 +51,8 @@ suite_list::insert(
char const* name,
char const* module,
char const* library,
bool manual)
bool manual,
int priority)
{
#ifndef NDEBUG
{
@@ -67,7 +69,7 @@ suite_list::insert(
}
#endif
cont().emplace(make_suite_info<Suite>(
name, module, library, manual));
name, module, library, manual, priority));
}
} // unit_test

View File

@@ -182,6 +182,40 @@ void printHelp (const po::options_description& desc)
//------------------------------------------------------------------------------
/* simple unit test selector that allows a comma separated list
* of selectors
*/
class multi_selector
{
private:
std::vector<beast::unit_test::selector> selectors_;
public:
explicit
multi_selector(std::string const& patterns = "")
{
std::vector<std::string> v;
boost::split (v, patterns, boost::algorithm::is_any_of (","));
selectors_.reserve(v.size());
std::for_each(v.begin(), v.end(),
[this](std::string s)
{
boost::trim (s);
if (selectors_.empty() || !s.empty())
selectors_.emplace_back(
beast::unit_test::selector::automatch, s);
});
}
bool
operator()(beast::unit_test::suite_info const& s)
{
for (auto& sel : selectors_)
if (sel(s))
return true;
return false;
}
};
static int runUnitTests(
std::string const& pattern,
std::string const& argument,
@@ -202,7 +236,8 @@ static int runUnitTests(
multi_runner_parent parent_runner;
multi_runner_child child_runner{num_jobs, quiet, log};
auto const any_failed = child_runner.run_multi(match_auto(pattern));
child_runner.arg(argument);
auto const any_failed = child_runner.run_multi(multi_selector(pattern));
if (any_failed)
return EXIT_FAILURE;
@@ -251,7 +286,8 @@ static int runUnitTests(
{
// child
multi_runner_child runner{num_jobs, quiet, log};
auto const anyFailed = runner.run_multi(match_auto(pattern));
runner.arg(argument);
auto const anyFailed = runner.run_multi(multi_selector(pattern));
if (anyFailed)
return EXIT_FAILURE;
@@ -291,50 +327,99 @@ int run (int argc, char** argv)
// Set up option parsing.
//
po::options_description desc ("General Options");
desc.add_options ()
("help,h", "Display this message.")
po::options_description gen ("General Options");
gen.add_options ()
("conf", po::value<std::string> (), "Specify the configuration file.")
("rpc", "Perform rpc command (default).")
("rpc_ip", po::value <std::string> (), "Specify the IP address for RPC command. Format: <ip-address>[':'<port-number>]")
("rpc_port", po::value <std::uint16_t> (), "Specify the port number for RPC command.")
("standalone,a", "Run with no peers.")
("unittest,u", po::value <std::string> ()->implicit_value (""), "Perform unit tests.")
("unittest-arg", po::value <std::string> ()->implicit_value (""), "Supplies argument to unit tests.")
("unittest-log", po::value <std::string> ()->implicit_value (""), "Force unit test log output, even in quiet mode.")
#if HAS_BOOST_PROCESS
("unittest-jobs", po::value <std::size_t> (), "Number of unittest jobs to run.")
("unittest-child", "For internal use only. Run the process as a unit test child process.")
#endif
("parameters", po::value< vector<string> > (), "Specify comma separated parameters.")
("quiet,q", "Reduce diagnotics.")
("quorum", po::value <std::size_t> (), "Override the minimum validation quorum.")
("silent", "No output to the console after startup.")
("verbose,v", "Verbose logging.")
("load", "Load the current ledger from the local DB.")
("valid", "Consider the initial ledger a valid network ledger.")
("replay","Replay a ledger close.")
("ledger", po::value<std::string> (), "Load the specified ledger and start from .")
("ledgerfile", po::value<std::string> (), "Load the specified ledger file.")
("start", "Start from a fresh Ledger.")
("net", "Get the initial ledger from the network.")
("debug", "Enable normally suppressed debug logging")
("fg", "Run in the foreground.")
("import", importText.c_str ())
("nodetoshard", "Import node store into shards")
("validateShards", shardsText.c_str ())
("help,h", "Display this message.")
("quorum", po::value <std::size_t> (),
"Override the minimum validation quorum.")
("silent", "No output to the console after startup.")
("standalone,a", "Run with no peers.")
("verbose,v", "Verbose logging.")
("version", "Display the build version.")
;
po::options_description data ("Ledger/Data Options");
data.add_options ()
("import", importText.c_str ())
("ledger", po::value<std::string> (),
"Load the specified ledger and start from the value given.")
("ledgerfile", po::value<std::string> (), "Load the specified ledger file.")
("load", "Load the current ledger from the local DB.")
("net", "Get the initial ledger from the network.")
("nodetoshard", "Import node store into shards")
("replay","Replay a ledger close.")
("start", "Start from a fresh Ledger.")
("valid", "Consider the initial ledger a valid network ledger.")
("validateShards", shardsText.c_str ())
;
po::options_description rpc ("RPC Client Options");
rpc.add_options()
("rpc",
"Perform rpc command - see below for available commands. "
"This is assumed if any positional parameters are provided.")
("rpc_ip", po::value <std::string> (),
"Specify the IP address for RPC command. "
"Format: <ip-address>[':'<port-number>]")
("rpc_port", po::value <std::uint16_t> (),
"Specify the port number for RPC command.")
;
po::options_description test ("Unit Test Options");
test.add_options()
("quiet,q",
"Suppress test suite messages, "
"including suite/case name (at start) and test log messages.")
("unittest,u", po::value <std::string> ()->implicit_value (""),
"Perform unit tests. The optional argument specifies one or "
"more comma-separated selectors. Each selector specifies a suite name, "
"full-name (lib.module.suite), module, or library "
"(checked in that ""order).")
("unittest-arg", po::value <std::string> ()->implicit_value (""),
"Supplies an argument string to unit tests. If provided, this argument "
"is made available to each suite that runs. Interpretation of the "
"argument is handled individually by any suite that accesses it -- "
"as such, it typically only make sense to provide this when running "
"a single suite.")
("unittest-log",
"Force unit test log message output. Only useful in combination with "
"--quiet, in which case log messages will print but suite/case names "
"will not.")
#if HAS_BOOST_PROCESS
("unittest-jobs", po::value <std::size_t> (),
"Number of unittest jobs to run in parallel (child processes).")
#endif
;
// These are hidden options, not intended to be shown in the usage/help message
po::options_description hidden ("Hidden Options");
hidden.add_options()
("parameters", po::value< vector<string> > (),
"Specify rpc command and parameters. This option must be repeated "
"for each command/param. Positional parameters also serve this purpose, "
"so this option is not needed for users")
("unittest-child",
"For internal use only when spawning child unit test processes.")
;
// Interpret positional arguments as --parameters.
po::positional_options_description p;
p.add ("parameters", -1);
po::options_description all;
all.add(gen).add(rpc).add(data).add(test).add(hidden);
po::options_description desc;
desc.add(gen).add(rpc).add(data).add(test);
// Parse options, if no error.
try
{
po::store (po::command_line_parser (argc, argv)
.options (desc) // Parse options.
.options (all) // Parse options.
.positional (p) // Remainder as --parameters.
.run (),
vm);

30
src/test/README.md Normal file
View File

@@ -0,0 +1,30 @@
# Unit Tests
## Running Tests
Unit tests are bundled in the `rippled` executable and can be executed using the
`--unittest` parameter. Without any arguments to this option, all non-manual
unit tests will be executed. If you want to run one or more manual tests, you
must specify it by suite or full-name (e.g. `ripple.app.NoRippleCheckLimits` or
just `NoRippleCheckLimits`).
More than one suite or group of suites can be specified as a comma separated
list via the argument. For example, `--unittest=beast,OversizeMeta` will run
all suites in the `beast` library (root identifier) as well as the test suite
named `OversizeMeta`). All name matches are case sensitive.
Tests can be executed in parallel using several child processes by specifying
the `--unittest-jobs=N` parameter. The default behavior is to execute serially
using a single process.
The order that suites are executed is determined by the suite priority that
is optionally specified when the suite is declared in the code with one of the
`BEAST_DEFINE_TESTSUITE` macros. By default, suites have a priority of 0, and
other suites can choose to declare an integer priority value to make themselves
execute before or after other suites based on their specified priority value.
By default, the framework will emit the name of each testcase/testsuite when it
starts and any messages sent to the suite `log` stream. The `--quiet` option will
suppress both types of messages, but combining `--unittest-log` with `--quiet`
will cause `log` messages to be emitted while suite/case names are suppressed.

View File

@@ -274,7 +274,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(CrossingLimits,tx,ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(CrossingLimits,tx,ripple,10);
} // test
} // ripple

View File

@@ -1322,8 +1322,8 @@ struct Flow_manual_test : public Flow_test
}
};
BEAST_DEFINE_TESTSUITE(Flow,app,ripple);
BEAST_DEFINE_TESTSUITE_MANUAL(Flow_manual,app,ripple);
BEAST_DEFINE_TESTSUITE_PRIO(Flow,app,ripple,2);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Flow_manual,app,ripple,4);
} // test
} // ripple

View File

@@ -4658,8 +4658,8 @@ class Offer_manual_test : public Offer_test
}
};
BEAST_DEFINE_TESTSUITE (Offer, tx, ripple);
BEAST_DEFINE_TESTSUITE_MANUAL (Offer_manual, tx, ripple);
BEAST_DEFINE_TESTSUITE_PRIO (Offer, tx, ripple, 4);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO (Offer_manual, tx, ripple, 20);
} // test
} // ripple

View File

@@ -62,7 +62,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(PlumpBook,tx,ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(PlumpBook,tx,ripple,5);
//------------------------------------------------------------------------------
@@ -121,7 +121,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(OversizeMeta,tx,ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(OversizeMeta,tx,ripple,3);
//------------------------------------------------------------------------------
@@ -189,7 +189,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(FindOversizeCross,tx,ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(FindOversizeCross,tx,ripple,50);
} // test
} // ripple

View File

@@ -852,7 +852,7 @@ struct PayStrandAllPairs_test : public beast::unit_test::suite
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(PayStrandAllPairs, app, ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(PayStrandAllPairs, app, ripple, 12);
struct PayStrand_test : public beast::unit_test::suite
{

View File

@@ -515,7 +515,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE (TrustAndBalance, app, ripple);
BEAST_DEFINE_TESTSUITE_PRIO (TrustAndBalance, app, ripple, 1);
} // ripple

View File

@@ -2801,7 +2801,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(TxQ,app,ripple);
BEAST_DEFINE_TESTSUITE_PRIO(TxQ,app,ripple,1);
}
}

View File

@@ -266,7 +266,7 @@ class DistributedValidators_test : public beast::unit_test::suite
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(DistributedValidators, consensus, ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(DistributedValidators, consensus, ripple, 2);
} // namespace test
} // namespace ripple

View File

@@ -116,7 +116,7 @@ class ScaleFreeSim_test : public beast::unit_test::suite
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(ScaleFreeSim, consensus, ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(ScaleFreeSim, consensus, ripple, 80);
} // namespace test
} // namespace ripple

View File

@@ -441,7 +441,7 @@ struct Directory_test : public beast::unit_test::suite
}
};
BEAST_DEFINE_TESTSUITE(Directory,ledger,ripple);
BEAST_DEFINE_TESTSUITE_PRIO(Directory,ledger,ripple,1);
}
}

View File

@@ -83,14 +83,16 @@ private:
beast::xor_shift_engine gen_;
std::uint8_t prefix_;
std::uniform_int_distribution<std::uint32_t> d_type_;
std::discrete_distribution<std::uint32_t> d_type_;
std::uniform_int_distribution<std::uint32_t> d_size_;
public:
explicit
Sequence(std::uint8_t prefix)
: prefix_ (prefix)
, d_type_ (hotLEDGER, hotTRANSACTION_NODE)
// uniform distribution over hotLEDGER - hotTRANSACTION_NODE
// but exclude hotTRANSACTION = 2 (removed)
, d_type_ ({1, 1, 0, 1, 1})
, d_size_ (minSize, maxSize)
{
}
@@ -744,7 +746,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(Timing,NodeStore,ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(Timing,NodeStore,ripple,1);
}
}

View File

@@ -585,7 +585,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(import,NodeStore,ripple);
BEAST_DEFINE_TESTSUITE_MANUAL(import,NodeStore,ripple);
#endif

View File

@@ -156,6 +156,6 @@ public:
}
};
BEAST_DEFINE_TESTSUITE_MANUAL(digest,ripple_data,ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(digest,ripple_data,ripple,20);
} // ripple

View File

@@ -1647,7 +1647,7 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(Book,app,ripple);
BEAST_DEFINE_TESTSUITE_PRIO(Book,app,ripple,1);
} // test
} // ripple

View File

@@ -448,6 +448,6 @@ public:
}
};
BEAST_DEFINE_TESTSUITE(LedgerData,app,ripple);
BEAST_DEFINE_TESTSUITE_PRIO(LedgerData,app,ripple,1);
}

View File

@@ -17,6 +17,7 @@
*/
//==============================================================================
#include <ripple/app/misc/TxQ.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/JsonFields.h>
#include <test/jtx.h>
@@ -273,9 +274,20 @@ class NoRippleCheckLimits_test : public beast::unit_test::suite
{steady_clock::now()};
}
}
auto& txq = env.app().getTxQ();
auto const gw = Account {"gw" + std::to_string(i)};
env.fund (XRP (1000), gw);
env (trust (alice, gw["USD"](10)));
env.memoize(gw);
env (pay (env.master, gw, XRP(1000)),
seq (autofill),
fee (txq.getMetrics(*env.current())->expFeeLevel + 1),
sig (autofill));
env (fset (gw, asfDefaultRipple),
seq (autofill),
fee (txq.getMetrics(*env.current())->expFeeLevel + 1),
sig (autofill));
env (trust (alice, gw["USD"](10)),
fee (txq.getMetrics(*env.current())->expFeeLevel + 1));
env.close();
}
@@ -328,7 +340,7 @@ BEAST_DEFINE_TESTSUITE(NoRippleCheck, app, ripple);
// offer/account setup, so making them manual -- the additional coverage provided
// by them is minimal
BEAST_DEFINE_TESTSUITE_MANUAL(NoRippleCheckLimits, app, ripple);
BEAST_DEFINE_TESTSUITE_MANUAL_PRIO(NoRippleCheckLimits, app, ripple, 1);
} // ripple

View File

@@ -285,32 +285,6 @@ multi_runner_child::run_multi(Pred pred)
{
auto const& suite = beast::unit_test::global_suites();
auto const num_tests = suite.size();
// actual order to run the tests. Use this to move longer running tests to
// the beginning to better take advantage of a multi process run.
std::vector<std::size_t> order(num_tests);
std::iota(order.begin(), order.end(), 0);
{
std::unordered_set<std::string> prioritize{
"ripple.app.Flow", "ripple.tx.Offer"};
std::vector<std::size_t> to_swap;
to_swap.reserve(prioritize.size());
size_t i = 0;
for (auto const& t : suite)
{
auto const full_name = t.full_name();
if (prioritize.count(full_name))
{
to_swap.push_back(i);
if (to_swap.size() == prioritize.size())
break;
}
++i;
}
for (std::size_t i = 0; i < to_swap.size(); ++i)
std::swap(order[to_swap[i]], order[i]);
}
bool failed = false;
auto get_test = [&]() -> beast::unit_test::suite_info const* {
@@ -318,7 +292,7 @@ multi_runner_child::run_multi(Pred pred)
if (cur_test_index >= num_tests)
return nullptr;
auto iter = suite.begin();
std::advance(iter, order[cur_test_index]);
std::advance(iter, cur_test_index);
return &*iter;
};
while (auto t = get_test())