diff --git a/fdbserver/tester.actor.cpp b/fdbserver/tester.actor.cpp index 710ad420d7..10aefd4e96 100644 --- a/fdbserver/tester.actor.cpp +++ b/fdbserver/tester.actor.cpp @@ -1306,17 +1306,58 @@ std::string toml_to_string(const T& value) { } } -std::vector readTOMLTests_(std::string fileName) { - TestSpec spec; +struct TestSet { + KnobKeyValuePairs overrideKnobs; + std::vector testSpecs; +}; + +namespace { + +// In the current TOML scope, look for "knobs" field. If exists, translate all +// key value pairs into KnobKeyValuePairs +KnobKeyValuePairs getOverriddenKnobKeyValues(const toml::value& context) { + KnobKeyValuePairs result; + + try { + const toml::array& overrideKnobs = toml::find(context, "knobs").as_array(); + for (const toml::value& knob : overrideKnobs) { + for (const auto& [key, value_] : knob.as_table()) { + const std::string& value = toml_to_string(value_); + ParsedKnobValue parsedValue = CLIENT_KNOBS->parseKnobValue(key, value); + if (std::get_if(&parsedValue)) { + parsedValue = SERVER_KNOBS->parseKnobValue(key, value); + } + if (std::get_if(&parsedValue)) { + TraceEvent(SevError, "TestSpecUnrecognizedKnob") + .detail("KnobName", key) + .detail("OverrideValue", value); + continue; + } + result.set(key, parsedValue); + } + } + } catch (const std::out_of_range&) { + // No knobs field in this scope, this is not an error + } + + return result; +} + +} // namespace + +TestSet readTOMLTests_(std::string fileName) { Standalone> workloadOptions; - std::vector result; + TestSet result; const toml::value& conf = toml::parse(fileName); + // Parse the global knob changes + result.overrideKnobs = getOverriddenKnobKeyValues(conf); + // Then parse each test const toml::array& tests = toml::find(conf, "test").as_array(); for (const toml::value& test : tests) { - spec = TestSpec(); + TestSpec spec; // First handle all test-level settings for (const auto& [k, v] : test.as_table()) { @@ -1347,29 +1388,9 @@ std::vector readTOMLTests_(std::string fileName) { } // And then copy the knob attributes to spec.overrideKnobs - try { - const toml::array& overrideKnobs = toml::find(test, "knobs").as_array(); - for (const toml::value& knob : overrideKnobs) { - for (const auto& [key, value_] : knob.as_table()) { - const std::string& value = toml_to_string(value_); - ParsedKnobValue parsedValue = CLIENT_KNOBS->parseKnobValue(key, value); - if (std::get_if(&parsedValue)) { - parsedValue = SERVER_KNOBS->parseKnobValue(key, value); - } - if (std::get_if(&parsedValue)) { - TraceEvent(SevError, "TestSpecUnrecognizedKnob") - .detail("KnobName", key) - .detail("OverrideValue", value); - continue; - } - spec.overrideKnobs.set(key, parsedValue); - } - } - } catch (const std::out_of_range&) { - // no knob overridden - } + spec.overrideKnobs = getOverriddenKnobKeyValues(test); - result.push_back(spec); + result.testSpecs.push_back(spec); } return result; @@ -1377,7 +1398,7 @@ std::vector readTOMLTests_(std::string fileName) { // A hack to catch and log std::exception, because TOML11 has very useful // error messages, but the actor framework can't handle std::exception. -std::vector readTOMLTests(std::string fileName) { +TestSet readTOMLTests(std::string fileName) { try { return readTOMLTests_(fileName); } catch (std::exception& e) { @@ -1680,7 +1701,8 @@ ACTOR Future runTests(Reference connRecord, LocalityData locality, UnitTestParameters testOptions, Optional defaultTenant) { - state std::vector testSpecs; + state TestSet testSet; + state std::unique_ptr knobProtectiveGroup(nullptr); auto cc = makeReference>>(); auto ci = makeReference>>(); std::vector> actors; @@ -1711,7 +1733,7 @@ ACTOR Future runTests(Reference connRecord, options.push_back_deep(options.arena(), KeyValueRef(LiteralStringRef("shuffleShards"), LiteralStringRef("true"))); spec.options.push_back_deep(spec.options.arena(), options); - testSpecs.push_back(spec); + testSet.testSpecs.push_back(spec); } else if (whatToRun == TEST_TYPE_UNIT_TESTS) { TestSpec spec; Standalone> options; @@ -1727,7 +1749,7 @@ ACTOR Future runTests(Reference connRecord, options.push_back_deep(options.arena(), KeyValueRef(kv.first, kv.second)); } spec.options.push_back_deep(spec.options.arena(), options); - testSpecs.push_back(spec); + testSet.testSpecs.push_back(spec); } else { std::ifstream ifs; ifs.open(fileName.c_str(), std::ifstream::in); @@ -1740,11 +1762,11 @@ ACTOR Future runTests(Reference connRecord, } enableClientInfoLogging(); // Enable Client Info logging by default for tester if (boost::algorithm::ends_with(fileName, ".txt")) { - testSpecs = readTests(ifs); + testSet.testSpecs = readTests(ifs); } else if (boost::algorithm::ends_with(fileName, ".toml")) { // TOML is weird about opening the file as binary on windows, so we // just let TOML re-open the file instead of using ifs. - testSpecs = readTOMLTests(fileName); + testSet = readTOMLTests(fileName); } else { TraceEvent(SevError, "TestHarnessFail") .detail("Reason", "unknown tests specification extension") @@ -1754,6 +1776,7 @@ ACTOR Future runTests(Reference connRecord, ifs.close(); } + knobProtectiveGroup = std::make_unique(testSet.overrideKnobs); Future tests; if (at == TEST_HERE) { auto db = makeReference>(); @@ -1761,10 +1784,10 @@ ACTOR Future runTests(Reference connRecord, actors.push_back( reportErrors(monitorServerDBInfo(cc, LocalityData(), db), "MonitorServerDBInfo")); // FIXME: Locality actors.push_back(reportErrors(testerServerCore(iTesters[0], connRecord, db, locality), "TesterServerCore")); - tests = runTests(cc, ci, iTesters, testSpecs, startingConfiguration, locality, defaultTenant); + tests = runTests(cc, ci, iTesters, testSet.testSpecs, startingConfiguration, locality, defaultTenant); } else { tests = reportErrors( - runTests(cc, ci, testSpecs, at, minTestersExpected, startingConfiguration, locality, defaultTenant), + runTests(cc, ci, testSet.testSpecs, at, minTestersExpected, startingConfiguration, locality, defaultTenant), "RunTests"); }