Skip to content

Commit 0131f0d

Browse files
committed
Make simulation able to load and run .toml files
1 parent 464e933 commit 0131f0d

File tree

2 files changed

+104
-15
lines changed

2 files changed

+104
-15
lines changed

fdbserver/fdbserver.actor.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -697,10 +697,12 @@ Optional<bool> checkBuggifyOverride(const char *testFile) {
697697
std::string value = removeWhitespace(line.substr(found + 1));
698698

699699
if (attrib == "buggify") {
700-
if( !strcmp( value.c_str(), "on" ) ) {
700+
// Testspec uses `on` or `off` (without quotes).
701+
// TOML uses literal `true` and `false`.
702+
if( !strcmp( value.c_str(), "on" ) || !strcmp( value.c_str(), "true" ) ) {
701703
ifs.close();
702704
return true;
703-
} else if( !strcmp( value.c_str(), "off" ) ) {
705+
} else if( !strcmp( value.c_str(), "off" ) || !strcmp( value.c_str(), "false" )) {
704706
ifs.close();
705707
return false;
706708
} else {

fdbserver/tester.actor.cpp

+100-13
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <fstream>
2323
#include <functional>
2424
#include <map>
25+
#include <boost/algorithm/string/predicate.hpp>
2526
#include <toml.hpp>
2627
#include "flow/ActorCollection.h"
2728
#include "fdbrpc/sim_validation.h"
@@ -875,29 +876,29 @@ std::vector<TestSpec> readTOMLTests( ifstream& ifs ) {
875876
return {};
876877
}
877878

878-
std::map<std::string, std::function<void(const std::string&, TestSpec*)>> testSpecGlobalKeys = {
879+
std::map<std::string, std::function<void(const std::string&)>> testSpecGlobalKeys = {
879880
// These are read by SimulatedCluster and used before testers exist. Thus, they must
880881
// be recognized and accepted, but there's no point in placing them into a testSpec.
881-
{"extraDB", [](const std::string& value, TestSpec* spec) {
882+
{"extraDB", [](const std::string& value) {
882883
TraceEvent("TestParserTest").detail("ParsedExtraDB", "");
883884
}},
884-
{"configureLocked", [](const std::string& value, TestSpec* spec) {
885+
{"configureLocked", [](const std::string& value) {
885886
TraceEvent("TestParserTest").detail("ParsedConfigureLocked", "");
886887
}},
887-
{"minimumReplication", [](const std::string& value, TestSpec* spec) {
888+
{"minimumReplication", [](const std::string& value) {
888889
TraceEvent("TestParserTest").detail("ParsedMinimumReplication", "");
889890
}},
890-
{"minimumRegions", [](const std::string& value, TestSpec* spec) {
891+
{"minimumRegions", [](const std::string& value) {
891892
TraceEvent("TestParserTest").detail("ParsedMinimumRegions", "");
892893
}},
893-
{"buggify", [](const std::string& value, TestSpec* spec) {
894+
{"buggify", [](const std::string& value) {
894895
TraceEvent("TestParserTest").detail("ParsedBuggify", "");
895896
}},
896897
// The test harness handles NewSeverity events specially.
897-
{"StderrSeverity", [](const std::string& value, TestSpec* spec) {
898+
{"StderrSeverity", [](const std::string& value) {
898899
TraceEvent("StderrSeverity").detail("NewSeverity", value);
899900
}},
900-
{"ClientInfoLogging", [](const std::string& value, TestSpec* spec) {
901+
{"ClientInfoLogging", [](const std::string& value) {
901902
if (value == "false") {
902903
setNetworkOption(FDBNetworkOptions::DISABLE_CLIENT_STATISTICS_LOGGING);
903904
}
@@ -907,6 +908,10 @@ std::map<std::string, std::function<void(const std::string&, TestSpec*)>> testSp
907908
};
908909

909910
std::map<std::string, std::function<void(const std::string& value, TestSpec* spec)>> testSpecTestKeys = {
911+
{ "testTitle", [](const std::string& value, TestSpec* spec) {
912+
spec->title = value;
913+
TraceEvent("TestParserTest").detail("ParsedTest", spec->title );
914+
}},
910915
{ "timeout", [](const std::string& value, TestSpec* spec) {
911916
sscanf( value.c_str(), "%d", &(spec->timeout) );
912917
ASSERT( spec->timeout > 0 );
@@ -1034,14 +1039,13 @@ vector<TestSpec> readTests( ifstream& ifs ) {
10341039
spec = TestSpec();
10351040
}
10361041

1037-
spec.title = StringRef( value );
1038-
TraceEvent("TestParserTest").detail("ParsedTest", spec.title );
1042+
testSpecTestKeys[attrib](value, &spec);
10391043
} else if ( testSpecTestKeys.find(attrib) != testSpecTestKeys.end() ) {
10401044
if (parsingWorkloads) TraceEvent(SevError, "TestSpecTestParamInWorkload").detail("Attrib", attrib).detail("Value", value);
10411045
testSpecTestKeys[attrib](value, &spec);
10421046
} else if ( testSpecGlobalKeys.find(attrib) != testSpecGlobalKeys.end() ) {
10431047
if (!beforeFirstTest) TraceEvent(SevError, "TestSpecGlobalParamInTest").detail("Attrib", attrib).detail("Value", value);
1044-
testSpecGlobalKeys[attrib](value, &spec);
1048+
testSpecGlobalKeys[attrib](value);
10451049
}
10461050
else {
10471051
if( attrib == "testName" ) {
@@ -1067,6 +1071,85 @@ vector<TestSpec> readTests( ifstream& ifs ) {
10671071
return result;
10681072
}
10691073

1074+
template <typename T>
1075+
std::string toml_to_string(const T& value) {
1076+
// TOML formatting converts numbers to strings exactly how they're in the file
1077+
// and thus, is equivalent to testspec. However, strings are quoted, so we
1078+
// must remove the quotes.
1079+
if (value.type() == toml::value_t::string) {
1080+
const std::string& formatted = toml::format(value);
1081+
return formatted.substr(1, formatted.size()-2);
1082+
} else {
1083+
return toml::format(value);
1084+
}
1085+
}
1086+
1087+
1088+
std::vector<TestSpec> readTOMLTests_( std::string fileName ) {
1089+
TestSpec spec;
1090+
Standalone< VectorRef< KeyValueRef > > workloadOptions;
1091+
std::vector<TestSpec> result;
1092+
1093+
const toml::value& conf = toml::parse(fileName);
1094+
1095+
// Handle all global settings
1096+
for (const auto& [k, v] : conf.as_table()) {
1097+
if (k == "test") {
1098+
continue;
1099+
}
1100+
if (testSpecGlobalKeys.find(k) != testSpecGlobalKeys.end()) {
1101+
testSpecGlobalKeys[k](toml_to_string(v));
1102+
}
1103+
}
1104+
1105+
// Then parse each test
1106+
const toml::array& tests = toml::find(conf, "test").as_array();
1107+
for (const toml::value& test : tests) {
1108+
spec = TestSpec();
1109+
1110+
// First handle all test-level settings
1111+
for (const auto& [k, v] : test.as_table()) {
1112+
if (k == "workload") {
1113+
continue;
1114+
}
1115+
if (testSpecTestKeys.find(k) != testSpecTestKeys.end()) {
1116+
testSpecTestKeys[k](toml_to_string(v), &spec);
1117+
}
1118+
}
1119+
1120+
// And then copy the workload attributes to spec.options
1121+
const toml::array& workloads = toml::find(test, "workload").as_array();
1122+
for (const toml::value& workload : workloads) {
1123+
workloadOptions = Standalone< VectorRef< KeyValueRef > >();
1124+
TraceEvent("TestParserFlush").detail("Reason", "new (compound) test");
1125+
for (const auto& [attrib, v] : workload.as_table()) {
1126+
const std::string& value = toml_to_string(v);
1127+
workloadOptions.push_back_deep( workloadOptions.arena(),
1128+
KeyValueRef( StringRef( attrib ), StringRef( value ) ) );
1129+
TraceEvent("TestParserOption").detail("ParsedKey", attrib).detail("ParsedValue", value);
1130+
}
1131+
spec.options.push_back_deep( spec.options.arena(), workloadOptions );
1132+
}
1133+
1134+
result.push_back(spec);
1135+
}
1136+
1137+
return result;
1138+
}
1139+
1140+
// A hack to catch and log std::exception, because TOML11 has very useful
1141+
// error messages, but the actor framework can't handle std::exception.
1142+
std::vector<TestSpec> readTOMLTests( std::string fileName ) {
1143+
try {
1144+
return readTOMLTests_( fileName );
1145+
} catch (std::exception &e) {
1146+
std::cerr << e.what() << std::endl;
1147+
TraceEvent("TOMLParseError").detail("Error", printable(e.what()));
1148+
// TODO: replace with toml_parse_error();
1149+
throw unknown_error();
1150+
}
1151+
}
1152+
10701153
ACTOR Future<Void> monitorServerDBInfo(Reference<AsyncVar<Optional<ClusterControllerFullInterface>>> ccInterface,
10711154
LocalityData locality,
10721155
Reference<AsyncVar<ServerDBInfo>> dbInfo) {
@@ -1259,11 +1342,15 @@ ACTOR Future<Void> runTests( Reference<ClusterConnectionFile> connFile, test_typ
12591342
ifs.open( fileName.c_str(), ifstream::in );
12601343
if( !ifs.good() ) {
12611344
TraceEvent(SevError, "TestHarnessFail").detail("Reason", "file open failed").detail("File", fileName.c_str());
1262-
fprintf(stderr, "ERROR: Could not open test spec file `%s'\n", fileName.c_str());
1345+
fprintf(stderr, "ERROR: Could not open file `%s'\n", fileName.c_str());
12631346
return Void();
12641347
}
12651348
enableClientInfoLogging(); // Enable Client Info logging by default for tester
1266-
testSpecs = readTests( ifs );
1349+
if ( boost::algorithm::ends_with(fileName, ".txt") ) {
1350+
testSpecs = readTests( ifs );
1351+
} else if ( boost::algorithm::ends_with(fileName, ".toml") ) {
1352+
testSpecs = readTOMLTests( fileName );
1353+
}
12671354
ifs.close();
12681355
}
12691356

0 commit comments

Comments
 (0)