diff options
author | Shaun Verch <shaun.verch@10gen.com> | 2013-08-15 11:59:46 -0400 |
---|---|---|
committer | Shaun Verch <shaun.verch@10gen.com> | 2013-09-05 13:49:33 -0400 |
commit | 5ee03db5cdc40ecc0a28aaad8031df177188ffee (patch) | |
tree | 8199d2184bab251c6f3d260cc461c9c92f06ef48 /src/mongo/util/options_parser | |
parent | 50a2462a66402499fa81c9fdb84ee6bab0e5d1a3 (diff) | |
download | mongo-5ee03db5cdc40ecc0a28aaad8031df177188ffee.tar.gz |
SERVER-8510 Handle default, implicit, and composing options in new configuration parser
Diffstat (limited to 'src/mongo/util/options_parser')
-rw-r--r-- | src/mongo/util/options_parser/option_description.h | 14 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_section.cpp | 398 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_section.h | 9 | ||||
-rw-r--r-- | src/mongo/util/options_parser/options_parser.cpp | 130 | ||||
-rw-r--r-- | src/mongo/util/options_parser/options_parser.h | 3 | ||||
-rw-r--r-- | src/mongo/util/options_parser/options_parser_test.cpp | 272 |
6 files changed, 808 insertions, 18 deletions
diff --git a/src/mongo/util/options_parser/option_description.h b/src/mongo/util/options_parser/option_description.h index 08d99122243..78bb88f80c2 100644 --- a/src/mongo/util/options_parser/option_description.h +++ b/src/mongo/util/options_parser/option_description.h @@ -18,6 +18,7 @@ #include <iostream> #include "mongo/base/status.h" +#include "mongo/util/options_parser/value.h" namespace mongo { namespace optionenvironment { @@ -48,18 +49,27 @@ namespace optionenvironment { const std::string& singleName, // Used for boost command line and INI const OptionType type, const std::string& description, - const bool isVisible = true) + const bool isVisible = true, + const Value defaultValue = Value(), + const Value implicitValue = Value(), + const bool isComposing = false) : _dottedName(dottedName), _singleName(singleName), _type(type), _description(description), - _isVisible(isVisible) { } + _isVisible(isVisible), + _default(defaultValue), + _implicit(implicitValue), + _isComposing(isComposing) { } std::string _dottedName; std::string _singleName; OptionType _type; std::string _description; bool _isVisible; + Value _default; + Value _implicit; + bool _isComposing; }; class PositionalOptionDescription { diff --git a/src/mongo/util/options_parser/option_section.cpp b/src/mongo/util/options_parser/option_section.cpp index d2fc250005d..783f4dc5853 100644 --- a/src/mongo/util/options_parser/option_section.cpp +++ b/src/mongo/util/options_parser/option_section.cpp @@ -18,12 +18,75 @@ #include <sstream> #include "mongo/bson/util/builder.h" +#include "mongo/util/options_parser/value.h" namespace mongo { namespace optionenvironment { // Registration interface + namespace { + /** + * Utility function check that the type of our Value matches our OptionType + */ + Status checkValueType(OptionType type, Value value) { + switch (type) { + case StringVector: + { + std::vector<std::string> valueType; + return value.get(&valueType); + } + case Bool: + { + bool valueType; + return value.get(&valueType); + } + case Double: + { + double valueType; + return value.get(&valueType); + } + case Int: + { + int valueType; + return value.get(&valueType); + } + case Long: + { + long valueType; + return value.get(&valueType); + } + case String: + { + std::string valueType; + return value.get(&valueType); + } + case UnsignedLongLong: + { + unsigned long long valueType; + return value.get(&valueType); + } + case Unsigned: + { + unsigned valueType; + return value.get(&valueType); + } + case Switch: + { + bool valueType; + return value.get(&valueType); + } + default: + { + StringBuilder sb; + sb << "Unrecognized option type: " << type; + return Status(ErrorCodes::InternalError, sb.str()); + } + } + } + } // namespace + + // TODO: Make sure the section we are adding does not have duplicate options Status OptionSection::addSection(const OptionSection& subSection) { if (!subSection._positionalOptions.empty()) { return Status(ErrorCodes::InternalError, @@ -51,6 +114,47 @@ namespace optionenvironment { return Status(ErrorCodes::InternalError, sb.str()); } } + + // Make sure the type of our default value matches our declared type + if (!option._default.isEmpty()) { + Status ret = checkValueType(option._type, option._default); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Could not register option \"" << option._dottedName << "\": " + << "mismatch between declared type and type of default value: " + << ret.toString(); + return Status(ErrorCodes::TypeMismatch, sb.str()); + } + } + + // Make sure that if we are registering a composing option it has the type of StringVector + if (option._isComposing) { + if (option._type != StringVector) { + StringBuilder sb; + sb << "Could not register option \"" << option._dottedName << "\": " + << "only options registered as StringVector can be composing"; + return Status(ErrorCodes::TypeMismatch, sb.str()); + } + } + + // Disallow registering a default for a composing option since the interaction between the + // two is unclear (for example, should we override or compose the default) + if (option._isComposing && !option._default.isEmpty()) { + StringBuilder sb; + sb << "Could not register option \"" << option._dottedName << "\": " + << "Cannot register a default value for a composing option"; + return Status(ErrorCodes::InternalError, sb.str()); + } + + // Disallow registering an implicit value for a composing option since the interaction + // between the two is unclear + if (option._isComposing && !option._implicit.isEmpty()) { + StringBuilder sb; + sb << "Could not register option \"" << option._dottedName << "\": " + << "Cannot register an implicit value for a composing option"; + return Status(ErrorCodes::InternalError, sb.str()); + } + _options.push_back(option); return Status::OK(); } @@ -86,30 +190,279 @@ namespace optionenvironment { /** Helper function to convert the values of our OptionType enum into the classes that * boost::program_option uses to pass around this information */ - po::value_semantic* typeToBoostType(OptionType type) { + Status typeToBoostType(std::auto_ptr<po::value_semantic>* boostType, + OptionType type, + const Value defaultValue = Value(), + const Value implicitValue = Value()) { switch (type) { - case StringVector: return po::value< std::vector<std::string> >(); - case Bool: return po::value<bool>(); - case Double: return po::value<double>(); - case Int: return po::value<int>(); - case Long: return po::value<long>(); - case String: return po::value<std::string>(); - case UnsignedLongLong: return po::value<unsigned long long>(); - case Unsigned: return po::value<unsigned>(); - case Switch: return po::bool_switch(); - default: return NULL; /* XXX: should not get here */ + case StringVector: + { + *boostType = std::auto_ptr<po::value_semantic>( + po::value< std::vector<std::string> >()); + + if (!implicitValue.isEmpty()) { + StringBuilder sb; + sb << "Implicit value not supported for string vector"; + return Status(ErrorCodes::InternalError, sb.str()); + } + + if (!defaultValue.isEmpty()) { + StringBuilder sb; + sb << "Default value not supported for string vector"; + return Status(ErrorCodes::InternalError, sb.str()); + } + + return Status::OK(); + } + case Bool: + { + std::auto_ptr<po::typed_value<bool> > boostTypeBuilder(po::value<bool>()); + + if (!implicitValue.isEmpty()) { + bool implicitValueType; + Status ret = implicitValue.get(&implicitValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting implicit value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->implicit_value(implicitValueType); + } + + if (!defaultValue.isEmpty()) { + bool defaultValueType; + Status ret = defaultValue.get(&defaultValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting default value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->default_value(defaultValueType); + } + + *boostType = boostTypeBuilder; + + return Status::OK(); + } + case Double: + { + std::auto_ptr<po::typed_value<double> > + boostTypeBuilder(po::value<double>()); + + if (!implicitValue.isEmpty()) { + double implicitValueType; + Status ret = implicitValue.get(&implicitValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting implicit value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->implicit_value(implicitValueType); + } + + if (!defaultValue.isEmpty()) { + double defaultValueType; + Status ret = defaultValue.get(&defaultValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting default value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->default_value(defaultValueType); + } + + *boostType = boostTypeBuilder; + + return Status::OK(); + } + case Int: + { + std::auto_ptr<po::typed_value<int> > boostTypeBuilder(po::value<int>()); + + if (!implicitValue.isEmpty()) { + int implicitValueType; + Status ret = implicitValue.get(&implicitValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting implicit value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->implicit_value(implicitValueType); + } + + if (!defaultValue.isEmpty()) { + int defaultValueType; + Status ret = defaultValue.get(&defaultValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting default value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->default_value(defaultValueType); + } + + *boostType = boostTypeBuilder; + + return Status::OK(); + } + case Long: + { + std::auto_ptr<po::typed_value<long> > boostTypeBuilder(po::value<long>()); + + if (!implicitValue.isEmpty()) { + long implicitValueType; + Status ret = implicitValue.get(&implicitValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting implicit value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->implicit_value(implicitValueType); + } + + if (!defaultValue.isEmpty()) { + long defaultValueType; + Status ret = defaultValue.get(&defaultValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting default value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->default_value(defaultValueType); + } + + *boostType = boostTypeBuilder; + + return Status::OK(); + } + case String: + { + std::auto_ptr<po::typed_value<std::string> > + boostTypeBuilder(po::value<std::string>()); + + if (!implicitValue.isEmpty()) { + std::string implicitValueType; + Status ret = implicitValue.get(&implicitValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting implicit value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->implicit_value(implicitValueType); + } + + if (!defaultValue.isEmpty()) { + std::string defaultValueType; + Status ret = defaultValue.get(&defaultValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting default value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->default_value(defaultValueType); + } + + *boostType = boostTypeBuilder; + + return Status::OK(); + } + case UnsignedLongLong: + { + std::auto_ptr<po::typed_value<unsigned long long> > + boostTypeBuilder(po::value<unsigned long long>()); + + if (!implicitValue.isEmpty()) { + unsigned long long implicitValueType; + Status ret = implicitValue.get(&implicitValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting implicit value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->implicit_value(implicitValueType); + } + + if (!defaultValue.isEmpty()) { + unsigned long long defaultValueType; + Status ret = defaultValue.get(&defaultValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting default value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->default_value(defaultValueType); + } + + *boostType = boostTypeBuilder; + + return Status::OK(); + } + case Unsigned: + { + std::auto_ptr<po::typed_value<unsigned> > + boostTypeBuilder(po::value<unsigned>()); + + if (!implicitValue.isEmpty()) { + unsigned implicitValueType; + Status ret = implicitValue.get(&implicitValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting implicit value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->implicit_value(implicitValueType); + } + + if (!defaultValue.isEmpty()) { + unsigned defaultValueType; + Status ret = defaultValue.get(&defaultValueType); + if(!ret.isOK()) { + StringBuilder sb; + sb << "Error getting default value: " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + boostTypeBuilder->default_value(defaultValueType); + } + + *boostType = boostTypeBuilder; + + return Status::OK(); + } + case Switch: + { + *boostType = std::auto_ptr<po::value_semantic>(po::bool_switch()); + return Status::OK(); + } + default: + { + StringBuilder sb; + sb << "Unrecognized option type: " << type; + return Status(ErrorCodes::InternalError, sb.str()); + } } } } // namespace Status OptionSection::getBoostOptions(po::options_description* boostOptions, - bool visibleOnly) const { + bool visibleOnly, + bool includeDefaults) const { std::vector<OptionDescription>::const_iterator oditerator; for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { if (!visibleOnly || (oditerator->_isVisible)) { + std::auto_ptr<po::value_semantic> boostType; + Status ret = typeToBoostType(&boostType, + oditerator->_type, + includeDefaults ? oditerator->_default : Value(), + oditerator->_implicit); + if (!ret.isOK()) { + StringBuilder sb; + sb << "Error getting boost type for option \"" + << oditerator->_dottedName << "\": " << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } boostOptions->add_options()(oditerator->_singleName.c_str(), - typeToBoostType(oditerator->_type), + boostType.release(), oditerator->_description.c_str()); } } @@ -119,7 +472,7 @@ namespace optionenvironment { po::options_description subGroup = ositerator->_name.empty() ? po::options_description() : po::options_description(ositerator->_name.c_str()); - ositerator->getBoostOptions(&subGroup, visibleOnly); + ositerator->getBoostOptions(&subGroup, visibleOnly, includeDefaults); boostOptions->add(subGroup); } @@ -156,6 +509,23 @@ namespace optionenvironment { return Status::OK(); } + Status OptionSection::getDefaults(std::map<Key, Value>* values) const { + + std::vector<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + if (!oditerator->_default.isEmpty()) { + (*values)[oditerator->_dottedName] = oditerator->_default; + } + } + + std::vector<OptionSection>::const_iterator ositerator; + for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { + ositerator->getDefaults(values); + } + + return Status::OK(); + } + std::string OptionSection::positionalHelpString(const std::string& execName) const { po::positional_options_description boostPositionalOptions; diff --git a/src/mongo/util/options_parser/option_section.h b/src/mongo/util/options_parser/option_section.h index 86e0a9d2e54..e0699232773 100644 --- a/src/mongo/util/options_parser/option_section.h +++ b/src/mongo/util/options_parser/option_section.h @@ -84,7 +84,8 @@ namespace optionenvironment { // These functions are used by the OptionsParser to make calls into boost::program_options Status getBoostOptions(po::options_description* boostOptions, - bool visibleOnly = false) const; + bool visibleOnly = false, + bool includeDefaults = false) const; Status getBoostPositionalOptions( po::positional_options_description* boostPositionalOptions) const; @@ -93,6 +94,12 @@ namespace optionenvironment { // found has been registered and has the correct type Status getAllOptions(std::vector<OptionDescription>* options) const; + /** + * Populates the given map with all the default values for any options in this option + * section and all sub sections. + */ + Status getDefaults(std::map<Key, Value>* values) const; + std::string positionalHelpString(const std::string& execName) const; std::string helpString() const; diff --git a/src/mongo/util/options_parser/options_parser.cpp b/src/mongo/util/options_parser/options_parser.cpp index 37d7067d9c7..f4de3e2c2ff 100644 --- a/src/mongo/util/options_parser/options_parser.cpp +++ b/src/mongo/util/options_parser/options_parser.cpp @@ -310,6 +310,59 @@ namespace optionenvironment { return Status::OK(); } + /** + * For all options that we registered as composable, combine the values from source and dest + * and set the result in dest. Note that this only works for options that are registered as + * vectors of strings. + */ + Status addCompositions(const OptionSection& options, + const Environment& source, + Environment* dest) { + std::vector<OptionDescription> options_vector; + Status ret = options.getAllOptions(&options_vector); + if (!ret.isOK()) { + return ret; + } + + for(std::vector<OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); iterator++) { + if (iterator->_isComposing) { + std::vector<std::string> source_value; + ret = source.get(iterator->_dottedName, &source_value); + if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { + StringBuilder sb; + sb << "Error getting composable vector value from source: " + << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + // Only do something if our source environment has something to add + else if (ret.isOK()) { + std::vector<std::string> dest_value; + ret = dest->get(iterator->_dottedName, &dest_value); + if (!ret.isOK() && ret != ErrorCodes::NoSuchKey) { + StringBuilder sb; + sb << "Error getting composable vector value from dest: " + << ret.toString(); + return Status(ErrorCodes::InternalError, sb.str()); + } + + // Append source_value on the end of dest_value + dest_value.insert(dest_value.end(), + source_value.begin(), + source_value.end()); + + // Set the resulting value in our output environment + ret = dest->set(Key(iterator->_dottedName), Value(dest_value)); + if (!ret.isOK()) { + return ret; + } + } + } + } + + return Status::OK(); + } + } // namespace /** @@ -451,6 +504,46 @@ namespace optionenvironment { } /** + * Extract default values from the given options and add to environment + */ + Status OptionsParser::getDefaultValues(const OptionSection& options, + Environment* environment) { + Environment defaultEnvironment; + + // This should have been caught at the time we registered our options, but we check that the + // default types match our declared types here just to be sure. + Status ret = addTypeConstraints(options, &defaultEnvironment); + if (!ret.isOK()) { + return ret; + } + + std::map <Key, Value> defaultOptions; + + ret = options.getDefaults(&defaultOptions); + if (!ret.isOK()) { + return ret; + } + + typedef std::map<Key, Value>::iterator it_type; + for(it_type iterator = defaultOptions.begin(); + iterator != defaultOptions.end(); iterator++) { + ret = defaultEnvironment.set(iterator->first, iterator->second); + if (!ret.isOK()) { + return ret; + } + } + + ret = defaultEnvironment.validate(); + if (!ret.isOK()) { + return ret; + } + + *environment = defaultEnvironment; + + return Status::OK(); + } + + /** * Reads the entire config file into the output string. This is done this way because the JSON * parser only takes complete strings */ @@ -550,10 +643,17 @@ namespace optionenvironment { const std::map<std::string, std::string>& env, // XXX: Currently unused Environment* environment) { + Environment defaultEnvironment; Environment commandLineEnvironment; Environment configEnvironment; + Environment composedEnvironment; - Status ret = parseCommandLine(options, argv, &commandLineEnvironment); + Status ret = getDefaultValues(options, &defaultEnvironment); + if (!ret.isOK()) { + return ret; + } + + ret = parseCommandLine(options, argv, &commandLineEnvironment); if (!ret.isOK()) { return ret; } @@ -602,8 +702,30 @@ namespace optionenvironment { } } + // Adds the values for all our options that were registered as composable to the composed + // environment. addCompositions doesn't override the values like "setAll" on our + // environment. Instead it aggregates the values in the result environment. + ret = addCompositions(options, commandLineEnvironment, &composedEnvironment); + if (!ret.isOK()) { + return ret; + } + + ret = addCompositions(options, configEnvironment, &composedEnvironment); + if (!ret.isOK()) { + return ret; + } + + ret = addCompositions(options, defaultEnvironment, &composedEnvironment); + if (!ret.isOK()) { + return ret; + } + // Add the values to our result in the order of override // NOTE: This should not fail validation as we haven't called environment->validate() yet + ret = environment->setAll(defaultEnvironment); + if (!ret.isOK()) { + return ret; + } ret = environment->setAll(configEnvironment); if (!ret.isOK()) { return ret; @@ -613,6 +735,12 @@ namespace optionenvironment { return ret; } + // Add this last because it represents the aggregated results of composing all environments + ret = environment->setAll(composedEnvironment); + if (!ret.isOK()) { + return ret; + } + return Status::OK(); } diff --git a/src/mongo/util/options_parser/options_parser.h b/src/mongo/util/options_parser/options_parser.h index dd401b04f5e..ceef90bb864 100644 --- a/src/mongo/util/options_parser/options_parser.h +++ b/src/mongo/util/options_parser/options_parser.h @@ -103,6 +103,9 @@ namespace optionenvironment { /** Handles parsing of a JSON config string and adds the results to the given Environment */ Status parseJSONConfigFile(const OptionSection&, const std::string& config, Environment*); + /** Gets defaults from the OptionSection and adds the results to the given Environment */ + Status getDefaultValues(const OptionSection&, Environment*); + /** Detects whether the given string represents a JSON config file or an INI config file */ bool isJSONConfig(const std::string& config); diff --git a/src/mongo/util/options_parser/options_parser_test.cpp b/src/mongo/util/options_parser/options_parser_test.cpp index d539279aca2..21f675189ea 100644 --- a/src/mongo/util/options_parser/options_parser_test.cpp +++ b/src/mongo/util/options_parser/options_parser_test.cpp @@ -73,6 +73,29 @@ namespace { moe::Int))); } + TEST(Registration, DefaultValueWrongType) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("help", "help", moe::Switch, "Display help")); + ASSERT_NOT_OK(testOpts.addOption(moe::OptionDescription("port", "port", moe::Int, "Port", + true, moe::Value("String")))); + } + + TEST(Registration, ComposableNotVector) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + ASSERT_NOT_OK(testOpts.addOption(moe::OptionDescription("setParameter", "setParameter", + moe::String, + "Multiple Values", true/*visible*/, + moe::Value()/*no default*/, + moe::Value()/*no implicit value*/, + true/*composing*/))); + } + TEST(Parsing, Good) { moe::OptionsParser parser; moe::Environment environment; @@ -372,6 +395,119 @@ namespace { ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); } + TEST(Parsing, DefaultValue) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("help", "help", moe::Switch, "Display help")); + testOpts.addOption(moe::OptionDescription("port", "port", moe::Int, "Port", true, + moe::Value(5))); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + std::map<std::string, std::string> env_map; + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); + } + + TEST(Parsing, DefaultValueOverride) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("help", "help", moe::Switch, "Display help")); + testOpts.addOption(moe::OptionDescription("port", "port", moe::Int, "Port", true, + moe::Value(5))); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("6"); + std::map<std::string, std::string> env_map; + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 6); + } + + TEST(Parsing, ImplicitValue) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("help", "help", moe::Switch, "Display help")); + testOpts.addOption(moe::OptionDescription("port", "port", moe::Int, "Port", true, + moe::Value(6), + moe::Value(7))); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--port"); + std::map<std::string, std::string> env_map; + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 7); + } + + TEST(Parsing, ImplicitValueDefault) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("help", "help", moe::Switch, "Display help")); + testOpts.addOption(moe::OptionDescription("port", "port", moe::Int, "Port", true, + moe::Value(6), + moe::Value(7))); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + std::map<std::string, std::string> env_map; + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 6); + } + + TEST(Parsing, ImplicitValueOverride) { + moe::OptionsParser parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("help", "help", moe::Switch, "Display help")); + testOpts.addOption(moe::OptionDescription("port", "port", moe::Int, "Port", true, + moe::Value(6), + moe::Value(7))); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("5"); + std::map<std::string, std::string> env_map; + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 5); + } + TEST(Style, NoSticky) { moe::OptionsParser parser; moe::Environment environment; @@ -561,6 +697,32 @@ namespace { ASSERT_EQUALS(str, "NotCommented"); } + TEST(INIConfigFile, DefaultValueOverride) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("config", "config", + moe::String, "Config file to parse")); + testOpts.addOption(moe::OptionDescription("port", "port", moe::Int, "Port", true, + moe::Value(5))); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("default.conf"); + std::map<std::string, std::string> env_map; + + parser.setConfig("default.conf", "port=6"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 6); + } + TEST(JSONConfigFile, Basic) { OptionsParserTester parser; moe::Environment environment; @@ -842,6 +1004,32 @@ namespace { ASSERT_NOT_OK(parser.run(testOpts, argv, env_map, &environment)); } + TEST(JSONConfigFile, DefaultValueOverride) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("config", "config", + moe::String, "Config file to parse")); + testOpts.addOption(moe::OptionDescription("port", "port", moe::Int, "Port", true, + moe::Value(5))); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.json"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.json", "{ port : 6 }"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + moe::Value value; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + int port; + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 6); + } + TEST(Parsing, BadConfigFileOption) { OptionsParserTester parser; moe::Environment environment; @@ -927,4 +1115,88 @@ namespace { ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); } + TEST(JSONConfigFile, Composing) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("config", "config", + moe::String, "Config file to parse")); + testOpts.addOption(moe::OptionDescription("setParameter", "setParameter", moe::StringVector, + "Multiple Values", true/*visible*/, + moe::Value()/*no default*/, + moe::Value()/*no implicit value*/, + true/*composing*/)); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("config.json"); + argv.push_back("--setParameter"); + argv.push_back("val1"); + argv.push_back("--setParameter"); + argv.push_back("val2"); + std::map<std::string, std::string> env_map; + + parser.setConfig("config.json", "{ setParameter : [ \"val3\", \"val4\" ] }"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("setParameter"), &value)); + std::vector<std::string> setParameter; + std::vector<std::string>::iterator setParameterit; + ASSERT_OK(value.get(&setParameter)); + ASSERT_EQUALS(setParameter.size(), static_cast<size_t>(4)); + setParameterit = setParameter.begin(); + ASSERT_EQUALS(*setParameterit, "val1"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val2"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val3"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val4"); + } + + TEST(INIConfigFile, Composing) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOption(moe::OptionDescription("config", "config", + moe::String, "Config file to parse")); + testOpts.addOption(moe::OptionDescription("setParameter", "setParameter", moe::StringVector, + "Multiple Values", true/*visible*/, + moe::Value()/*no default*/, + moe::Value()/*no implicit value*/, + true/*composing*/)); + + std::vector<std::string> argv; + argv.push_back("binaryname"); + argv.push_back("--config"); + argv.push_back("default.conf"); + argv.push_back("--setParameter"); + argv.push_back("val1"); + argv.push_back("--setParameter"); + argv.push_back("val2"); + std::map<std::string, std::string> env_map; + + parser.setConfig("default.conf", "setParameter=val3\nsetParameter=val4"); + + moe::Value value; + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.get(moe::Key("setParameter"), &value)); + std::vector<std::string> setParameter; + std::vector<std::string>::iterator setParameterit; + ASSERT_OK(value.get(&setParameter)); + ASSERT_EQUALS(setParameter.size(), static_cast<size_t>(4)); + setParameterit = setParameter.begin(); + ASSERT_EQUALS(*setParameterit, "val1"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val2"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val3"); + setParameterit++; + ASSERT_EQUALS(*setParameterit, "val4"); + } + } // unnamed namespace |