mirror of
https://github.com/apple/foundationdb.git
synced 2025-05-14 09:58:50 +08:00
Added an fdbserver role to run unit tests directly without a cluster or test spec file, and added a unit test parameters concept for passing options into unit tests.
Updated p2p network test to use unit test parameters instead of the environment.
This commit is contained in:
parent
781255d2c2
commit
eec119e0d0
@ -135,7 +135,7 @@ ACTOR Future<Void> testerServerCore(TesterInterface interf,
|
||||
LocalityData locality);
|
||||
|
||||
enum test_location_t { TEST_HERE, TEST_ON_SERVERS, TEST_ON_TESTERS };
|
||||
enum test_type_t { TEST_TYPE_FROM_FILE, TEST_TYPE_CONSISTENCY_CHECK };
|
||||
enum test_type_t { TEST_TYPE_FROM_FILE, TEST_TYPE_CONSISTENCY_CHECK, TEST_TYPE_UNIT_TESTS };
|
||||
|
||||
ACTOR Future<Void> runTests(Reference<ClusterConnectionFile> connFile,
|
||||
test_type_t whatToRun,
|
||||
|
@ -66,6 +66,7 @@
|
||||
#include "flow/SystemMonitor.h"
|
||||
#include "flow/TLSConfig.actor.h"
|
||||
#include "flow/Tracing.h"
|
||||
#include "flow/UnitTest.h"
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#include <execinfo.h>
|
||||
@ -88,7 +89,7 @@ enum {
|
||||
OPT_CONNFILE, OPT_SEEDCONNFILE, OPT_SEEDCONNSTRING, OPT_ROLE, OPT_LISTEN, OPT_PUBLICADDR, OPT_DATAFOLDER, OPT_LOGFOLDER, OPT_PARENTPID, OPT_TRACER, OPT_NEWCONSOLE,
|
||||
OPT_NOBOX, OPT_TESTFILE, OPT_RESTARTING, OPT_RESTORING, OPT_RANDOMSEED, OPT_KEY, OPT_MEMLIMIT, OPT_STORAGEMEMLIMIT, OPT_CACHEMEMLIMIT, OPT_MACHINEID,
|
||||
OPT_DCID, OPT_MACHINE_CLASS, OPT_BUGGIFY, OPT_VERSION, OPT_BUILD_FLAGS, OPT_CRASHONERROR, OPT_HELP, OPT_NETWORKIMPL, OPT_NOBUFSTDOUT, OPT_BUFSTDOUTERR,
|
||||
OPT_TRACECLOCK, OPT_NUMTESTERS, OPT_DEVHELP, OPT_ROLLSIZE, OPT_MAXLOGS, OPT_MAXLOGSSIZE, OPT_KNOB, OPT_TESTSERVERS, OPT_TEST_ON_SERVERS, OPT_METRICSCONNFILE,
|
||||
OPT_TRACECLOCK, OPT_NUMTESTERS, OPT_DEVHELP, OPT_ROLLSIZE, OPT_MAXLOGS, OPT_MAXLOGSSIZE, OPT_KNOB, OPT_UNITTESTPARAM, OPT_TESTSERVERS, OPT_TEST_ON_SERVERS, OPT_METRICSCONNFILE,
|
||||
OPT_METRICSPREFIX, OPT_LOGGROUP, OPT_LOCALITY, OPT_IO_TRUST_SECONDS, OPT_IO_TRUST_WARN_ONLY, OPT_FILESYSTEM, OPT_PROFILER_RSS_SIZE, OPT_KVFILE,
|
||||
OPT_TRACE_FORMAT, OPT_WHITELIST_BINPATH, OPT_BLOB_CREDENTIAL_FILE
|
||||
};
|
||||
@ -162,6 +163,7 @@ CSimpleOpt::SOption g_rgOptions[] = {
|
||||
{ OPT_HELP, "--help", SO_NONE },
|
||||
{ OPT_DEVHELP, "--dev-help", SO_NONE },
|
||||
{ OPT_KNOB, "--knob_", SO_REQ_SEP },
|
||||
{ OPT_UNITTESTPARAM, "--test_", SO_REQ_SEP },
|
||||
{ OPT_LOCALITY, "--locality_", SO_REQ_SEP },
|
||||
{ OPT_TESTSERVERS, "--testservers", SO_REQ_SEP },
|
||||
{ OPT_TEST_ON_SERVERS, "--testonservers", SO_NONE },
|
||||
@ -622,16 +624,19 @@ static void printUsage(const char* name, bool devhelp) {
|
||||
printOptionUsage("-h, -?, --help", "Display this help and exit.");
|
||||
if (devhelp) {
|
||||
printf(" --build_flags Print build information and exit.\n");
|
||||
printOptionUsage("-r ROLE, --role ROLE",
|
||||
" Server role (valid options are fdbd, test, multitest,"
|
||||
" simulation, networktestclient, networktestserver, restore"
|
||||
" consistencycheck, kvfileintegritycheck, kvfilegeneratesums). The default is `fdbd'.");
|
||||
printOptionUsage(
|
||||
"-r ROLE, --role ROLE",
|
||||
" Server role (valid options are fdbd, test, multitest,"
|
||||
" simulation, networktestclient, networktestserver, restore"
|
||||
" consistencycheck, kvfileintegritycheck, kvfilegeneratesums, unittests). The default is `fdbd'.");
|
||||
#ifdef _WIN32
|
||||
printOptionUsage("-n, --newconsole", " Create a new console.");
|
||||
printOptionUsage("-q, --no_dialog", " Disable error dialog on crash.");
|
||||
printOptionUsage("--parentpid PID", " Specify a process after whose termination to exit.");
|
||||
#endif
|
||||
printOptionUsage("-f TESTFILE, --testfile", " Testfile to run, defaults to `tests/default.txt'.");
|
||||
printOptionUsage("-f TESTFILE, --testfile",
|
||||
" Testfile to run, defaults to `tests/default.txt'. If role is `unittests', specifies which "
|
||||
"unit tests to run as a search prefix.");
|
||||
printOptionUsage("-R, --restarting", " Restart a previous simulation that was cleanly shut down.");
|
||||
printOptionUsage("-s SEED, --seed SEED", " Random seed.");
|
||||
printOptionUsage("-k KEY, --key KEY", "Target key for search role.");
|
||||
@ -651,6 +656,8 @@ static void printUsage(const char* name, bool devhelp) {
|
||||
printOptionUsage("--num_testers NUM",
|
||||
" A multitester will wait for NUM testers before starting"
|
||||
" (defaults to 1).");
|
||||
printOptionUsage("--test_PARAMNAME PARAMVALUE",
|
||||
" Set a UnitTest named parameter to the given value. Names are case sensitive.");
|
||||
#ifdef __linux__
|
||||
printOptionUsage("--rsssize SIZE",
|
||||
" Turns on automatic heap profiling when RSS memory size exceeds"
|
||||
@ -922,6 +929,7 @@ enum class ServerRole {
|
||||
SkipListTest,
|
||||
Test,
|
||||
VersionedMapTest,
|
||||
UnitTests
|
||||
};
|
||||
struct CLIOptions {
|
||||
std::string commandLine;
|
||||
@ -1044,6 +1052,15 @@ private:
|
||||
knobs.push_back(std::make_pair(syn, args.OptionArg()));
|
||||
break;
|
||||
}
|
||||
case OPT_UNITTESTPARAM: {
|
||||
std::string syn = args.OptionSyntax();
|
||||
if (!StringRef(syn).startsWith(LiteralStringRef("--test_"))) {
|
||||
fprintf(stderr, "ERROR: unable to parse knob option '%s'\n", syn.c_str());
|
||||
flushAndExit(FDB_EXIT_ERROR);
|
||||
}
|
||||
UnitTestCollection::setParam(syn.substr(7), args.OptionArg());
|
||||
break;
|
||||
}
|
||||
case OPT_LOCALITY: {
|
||||
std::string syn = args.OptionSyntax();
|
||||
if (!StringRef(syn).startsWith(LiteralStringRef("--locality_"))) {
|
||||
@ -1102,6 +1119,8 @@ private:
|
||||
role = ServerRole::KVFileGenerateIOLogChecksums;
|
||||
else if (!strcmp(sRole, "consistencycheck"))
|
||||
role = ServerRole::ConsistencyCheck;
|
||||
else if (!strcmp(sRole, "unittests"))
|
||||
role = ServerRole::UnitTests;
|
||||
else {
|
||||
fprintf(stderr, "ERROR: Unknown role `%s'\n", sRole);
|
||||
printHelpTeaser(argv[0]);
|
||||
@ -1461,7 +1480,8 @@ private:
|
||||
return StringRef(addr).startsWith(LiteralStringRef("auto:"));
|
||||
});
|
||||
if ((role != ServerRole::Simulation && role != ServerRole::CreateTemplateDatabase &&
|
||||
role != ServerRole::KVFileIntegrityCheck && role != ServerRole::KVFileGenerateIOLogChecksums) ||
|
||||
role != ServerRole::KVFileIntegrityCheck && role != ServerRole::KVFileGenerateIOLogChecksums &&
|
||||
role != ServerRole::UnitTests) ||
|
||||
autoPublicAddress) {
|
||||
|
||||
if (seedSpecified && !fileExists(connFile)) {
|
||||
@ -1994,6 +2014,12 @@ int main(int argc, char* argv[]) {
|
||||
StringRef(),
|
||||
opts.localities));
|
||||
g_network->run();
|
||||
} else if (role == ServerRole::UnitTests) {
|
||||
setupRunLoopProfiler();
|
||||
auto m = startSystemMonitor(opts.dataFolder, opts.dcId, opts.zoneId, opts.zoneId);
|
||||
f = stopAfter(runTests(
|
||||
opts.connectionFile, TEST_TYPE_UNIT_TESTS, TEST_HERE, 1, opts.testFile, StringRef(), opts.localities));
|
||||
g_network->run();
|
||||
} else if (role == ServerRole::CreateTemplateDatabase) {
|
||||
createTemplateDatabase();
|
||||
} else if (role == ServerRole::NetworkTestClient) {
|
||||
|
@ -517,13 +517,6 @@ struct P2PNetworkTest {
|
||||
self->listeners.size(),
|
||||
self->remotes.size(),
|
||||
self->connectionsOut);
|
||||
printf("Request size: %s\n", self->requestBytes.toString().c_str());
|
||||
printf("Response size: %s\n", self->replyBytes.toString().c_str());
|
||||
printf("Requests per outgoing session: %d\n", self->requests.toString().c_str());
|
||||
printf("Delay before socket read: %s\n", self->waitReadMilliseconds.toString().c_str());
|
||||
printf("Delay before socket write: %s\n", self->waitWriteMilliseconds.toString().c_str());
|
||||
printf("Delay before session close: %s\n", self->idleMilliseconds.toString().c_str());
|
||||
printf("Send/Recv size %d bytes\n", FLOW_KNOBS->MAX_PACKET_SEND_BYTES);
|
||||
|
||||
for (auto n : self->remotes) {
|
||||
printf("Remote: %s\n", n.toString().c_str());
|
||||
@ -534,6 +527,19 @@ struct P2PNetworkTest {
|
||||
actors.add(incoming(self, el));
|
||||
}
|
||||
|
||||
printf("Request size: %s\n", self->requestBytes.toString().c_str());
|
||||
printf("Response size: %s\n", self->replyBytes.toString().c_str());
|
||||
printf("Requests per outgoing session: %s\n", self->requests.toString().c_str());
|
||||
printf("Delay before socket read: %s\n", self->waitReadMilliseconds.toString().c_str());
|
||||
printf("Delay before socket write: %s\n", self->waitWriteMilliseconds.toString().c_str());
|
||||
printf("Delay before session close: %s\n", self->idleMilliseconds.toString().c_str());
|
||||
printf("Send/Recv size %d bytes\n", FLOW_KNOBS->MAX_PACKET_SEND_BYTES);
|
||||
|
||||
if ((self->remotes.empty() || self->connectionsOut == 0) && self->listeners.empty()) {
|
||||
printf("No listeners and no remotes or connectionsOut, so there is nothing to do!\n");
|
||||
ASSERT((!self->remotes.empty() && (self->connectionsOut > 0)) || !self->listeners.empty());
|
||||
}
|
||||
|
||||
if (!self->remotes.empty()) {
|
||||
for (int i = 0; i < self->connectionsOut; ++i) {
|
||||
actors.add(outgoing(self));
|
||||
@ -549,27 +555,30 @@ struct P2PNetworkTest {
|
||||
Future<Void> run() { return run_impl(this); }
|
||||
};
|
||||
|
||||
int getEnvInt(const char* name, int defaultValue = 0) {
|
||||
const char* val = getenv(name);
|
||||
return val != nullptr ? atol(val) : defaultValue;
|
||||
}
|
||||
|
||||
std::string getEnvStr(const char* name, std::string defaultValue = "") {
|
||||
const char* val = getenv(name);
|
||||
return val != nullptr ? val : defaultValue;
|
||||
}
|
||||
|
||||
// TODO: Remove this hacky thing and make a "networkp2ptest" role in fdbserver
|
||||
// Peer-to-Peer network test.
|
||||
// One or more instances can be run and set to talk to each other.
|
||||
// Each instance
|
||||
// - listens on 0 or more listenerAddresses
|
||||
// - maintains 0 or more connectionsOut at a time, each to a random choice from remoteAddresses
|
||||
// Address lists are a string of comma-separated IP:port[:tls] strings.
|
||||
//
|
||||
// The other arguments can be specified as "fixedValue" or "minValue:maxValue".
|
||||
// Each outgoing connection will live for a random requests count.
|
||||
// Each request will
|
||||
// - send a random requestBytes sized message
|
||||
// - wait for a random replyBytes sized response.
|
||||
// The client will close the connection after a random idleMilliseconds.
|
||||
// Reads and writes can optionally preceded by random delays, waitReadMilliseconds and waitWriteMilliseconds.
|
||||
TEST_CASE("!p2ptest") {
|
||||
state P2PNetworkTest p2p(getEnvStr("listenerAddresses", ""),
|
||||
getEnvStr("remoteAddresses", ""),
|
||||
getEnvInt("connectionsOut", 0),
|
||||
getEnvStr("requestBytes", "0"),
|
||||
getEnvStr("replyBytes", "0"),
|
||||
getEnvStr("requests", "0"),
|
||||
getEnvStr("idleMilliseconds", "0"),
|
||||
getEnvStr("waitReadMilliseconds", "0"),
|
||||
getEnvStr("waitWriteMilliseconds", "0"));
|
||||
state P2PNetworkTest p2p(UnitTestCollection::getParam("listenerAddresses").orDefault(""),
|
||||
UnitTestCollection::getParam("remoteAddresses").orDefault(""),
|
||||
UnitTestCollection::getIntParam("connectionsOut").orDefault(1),
|
||||
UnitTestCollection::getParam("requestBytes").orDefault("50:100"),
|
||||
UnitTestCollection::getParam("replyBytes").orDefault("500:1000"),
|
||||
UnitTestCollection::getParam("requests").orDefault("10:10000"),
|
||||
UnitTestCollection::getParam("idleMilliseconds").orDefault("0"),
|
||||
UnitTestCollection::getParam("waitReadMilliseconds").orDefault("0"),
|
||||
UnitTestCollection::getParam("waitWriteMilliseconds").orDefault("0"));
|
||||
|
||||
wait(p2p.run());
|
||||
return Void();
|
||||
|
@ -763,7 +763,7 @@ ACTOR Future<DistributedTestResults> runWorkload(Database cx, std::vector<Tester
|
||||
req.title = spec.title;
|
||||
req.useDatabase = spec.useDB;
|
||||
req.timeout = spec.timeout;
|
||||
req.databasePingDelay = spec.databasePingDelay;
|
||||
req.databasePingDelay = spec.useDB ? spec.databasePingDelay : 0.0;
|
||||
req.options = spec.options;
|
||||
req.clientId = i;
|
||||
req.clientCount = testers.size();
|
||||
@ -1577,8 +1577,10 @@ ACTOR Future<Void> runTests(Reference<ClusterConnectionFile> connFile,
|
||||
auto cc = makeReference<AsyncVar<Optional<ClusterControllerFullInterface>>>();
|
||||
auto ci = makeReference<AsyncVar<Optional<ClusterInterface>>>();
|
||||
vector<Future<Void>> actors;
|
||||
actors.push_back(reportErrors(monitorLeader(connFile, cc), "MonitorLeader"));
|
||||
actors.push_back(reportErrors(extractClusterInterface(cc, ci), "ExtractClusterInterface"));
|
||||
if (connFile) {
|
||||
actors.push_back(reportErrors(monitorLeader(connFile, cc), "MonitorLeader"));
|
||||
actors.push_back(reportErrors(extractClusterInterface(cc, ci), "ExtractClusterInterface"));
|
||||
}
|
||||
|
||||
if (whatToRun == TEST_TYPE_CONSISTENCY_CHECK) {
|
||||
TestSpec spec;
|
||||
@ -1603,6 +1605,18 @@ ACTOR Future<Void> runTests(Reference<ClusterConnectionFile> connFile,
|
||||
KeyValueRef(LiteralStringRef("shuffleShards"), LiteralStringRef("true")));
|
||||
spec.options.push_back_deep(spec.options.arena(), options);
|
||||
testSpecs.push_back(spec);
|
||||
} else if (whatToRun == TEST_TYPE_UNIT_TESTS) {
|
||||
TestSpec spec;
|
||||
Standalone<VectorRef<KeyValueRef>> options;
|
||||
spec.title = LiteralStringRef("UnitTests");
|
||||
spec.startDelay = 0;
|
||||
spec.useDB = false;
|
||||
spec.timeout = 0;
|
||||
options.push_back_deep(options.arena(),
|
||||
KeyValueRef(LiteralStringRef("testName"), LiteralStringRef("UnitTests")));
|
||||
options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("testsMatching"), fileName));
|
||||
spec.options.push_back_deep(spec.options.arena(), options);
|
||||
testSpecs.push_back(spec);
|
||||
} else {
|
||||
ifstream ifs;
|
||||
ifs.open(fileName.c_str(), ifstream::in);
|
||||
|
@ -26,3 +26,33 @@ UnitTest::UnitTest(const char* name, const char* file, int line, TestFunction fu
|
||||
: name(name), file(file), line(line), func(func), next(g_unittests.tests) {
|
||||
g_unittests.tests = this;
|
||||
}
|
||||
|
||||
UnitTestParameters& UnitTestCollection::params() {
|
||||
static UnitTestParameters p;
|
||||
return p;
|
||||
}
|
||||
|
||||
void UnitTestCollection::setParam(const std::string& name, const std::string& value) {
|
||||
printf("setting %s = %s\n", name.c_str(), value.c_str());
|
||||
params()[name] = value;
|
||||
}
|
||||
|
||||
Optional<std::string> UnitTestCollection::getParam(const std::string& name) {
|
||||
auto it = params().find(name);
|
||||
if (it != params().end()) {
|
||||
return it->second;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void UnitTestCollection::setParam(const std::string& name, int64_t value) {
|
||||
setParam(name, format("%" PRId64, value));
|
||||
};
|
||||
|
||||
Optional<int64_t> UnitTestCollection::getIntParam(const std::string& name) {
|
||||
auto opt = getParam(name);
|
||||
if (opt.present()) {
|
||||
return atoll(opt.get().c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -45,6 +45,9 @@
|
||||
|
||||
#include "flow/flow.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
// Unit test definition structured as a linked list item
|
||||
struct UnitTest {
|
||||
typedef Future<Void> (*TestFunction)();
|
||||
|
||||
@ -57,8 +60,25 @@ struct UnitTest {
|
||||
UnitTest(const char* name, const char* file, int line, TestFunction func);
|
||||
};
|
||||
|
||||
// Collection of unit tests in the form of a linked list
|
||||
typedef std::map<std::string, std::string> UnitTestParameters;
|
||||
struct UnitTestCollection {
|
||||
UnitTest* tests;
|
||||
|
||||
// Map of named case-sensitive parameters available for all unit tests
|
||||
static UnitTestParameters& params();
|
||||
|
||||
// Set a named parameter to a string value, replacing any existing value
|
||||
static void setParam(const std::string& name, const std::string& value);
|
||||
|
||||
// Set a named parameter to an integer converted to a string value, replacing any existing value
|
||||
static void setParam(const std::string& name, int64_t value);
|
||||
|
||||
// Get a parameter's value, will return !present() if parameter was not set
|
||||
static Optional<std::string> getParam(const std::string& name);
|
||||
|
||||
// Get a parameter's value as an integer, will return !present() if parameter was not set
|
||||
static Optional<int64_t> getIntParam(const std::string& name);
|
||||
};
|
||||
|
||||
extern UnitTestCollection g_unittests;
|
||||
|
Loading…
x
Reference in New Issue
Block a user