22
22
#include < fstream>
23
23
#include < functional>
24
24
#include < map>
25
+ #include < boost/algorithm/string/predicate.hpp>
25
26
#include < toml.hpp>
26
27
#include " flow/ActorCollection.h"
27
28
#include " fdbrpc/sim_validation.h"
@@ -875,29 +876,29 @@ std::vector<TestSpec> readTOMLTests( ifstream& ifs ) {
875
876
return {};
876
877
}
877
878
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 = {
879
880
// These are read by SimulatedCluster and used before testers exist. Thus, they must
880
881
// 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) {
882
883
TraceEvent (" TestParserTest" ).detail (" ParsedExtraDB" , " " );
883
884
}},
884
- {" configureLocked" , [](const std::string& value, TestSpec* spec ) {
885
+ {" configureLocked" , [](const std::string& value) {
885
886
TraceEvent (" TestParserTest" ).detail (" ParsedConfigureLocked" , " " );
886
887
}},
887
- {" minimumReplication" , [](const std::string& value, TestSpec* spec ) {
888
+ {" minimumReplication" , [](const std::string& value) {
888
889
TraceEvent (" TestParserTest" ).detail (" ParsedMinimumReplication" , " " );
889
890
}},
890
- {" minimumRegions" , [](const std::string& value, TestSpec* spec ) {
891
+ {" minimumRegions" , [](const std::string& value) {
891
892
TraceEvent (" TestParserTest" ).detail (" ParsedMinimumRegions" , " " );
892
893
}},
893
- {" buggify" , [](const std::string& value, TestSpec* spec ) {
894
+ {" buggify" , [](const std::string& value) {
894
895
TraceEvent (" TestParserTest" ).detail (" ParsedBuggify" , " " );
895
896
}},
896
897
// The test harness handles NewSeverity events specially.
897
- {" StderrSeverity" , [](const std::string& value, TestSpec* spec ) {
898
+ {" StderrSeverity" , [](const std::string& value) {
898
899
TraceEvent (" StderrSeverity" ).detail (" NewSeverity" , value);
899
900
}},
900
- {" ClientInfoLogging" , [](const std::string& value, TestSpec* spec ) {
901
+ {" ClientInfoLogging" , [](const std::string& value) {
901
902
if (value == " false" ) {
902
903
setNetworkOption (FDBNetworkOptions::DISABLE_CLIENT_STATISTICS_LOGGING);
903
904
}
@@ -907,6 +908,10 @@ std::map<std::string, std::function<void(const std::string&, TestSpec*)>> testSp
907
908
};
908
909
909
910
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
+ }},
910
915
{ " timeout" , [](const std::string& value, TestSpec* spec) {
911
916
sscanf ( value.c_str (), " %d" , &(spec->timeout ) );
912
917
ASSERT ( spec->timeout > 0 );
@@ -1034,14 +1039,13 @@ vector<TestSpec> readTests( ifstream& ifs ) {
1034
1039
spec = TestSpec ();
1035
1040
}
1036
1041
1037
- spec.title = StringRef ( value );
1038
- TraceEvent (" TestParserTest" ).detail (" ParsedTest" , spec.title );
1042
+ testSpecTestKeys[attrib](value, &spec);
1039
1043
} else if ( testSpecTestKeys.find (attrib) != testSpecTestKeys.end () ) {
1040
1044
if (parsingWorkloads) TraceEvent (SevError, " TestSpecTestParamInWorkload" ).detail (" Attrib" , attrib).detail (" Value" , value);
1041
1045
testSpecTestKeys[attrib](value, &spec);
1042
1046
} else if ( testSpecGlobalKeys.find (attrib) != testSpecGlobalKeys.end () ) {
1043
1047
if (!beforeFirstTest) TraceEvent (SevError, " TestSpecGlobalParamInTest" ).detail (" Attrib" , attrib).detail (" Value" , value);
1044
- testSpecGlobalKeys[attrib](value, &spec );
1048
+ testSpecGlobalKeys[attrib](value);
1045
1049
}
1046
1050
else {
1047
1051
if ( attrib == " testName" ) {
@@ -1067,6 +1071,85 @@ vector<TestSpec> readTests( ifstream& ifs ) {
1067
1071
return result;
1068
1072
}
1069
1073
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
+
1070
1153
ACTOR Future<Void> monitorServerDBInfo (Reference<AsyncVar<Optional<ClusterControllerFullInterface>>> ccInterface,
1071
1154
LocalityData locality,
1072
1155
Reference<AsyncVar<ServerDBInfo>> dbInfo) {
@@ -1259,11 +1342,15 @@ ACTOR Future<Void> runTests( Reference<ClusterConnectionFile> connFile, test_typ
1259
1342
ifs.open ( fileName.c_str (), ifstream::in );
1260
1343
if ( !ifs.good () ) {
1261
1344
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 ());
1263
1346
return Void ();
1264
1347
}
1265
1348
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
+ }
1267
1354
ifs.close ();
1268
1355
}
1269
1356
0 commit comments